commit
67c032c85a
@ -7,7 +7,7 @@ import { demoConfig } from "../data/demo_config";
|
||||
import { demoServices } from "../data/demo_services";
|
||||
import demoResources from "../data/demo_resources";
|
||||
import demoStates from "../data/demo_states";
|
||||
import createCardElement from "../../../src/panels/lovelace/common/create-card-element";
|
||||
import { createCardElement } from "../../../src/panels/lovelace/common/create-card-element";
|
||||
|
||||
class DemoCard extends PolymerElement {
|
||||
static get template() {
|
||||
@ -78,6 +78,10 @@ class DemoCard extends PolymerElement {
|
||||
hass.resources = demoResources;
|
||||
hass.language = "en";
|
||||
hass.states = demoStates;
|
||||
hass.themes = {
|
||||
default_theme: "default",
|
||||
themes: {},
|
||||
};
|
||||
el.hass = hass;
|
||||
}
|
||||
|
||||
|
||||
@ -29,6 +29,31 @@ const ENTITIES = [
|
||||
friendly_name: "Home",
|
||||
icon: "mdi:home",
|
||||
}),
|
||||
getEntity("zone", "bushfire", "zoning", {
|
||||
latitude: -33.8611,
|
||||
longitude: 151.203,
|
||||
radius: 35000,
|
||||
friendly_name: "Bushfire Zone",
|
||||
icon: "mdi:home",
|
||||
}),
|
||||
getEntity("geo_location", "nelsons_creek", "15", {
|
||||
source: "bushfire_demo",
|
||||
latitude: -34.07792,
|
||||
longitude: 151.03219,
|
||||
friendly_name: "Nelsons Creek",
|
||||
}),
|
||||
getEntity("geo_location", "forest_rd_nowra_hill", "8", {
|
||||
source: "bushfire_demo",
|
||||
latitude: -33.69452,
|
||||
longitude: 151.19577,
|
||||
friendly_name: "Forest Rd, Nowra Hill",
|
||||
}),
|
||||
getEntity("geo_location", "stoney_ridge_rd_kremnos", "20", {
|
||||
source: "bushfire_demo",
|
||||
latitude: -33.66584,
|
||||
longitude: 150.97209,
|
||||
friendly_name: "Stoney Ridge Rd, Kremnos",
|
||||
}),
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
@ -116,6 +141,24 @@ const CONFIGS = [
|
||||
- light.bed_light
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Geo Location Entities",
|
||||
config: `
|
||||
- type: map
|
||||
geo_location_sources:
|
||||
- bushfire_demo
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Geo Location Entities with Home Zone",
|
||||
config: `
|
||||
- type: map
|
||||
geo_location_sources:
|
||||
- bushfire_demo
|
||||
entities:
|
||||
- zone.bushfire
|
||||
`,
|
||||
},
|
||||
];
|
||||
|
||||
class DemoMap extends PolymerElement {
|
||||
|
||||
@ -9,6 +9,9 @@ const ENTITIES = [
|
||||
getEntity("light", "kitchen_lights", "on", {
|
||||
friendly_name: "Kitchen Lights",
|
||||
}),
|
||||
getEntity("light", "bed_light", "on", {
|
||||
friendly_name: "Bed Lights",
|
||||
}),
|
||||
getEntity("device_tracker", "demo_paulus", "work", {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 27 KiB |
2
setup.py
2
setup.py
@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name="home-assistant-frontend",
|
||||
version="20181211.2",
|
||||
version="2019109.0",
|
||||
description="The Home Assistant frontend",
|
||||
url="https://github.com/home-assistant/home-assistant-polymer",
|
||||
author="The Home Assistant Authors",
|
||||
|
||||
@ -8,6 +8,7 @@ import "../components/ha-icon";
|
||||
|
||||
import EventsMixin from "../mixins/events-mixin";
|
||||
import LocalizeMixin from "../mixins/localize-mixin";
|
||||
import { computeRTL } from "../common/util/compute_rtl";
|
||||
|
||||
/*
|
||||
* @appliesMixin LocalizeMixin
|
||||
@ -30,12 +31,16 @@ class HaWeatherCard extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||
|
||||
.header {
|
||||
font-family: var(--paper-font-headline_-_font-family);
|
||||
-webkit-font-smoothing: var(--paper-font-headline_-_-webkit-font-smoothing);
|
||||
-webkit-font-smoothing: var(
|
||||
--paper-font-headline_-_-webkit-font-smoothing
|
||||
);
|
||||
font-size: var(--paper-font-headline_-_font-size);
|
||||
font-weight: var(--paper-font-headline_-_font-weight);
|
||||
letter-spacing: var(--paper-font-headline_-_letter-spacing);
|
||||
line-height: var(--paper-font-headline_-_line-height);
|
||||
text-rendering: var(--paper-font-common-expensive-kerning_-_text-rendering);
|
||||
text-rendering: var(
|
||||
--paper-font-common-expensive-kerning_-_text-rendering
|
||||
);
|
||||
opacity: var(--dark-primary-opacity);
|
||||
padding: 24px 16px 16px;
|
||||
display: flex;
|
||||
@ -48,6 +53,11 @@ class HaWeatherCard extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
:host([rtl]) .name {
|
||||
margin-left: 0px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.now {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@ -61,18 +71,31 @@ class HaWeatherCard extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||
margin-right: 32px;
|
||||
}
|
||||
|
||||
:host([rtl]) .main {
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
.main ha-icon {
|
||||
--iron-icon-height: 72px;
|
||||
--iron-icon-width: 72px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
:host([rtl]) .main ha-icon {
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
.main .temp {
|
||||
font-size: 52px;
|
||||
line-height: 1em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
:host([rtl]) .main .temp {
|
||||
direction: ltr;
|
||||
margin-right: 28px;
|
||||
}
|
||||
|
||||
.main .temp span {
|
||||
font-size: 24px;
|
||||
line-height: 1em;
|
||||
@ -80,6 +103,14 @@ class HaWeatherCard extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
.measurand {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
:host([rtl]) .measurand {
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
.forecast {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
@ -96,13 +127,17 @@ class HaWeatherCard extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
:host([rtl]) .forecast .temp {
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
.weekday {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.attributes,
|
||||
.templow,
|
||||
.precipitation { {
|
||||
.precipitation {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
</style>
|
||||
@ -130,7 +165,9 @@ class HaWeatherCard extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||
>
|
||||
<div>
|
||||
[[localize('ui.card.weather.attributes.air_pressure')]]:
|
||||
[[stateObj.attributes.pressure]] [[getUnit('air_pressure')]]
|
||||
<span class="measurand">
|
||||
[[stateObj.attributes.pressure]] [[getUnit('air_pressure')]]
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template
|
||||
@ -139,7 +176,9 @@ class HaWeatherCard extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||
>
|
||||
<div>
|
||||
[[localize('ui.card.weather.attributes.humidity')]]:
|
||||
[[stateObj.attributes.humidity]] %
|
||||
<span class="measurand"
|
||||
>[[stateObj.attributes.humidity]] %</span
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
<template
|
||||
@ -148,8 +187,10 @@ class HaWeatherCard extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||
>
|
||||
<div>
|
||||
[[localize('ui.card.weather.attributes.wind_speed')]]:
|
||||
[[getWind(stateObj.attributes.wind_speed,
|
||||
stateObj.attributes.wind_bearing, localize)]]
|
||||
<span class="measurand">
|
||||
[[getWindSpeed(stateObj.attributes.wind_speed)]]
|
||||
</span>
|
||||
[[getWindBearing(stateObj.attributes.wind_bearing, localize)]]
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
@ -202,6 +243,11 @@ class HaWeatherCard extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||
type: Array,
|
||||
computed: "computeForecast(stateObj.attributes.forecast)",
|
||||
},
|
||||
rtl: {
|
||||
type: Boolean,
|
||||
reflectToAttribute: true,
|
||||
computed: "_computeRTL(hass)",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -295,14 +341,18 @@ class HaWeatherCard extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||
return degree;
|
||||
}
|
||||
|
||||
getWind(speed, bearing, localize) {
|
||||
getWindSpeed(speed) {
|
||||
return `${speed} ${this.getUnit("length")}/h`;
|
||||
}
|
||||
|
||||
getWindBearing(bearing, localize) {
|
||||
if (bearing != null) {
|
||||
const cardinalDirection = this.windBearingToText(bearing);
|
||||
return `${speed} ${this.getUnit("length")}/h (${localize(
|
||||
return `(${localize(
|
||||
`ui.card.weather.cardinal_direction.${cardinalDirection.toLowerCase()}`
|
||||
) || cardinalDirection})`;
|
||||
}
|
||||
return `${speed} ${this.getUnit("length")}/h`;
|
||||
return ``;
|
||||
}
|
||||
|
||||
_showValue(item) {
|
||||
@ -324,5 +374,9 @@ class HaWeatherCard extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||
{ hour: "numeric" }
|
||||
);
|
||||
}
|
||||
|
||||
_computeRTL(hass) {
|
||||
return computeRTL(hass);
|
||||
}
|
||||
}
|
||||
customElements.define("ha-weather-card", HaWeatherCard);
|
||||
|
||||
@ -18,8 +18,8 @@ interface RefreshTokenResponse {
|
||||
declare global {
|
||||
interface Window {
|
||||
externalApp?: {
|
||||
getExternalAuth(payload: BasePayload);
|
||||
revokeExternalAuth(payload: BasePayload);
|
||||
getExternalAuth(payload: string);
|
||||
revokeExternalAuth(payload: string);
|
||||
};
|
||||
webkit?: {
|
||||
messageHandlers: {
|
||||
@ -67,7 +67,7 @@ export default class ExternalAuth extends Auth {
|
||||
const callbackPayload = { callback: CALLBACK_SET_TOKEN };
|
||||
|
||||
if (window.externalApp) {
|
||||
window.externalApp.getExternalAuth(callbackPayload);
|
||||
window.externalApp.getExternalAuth(JSON.stringify(callbackPayload));
|
||||
} else {
|
||||
window.webkit!.messageHandlers.getExternalAuth.postMessage(
|
||||
callbackPayload
|
||||
@ -92,7 +92,7 @@ export default class ExternalAuth extends Auth {
|
||||
const callbackPayload = { callback: CALLBACK_REVOKE_TOKEN };
|
||||
|
||||
if (window.externalApp) {
|
||||
window.externalApp.revokeExternalAuth(callbackPayload);
|
||||
window.externalApp.revokeExternalAuth(JSON.stringify(callbackPayload));
|
||||
} else {
|
||||
window.webkit!.messageHandlers.revokeExternalAuth.postMessage(
|
||||
callbackPayload
|
||||
|
||||
@ -2,5 +2,6 @@ import { HassEntity } from "home-assistant-js-websocket";
|
||||
import computeObjectId from "./compute_object_id";
|
||||
|
||||
export default (stateObj: HassEntity): string =>
|
||||
stateObj.attributes.friendly_name ||
|
||||
computeObjectId(stateObj.entity_id).replace(/_/g, " ");
|
||||
stateObj.attributes.friendly_name === undefined
|
||||
? computeObjectId(stateObj.entity_id).replace(/_/g, " ")
|
||||
: stateObj.attributes.friendly_name || "";
|
||||
|
||||
3
src/common/util/render-status.ts
Normal file
3
src/common/util/render-status.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export const afterNextRender = (cb: () => void): void => {
|
||||
requestAnimationFrame(() => setTimeout(cb, 0));
|
||||
};
|
||||
@ -1,25 +1,208 @@
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
|
||||
import "../ha-label-badge";
|
||||
import {
|
||||
LitElement,
|
||||
html,
|
||||
PropertyValues,
|
||||
PropertyDeclarations,
|
||||
} from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { classMap } from "lit-html/directives/classMap";
|
||||
|
||||
import computeStateDomain from "../../common/entity/compute_state_domain";
|
||||
import computeStateName from "../../common/entity/compute_state_name";
|
||||
import domainIcon from "../../common/entity/domain_icon";
|
||||
import stateIcon from "../../common/entity/state_icon";
|
||||
import timerTimeRemaining from "../../common/entity/timer_time_remaining";
|
||||
import attributeClassNames from "../../common/entity/attribute_class_names";
|
||||
import secondsToDuration from "../../common/datetime/seconds_to_duration";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { hassLocalizeLitMixin } from "../../mixins/lit-localize-mixin";
|
||||
|
||||
import EventsMixin from "../../mixins/events-mixin";
|
||||
import LocalizeMixin from "../../mixins/localize-mixin";
|
||||
import "../ha-label-badge";
|
||||
|
||||
/*
|
||||
* @appliesMixin LocalizeMixin
|
||||
* @appliesMixin EventsMixin
|
||||
*/
|
||||
class HaStateLabelBadge extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||
static get template() {
|
||||
export class HaStateLabelBadge extends hassLocalizeLitMixin(LitElement) {
|
||||
public state?: HassEntity;
|
||||
private _connected?: boolean;
|
||||
private _updateRemaining?: number;
|
||||
private _timerTimeRemaining?: number;
|
||||
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
this._connected = true;
|
||||
this.startInterval(this.state);
|
||||
}
|
||||
|
||||
public disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
this._connected = false;
|
||||
this.clearInterval();
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const state = this.state;
|
||||
|
||||
if (!state) {
|
||||
return html`
|
||||
${this.renderStyle()}
|
||||
<ha-label-badge label="not found"></ha-label-badge>
|
||||
`;
|
||||
}
|
||||
|
||||
const domain = computeStateDomain(state);
|
||||
|
||||
return html`
|
||||
${this.renderStyle()}
|
||||
<ha-label-badge
|
||||
class="${
|
||||
classMap({
|
||||
[domain]: true,
|
||||
"has-unit_of_measurement":
|
||||
"unit_of_measurement" in state.attributes,
|
||||
})
|
||||
}"
|
||||
.value="${this._computeValue(domain, state)}"
|
||||
.icon="${this._computeIcon(domain, state)}"
|
||||
.image="${state.attributes.entity_picture}"
|
||||
.label="${this._computeLabel(domain, state, this._timerTimeRemaining)}"
|
||||
.description="${computeStateName(state)}"
|
||||
></ha-label-badge>
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return {
|
||||
hass: {},
|
||||
state: {},
|
||||
_timerTimeRemaining: {},
|
||||
};
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProperties: PropertyValues): void {
|
||||
super.firstUpdated(changedProperties);
|
||||
this.addEventListener("click", (ev) => {
|
||||
ev.stopPropagation();
|
||||
if (this.state) {
|
||||
fireEvent(this, "hass-more-info", { entityId: this.state.entity_id });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
super.updated(changedProperties);
|
||||
|
||||
if (this._connected && changedProperties.has("state")) {
|
||||
this.startInterval(this.state);
|
||||
}
|
||||
}
|
||||
|
||||
private _computeValue(domain: string, state: HassEntity) {
|
||||
switch (domain) {
|
||||
case "binary_sensor":
|
||||
case "device_tracker":
|
||||
case "updater":
|
||||
case "sun":
|
||||
case "alarm_control_panel":
|
||||
case "timer":
|
||||
return null;
|
||||
case "sensor":
|
||||
default:
|
||||
return state.state === "unknown"
|
||||
? "-"
|
||||
: this.localize(`component.${domain}.state.${state.state}`) ||
|
||||
state.state;
|
||||
}
|
||||
}
|
||||
|
||||
private _computeIcon(domain: string, state: HassEntity) {
|
||||
if (state.state === "unavailable") {
|
||||
return null;
|
||||
}
|
||||
switch (domain) {
|
||||
case "alarm_control_panel":
|
||||
if (state.state === "pending") {
|
||||
return "hass:clock-fast";
|
||||
}
|
||||
if (state.state === "armed_away") {
|
||||
return "hass:nature";
|
||||
}
|
||||
if (state.state === "armed_home") {
|
||||
return "hass:home-variant";
|
||||
}
|
||||
if (state.state === "armed_night") {
|
||||
return "hass:weather-night";
|
||||
}
|
||||
if (state.state === "armed_custom_bypass") {
|
||||
return "hass:security-home";
|
||||
}
|
||||
if (state.state === "triggered") {
|
||||
return "hass:alert-circle";
|
||||
}
|
||||
// state == 'disarmed'
|
||||
return domainIcon(domain, state.state);
|
||||
case "binary_sensor":
|
||||
case "device_tracker":
|
||||
case "updater":
|
||||
return stateIcon(state);
|
||||
case "sun":
|
||||
return state.state === "above_horizon"
|
||||
? domainIcon(domain)
|
||||
: "hass:brightness-3";
|
||||
case "timer":
|
||||
return state.state === "active" ? "hass:timer" : "hass:timer-off";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private _computeLabel(domain, state, _timerTimeRemaining) {
|
||||
if (
|
||||
state.state === "unavailable" ||
|
||||
["device_tracker", "alarm_control_panel"].includes(domain)
|
||||
) {
|
||||
// Localize the state with a special state_badge namespace, which has variations of
|
||||
// the state translations that are truncated to fit within the badge label. Translations
|
||||
// are only added for device_tracker and alarm_control_panel.
|
||||
return (
|
||||
this.localize(`state_badge.${domain}.${state.state}`) ||
|
||||
this.localize(`state_badge.default.${state.state}`) ||
|
||||
state.state
|
||||
);
|
||||
}
|
||||
if (domain === "timer") {
|
||||
return secondsToDuration(_timerTimeRemaining);
|
||||
}
|
||||
return state.attributes.unit_of_measurement || null;
|
||||
}
|
||||
|
||||
private clearInterval() {
|
||||
if (this._updateRemaining) {
|
||||
clearInterval(this._updateRemaining);
|
||||
this._updateRemaining = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private startInterval(stateObj) {
|
||||
this.clearInterval();
|
||||
if (stateObj && computeStateDomain(stateObj) === "timer") {
|
||||
this.calculateTimerRemaining(stateObj);
|
||||
|
||||
if (stateObj.state === "active") {
|
||||
this._updateRemaining = window.setInterval(
|
||||
() => this.calculateTimerRemaining(this.state),
|
||||
1000
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private calculateTimerRemaining(stateObj) {
|
||||
this._timerTimeRemaining = timerTimeRemaining(stateObj);
|
||||
}
|
||||
|
||||
private renderStyle(): TemplateResult {
|
||||
return html`
|
||||
<style>
|
||||
:host {
|
||||
@ -61,175 +244,13 @@ class HaStateLabelBadge extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||
);
|
||||
}
|
||||
</style>
|
||||
|
||||
<ha-label-badge
|
||||
class$="[[computeClassNames(state)]]"
|
||||
value="[[computeValue(localize, state)]]"
|
||||
icon="[[computeIcon(state)]]"
|
||||
image="[[computeImage(state)]]"
|
||||
label="[[computeLabel(localize, state, _timerTimeRemaining)]]"
|
||||
description="[[computeDescription(state)]]"
|
||||
></ha-label-badge>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
hass: Object,
|
||||
state: {
|
||||
type: Object,
|
||||
observer: "stateChanged",
|
||||
},
|
||||
_timerTimeRemaining: {
|
||||
type: Number,
|
||||
value: 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.startInterval(this.state);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.clearInterval();
|
||||
}
|
||||
|
||||
ready() {
|
||||
super.ready();
|
||||
this.addEventListener("click", (ev) => this.badgeTap(ev));
|
||||
}
|
||||
|
||||
badgeTap(ev) {
|
||||
ev.stopPropagation();
|
||||
this.fire("hass-more-info", { entityId: this.state.entity_id });
|
||||
}
|
||||
|
||||
computeClassNames(state) {
|
||||
const classes = [computeStateDomain(state)];
|
||||
classes.push(attributeClassNames(state, ["unit_of_measurement"]));
|
||||
return classes.join(" ");
|
||||
}
|
||||
|
||||
computeValue(localize, state) {
|
||||
const domain = computeStateDomain(state);
|
||||
switch (domain) {
|
||||
case "binary_sensor":
|
||||
case "device_tracker":
|
||||
case "updater":
|
||||
case "sun":
|
||||
case "alarm_control_panel":
|
||||
case "timer":
|
||||
return null;
|
||||
case "sensor":
|
||||
default:
|
||||
return state.state === "unknown"
|
||||
? "-"
|
||||
: localize(`component.${domain}.state.${state.state}`) || state.state;
|
||||
}
|
||||
}
|
||||
|
||||
computeIcon(state) {
|
||||
if (state.state === "unavailable") {
|
||||
return null;
|
||||
}
|
||||
const domain = computeStateDomain(state);
|
||||
switch (domain) {
|
||||
case "alarm_control_panel":
|
||||
if (state.state === "pending") {
|
||||
return "hass:clock-fast";
|
||||
}
|
||||
if (state.state === "armed_away") {
|
||||
return "hass:nature";
|
||||
}
|
||||
if (state.state === "armed_home") {
|
||||
return "hass:home-variant";
|
||||
}
|
||||
if (state.state === "armed_night") {
|
||||
return "hass:weather-night";
|
||||
}
|
||||
if (state.state === "armed_custom_bypass") {
|
||||
return "hass:security-home";
|
||||
}
|
||||
if (state.state === "triggered") {
|
||||
return "hass:alert-circle";
|
||||
}
|
||||
// state == 'disarmed'
|
||||
return domainIcon(domain, state.state);
|
||||
case "binary_sensor":
|
||||
case "device_tracker":
|
||||
case "updater":
|
||||
return stateIcon(state);
|
||||
case "sun":
|
||||
return state.state === "above_horizon"
|
||||
? domainIcon(domain)
|
||||
: "hass:brightness-3";
|
||||
case "timer":
|
||||
return state.state === "active" ? "hass:timer" : "hass:timer-off";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
computeImage(state) {
|
||||
return state.attributes.entity_picture || null;
|
||||
}
|
||||
|
||||
computeLabel(localize, state, _timerTimeRemaining) {
|
||||
const domain = computeStateDomain(state);
|
||||
if (
|
||||
state.state === "unavailable" ||
|
||||
["device_tracker", "alarm_control_panel"].includes(domain)
|
||||
) {
|
||||
// Localize the state with a special state_badge namespace, which has variations of
|
||||
// the state translations that are truncated to fit within the badge label. Translations
|
||||
// are only added for device_tracker and alarm_control_panel.
|
||||
return (
|
||||
localize(`state_badge.${domain}.${state.state}`) ||
|
||||
localize(`state_badge.default.${state.state}`) ||
|
||||
state.state
|
||||
);
|
||||
}
|
||||
if (domain === "timer") {
|
||||
return secondsToDuration(_timerTimeRemaining);
|
||||
}
|
||||
return state.attributes.unit_of_measurement || null;
|
||||
}
|
||||
|
||||
computeDescription(state) {
|
||||
return computeStateName(state);
|
||||
}
|
||||
|
||||
stateChanged(stateObj) {
|
||||
this.updateStyles();
|
||||
this.startInterval(stateObj);
|
||||
}
|
||||
|
||||
clearInterval() {
|
||||
if (this._updateRemaining) {
|
||||
clearInterval(this._updateRemaining);
|
||||
this._updateRemaining = null;
|
||||
}
|
||||
}
|
||||
|
||||
startInterval(stateObj) {
|
||||
this.clearInterval();
|
||||
if (computeStateDomain(stateObj) === "timer") {
|
||||
this.calculateTimerRemaining(stateObj);
|
||||
|
||||
if (stateObj.state === "active") {
|
||||
this._updateRemaining = setInterval(
|
||||
() => this.calculateTimerRemaining(this.state),
|
||||
1000
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
calculateTimerRemaining(stateObj) {
|
||||
this._timerTimeRemaining = timerTimeRemaining(stateObj);
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-state-label-badge": HaStateLabelBadge;
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,8 +81,15 @@ class HaClimateControl extends EventsMixin(PolymerElement) {
|
||||
this.$.target_temperature.classList.toggle("in-flux", inFlux);
|
||||
}
|
||||
|
||||
_round(val) {
|
||||
// round value to precision derived from step
|
||||
// insired by https://github.com/soundar24/roundSlider/blob/master/src/roundslider.js
|
||||
const s = this.step.toString().split(".");
|
||||
return s[1] ? parseFloat(val.toFixed(s[1].length)) : Math.round(val);
|
||||
}
|
||||
|
||||
incrementValue() {
|
||||
const newval = this.value + this.step;
|
||||
const newval = this._round(this.value + this.step);
|
||||
if (this.value < this.max) {
|
||||
this.last_changed = Date.now();
|
||||
this.temperatureStateInFlux(true);
|
||||
@ -102,7 +109,7 @@ class HaClimateControl extends EventsMixin(PolymerElement) {
|
||||
}
|
||||
|
||||
decrementValue() {
|
||||
const newval = this.value - this.step;
|
||||
const newval = this._round(this.value - this.step);
|
||||
if (this.value > this.min) {
|
||||
this.last_changed = Date.now();
|
||||
this.temperatureStateInFlux(true);
|
||||
|
||||
@ -1,9 +1,83 @@
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import {
|
||||
LitElement,
|
||||
PropertyDeclarations,
|
||||
PropertyValues,
|
||||
} from "@polymer/lit-element";
|
||||
import { TemplateResult, html } from "lit-html";
|
||||
import { classMap } from "lit-html/directives/classMap";
|
||||
import "./ha-icon";
|
||||
|
||||
class HaLabelBadge extends PolymerElement {
|
||||
static get template() {
|
||||
class HaLabelBadge extends LitElement {
|
||||
public value?: string;
|
||||
public icon?: string;
|
||||
public label?: string;
|
||||
public description?: string;
|
||||
public image?: string;
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return {
|
||||
value: {},
|
||||
icon: {},
|
||||
label: {},
|
||||
description: {},
|
||||
image: {},
|
||||
};
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
${this.renderStyle()}
|
||||
<div class="badge-container">
|
||||
<div class="label-badge" id="badge">
|
||||
<div
|
||||
class="${
|
||||
classMap({
|
||||
value: true,
|
||||
big: Boolean(this.value && this.value.length > 4),
|
||||
})
|
||||
}"
|
||||
>
|
||||
${
|
||||
this.icon && !this.value && !this.image
|
||||
? html`
|
||||
<ha-icon .icon="${this.icon}"></ha-icon>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
${
|
||||
this.value && !this.image
|
||||
? html`
|
||||
<span>${this.value}</span>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
</div>
|
||||
${
|
||||
this.label
|
||||
? html`
|
||||
<div
|
||||
class="${
|
||||
classMap({ label: true, big: this.label.length > 5 })
|
||||
}"
|
||||
>
|
||||
<span>${this.label}</span>
|
||||
</div>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
</div>
|
||||
${
|
||||
this.description
|
||||
? html`
|
||||
<div class="title">${this.description}</div>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
protected renderStyle(): TemplateResult {
|
||||
return html`
|
||||
<style>
|
||||
.badge-container {
|
||||
@ -74,69 +148,25 @@ class HaLabelBadge extends PolymerElement {
|
||||
text-overflow: ellipsis;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="badge-container">
|
||||
<div class="label-badge" id="badge">
|
||||
<div class$="[[computeValueClasses(value)]]">
|
||||
<ha-icon
|
||||
icon="[[icon]]"
|
||||
hidden$="[[computeHideIcon(icon, value, image)]]"
|
||||
></ha-icon>
|
||||
<span hidden$="[[computeHideValue(value, image)]]">[[value]]</span>
|
||||
</div>
|
||||
<div
|
||||
hidden$="[[computeHideLabel(label)]]"
|
||||
class$="[[computeLabelClasses(label)]]"
|
||||
>
|
||||
<span>[[label]]</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="title" hidden$="[[!description]]">[[description]]</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
value: String,
|
||||
icon: String,
|
||||
label: String,
|
||||
description: String,
|
||||
|
||||
image: {
|
||||
type: String,
|
||||
observer: "imageChanged",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
computeValueClasses(value) {
|
||||
return value && value.length > 4 ? "value big" : "value";
|
||||
}
|
||||
|
||||
computeLabelClasses(label) {
|
||||
return label && label.length > 5 ? "label big" : "label";
|
||||
}
|
||||
|
||||
computeHideLabel(label) {
|
||||
return !label || !label.trim();
|
||||
}
|
||||
|
||||
computeHideIcon(icon, value, image) {
|
||||
return !icon || value || image;
|
||||
}
|
||||
|
||||
computeHideValue(value, image) {
|
||||
return !value || image;
|
||||
}
|
||||
|
||||
imageChanged(newVal) {
|
||||
this.$.badge.style.backgroundImage = newVal ? "url(" + newVal + ")" : "";
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
super.updated(changedProperties);
|
||||
if (changedProperties.has("image")) {
|
||||
this.shadowRoot!.getElementById("badge")!.style.backgroundImage = this
|
||||
.image
|
||||
? `url(${this.image})`
|
||||
: "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-label-badge": HaLabelBadge;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-label-badge", HaLabelBadge);
|
||||
18
src/data/alarm_control_panel.ts
Normal file
18
src/data/alarm_control_panel.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { HomeAssistant } from "../types";
|
||||
|
||||
export const callAlarmAction = (
|
||||
hass: HomeAssistant,
|
||||
entity: string,
|
||||
action:
|
||||
| "arm_away"
|
||||
| "arm_home"
|
||||
| "arm_night"
|
||||
| "arm_custom_bypass"
|
||||
| "disarm",
|
||||
code: string
|
||||
) => {
|
||||
hass!.callService("alarm_control_panel", "alarm_" + action, {
|
||||
entity_id: entity,
|
||||
code,
|
||||
});
|
||||
};
|
||||
@ -3,6 +3,9 @@ import { HomeAssistant } from "../types";
|
||||
export interface LovelaceConfig {
|
||||
title?: string;
|
||||
views: LovelaceViewConfig[];
|
||||
background?: string;
|
||||
resources?: Array<{ type: "css" | "js" | "module" | "html"; url: string }>;
|
||||
excluded_entities?: string[];
|
||||
}
|
||||
|
||||
export interface LovelaceViewConfig {
|
||||
@ -13,6 +16,8 @@ export interface LovelaceViewConfig {
|
||||
path?: string;
|
||||
icon?: string;
|
||||
theme?: string;
|
||||
panel?: boolean;
|
||||
background?: string;
|
||||
}
|
||||
|
||||
export interface LovelaceCardConfig {
|
||||
|
||||
@ -1,15 +1,18 @@
|
||||
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import LocalizeMixin from "../../../mixins/localize-mixin";
|
||||
|
||||
class MoreInfoScript extends PolymerElement {
|
||||
class MoreInfoScript extends LocalizeMixin(PolymerElement) {
|
||||
static get template() {
|
||||
return html`
|
||||
<style include="iron-flex iron-flex-alignment"></style>
|
||||
|
||||
<div class="layout vertical">
|
||||
<div class="data-entry layout justified horizontal">
|
||||
<div class="key">Last Action</div>
|
||||
<div class="key">
|
||||
[[localize('ui.dialogs.more_info_control.script.last_action')]]
|
||||
</div>
|
||||
<div class="value">[[stateObj.attributes.last_action]]</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -28,7 +28,9 @@ class MoreInfoSun extends LocalizeMixin(PolymerElement) {
|
||||
</div>
|
||||
</template>
|
||||
<div class="data-entry layout justified horizontal">
|
||||
<div class="key">Elevation</div>
|
||||
<div class="key">
|
||||
[[localize('ui.dialogs.more_info_control.sun.elevation')]]
|
||||
</div>
|
||||
<div class="value">[[stateObj.attributes.elevation]]</div>
|
||||
</div>
|
||||
`;
|
||||
@ -63,7 +65,10 @@ class MoreInfoSun extends LocalizeMixin(PolymerElement) {
|
||||
}
|
||||
|
||||
itemCaption(type) {
|
||||
return type === "ris" ? "Rising " : "Setting ";
|
||||
if (type === "ris") {
|
||||
return this.localize("ui.dialogs.more_info_control.sun.rising");
|
||||
}
|
||||
return this.localize("ui.dialogs.more_info_control.sun.setting");
|
||||
}
|
||||
|
||||
itemDate(type) {
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import LocalizeMixin from "../../../mixins/localize-mixin";
|
||||
|
||||
class MoreInfoUpdater extends PolymerElement {
|
||||
class MoreInfoUpdater extends LocalizeMixin(PolymerElement) {
|
||||
static get template() {
|
||||
return html`
|
||||
<style>
|
||||
@ -15,7 +16,7 @@ class MoreInfoUpdater extends PolymerElement {
|
||||
class="link"
|
||||
href="https://www.home-assistant.io/docs/installation/updating/"
|
||||
target="_blank"
|
||||
>Update Instructions</a
|
||||
>[[localize('ui.dialogs.more_info_control.updater.title')]]</a
|
||||
>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@ -26,7 +26,7 @@ const isExternal = location.search.includes("external_auth=1");
|
||||
|
||||
const authProm = isExternal
|
||||
? () =>
|
||||
import("../common/auth/external_auth").then(
|
||||
import(/* webpackChunkName: "external_auth" */ "../common/auth/external_auth").then(
|
||||
(mod) => new mod.default(hassUrl)
|
||||
)
|
||||
: () =>
|
||||
|
||||
@ -37,13 +37,15 @@ export const hassLocalizeLitMixin = <T extends LitElement>(
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
|
||||
let language;
|
||||
let resources;
|
||||
if (this.hass) {
|
||||
language = this.hass.language;
|
||||
resources = this.hass.resources;
|
||||
if (this.localize === empty) {
|
||||
let language;
|
||||
let resources;
|
||||
if (this.hass) {
|
||||
language = this.hass.language;
|
||||
resources = this.hass.resources;
|
||||
}
|
||||
this.localize = this.__computeLocalize(language, resources);
|
||||
}
|
||||
this.localize = this.__computeLocalize(language, resources);
|
||||
}
|
||||
|
||||
public updated(changedProperties: PropertyValues) {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import IntlMessageFormat from "intl-messageformat/src/main";
|
||||
import { HomeAssistant } from "../types";
|
||||
|
||||
/**
|
||||
* Adapted from Polymer app-localize-behavior.
|
||||
@ -32,6 +33,7 @@ export interface FormatsType {
|
||||
export type LocalizeFunc = (key: string, ...args: any[]) => string;
|
||||
|
||||
export interface LocalizeMixin {
|
||||
hass?: HomeAssistant;
|
||||
localize: LocalizeFunc;
|
||||
}
|
||||
|
||||
|
||||
@ -169,7 +169,8 @@ class HaConfigCloudAccount extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
||||
fireEvent(this, "register-dialog", {
|
||||
dialogShowEvent: "manage-cloud-webhook",
|
||||
dialogTag: "cloud-webhook-manage-dialog",
|
||||
dialogImport: () => import("./cloud-webhook-manage-dialog"),
|
||||
dialogImport: () =>
|
||||
import(/* webpackChunkName: "cloud-webhook-manage-dialog" */ "./cloud-webhook-manage-dialog"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,7 +172,8 @@ class HaConfigManagerDashboard extends LocalizeMixin(
|
||||
this.fire("register-dialog", {
|
||||
dialogShowEvent: "show-config-flow",
|
||||
dialogTag: "ha-config-flow",
|
||||
dialogImport: () => import("./ha-config-flow"),
|
||||
dialogImport: () =>
|
||||
import(/* webpackChunkName: "ha-config-flow" */ "./ha-config-flow"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,7 +50,7 @@ class HaConfigNavigation extends LocalizeMixin(NavigateMixin(PolymerElement)) {
|
||||
|
||||
pages: {
|
||||
type: Array,
|
||||
value: ["core", "customize", "automation", "script", "zwave"],
|
||||
value: ["core", "customize", "automation", "script", "zha", "zwave"],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@ import(/* webpackChunkName: "panel-config-customize" */ "./customize/ha-config-c
|
||||
import(/* webpackChunkName: "panel-config-dashboard" */ "./dashboard/ha-config-dashboard");
|
||||
import(/* webpackChunkName: "panel-config-script" */ "./script/ha-config-script");
|
||||
import(/* webpackChunkName: "panel-config-users" */ "./users/ha-config-users");
|
||||
import(/* webpackChunkName: "panel-config-zha" */ "./zha/ha-config-zha");
|
||||
import(/* webpackChunkName: "panel-config-zwave" */ "./zwave/ha-config-zwave");
|
||||
|
||||
/*
|
||||
@ -106,6 +107,18 @@ class HaPanelConfig extends EventsMixin(NavigateMixin(PolymerElement)) {
|
||||
></ha-config-script>
|
||||
</template>
|
||||
|
||||
<template
|
||||
is="dom-if"
|
||||
if="[[_equals(_routeData.page, "zha")]]"
|
||||
restamp
|
||||
>
|
||||
<ha-config-zha
|
||||
page-name="zha"
|
||||
hass="[[hass]]"
|
||||
is-wide="[[isWide]]"
|
||||
></ha-config-zha>
|
||||
</template>
|
||||
|
||||
<template
|
||||
is="dom-if"
|
||||
if="[[_equals(_routeData.page, "zwave")]]"
|
||||
|
||||
@ -90,7 +90,8 @@ class HaUserPicker extends EventsMixin(
|
||||
this.fire("register-dialog", {
|
||||
dialogShowEvent: "show-add-user",
|
||||
dialogTag: "ha-dialog-add-user",
|
||||
dialogImport: () => import("./ha-dialog-add-user"),
|
||||
dialogImport: () =>
|
||||
import(/* webpackChunkName: "ha-dialog-add-user" */ "./ha-dialog-add-user"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
80
src/panels/config/zha/ha-config-zha.ts
Executable file
80
src/panels/config/zha/ha-config-zha.ts
Executable file
@ -0,0 +1,80 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import "@polymer/app-layout/app-header/app-header";
|
||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||
import "@polymer/paper-icon-button/paper-icon-button";
|
||||
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
|
||||
import "../../../layouts/ha-app-layout";
|
||||
import "../../../resources/ha-style";
|
||||
|
||||
import "./zha-network";
|
||||
|
||||
export class HaConfigZha extends LitElement {
|
||||
public hass?: HomeAssistant;
|
||||
public isWide?: boolean;
|
||||
private _haStyle?: DocumentFragment;
|
||||
private _ironFlex?: DocumentFragment;
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return {
|
||||
hass: {},
|
||||
isWide: {},
|
||||
};
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
${this.renderStyle()}
|
||||
<ha-app-layout has-scrolling-region="">
|
||||
<app-header slot="header" fixed="">
|
||||
<app-toolbar>
|
||||
<paper-icon-button
|
||||
icon="hass:arrow-left"
|
||||
@click="${this._onBackTapped}"
|
||||
></paper-icon-button>
|
||||
</app-toolbar>
|
||||
</app-header>
|
||||
|
||||
<zha-network
|
||||
id="zha-network"
|
||||
.is-wide="${this.isWide}"
|
||||
.hass="${this.hass}"
|
||||
></zha-network>
|
||||
</ha-app-layout>
|
||||
`;
|
||||
}
|
||||
|
||||
private renderStyle(): TemplateResult {
|
||||
if (!this._haStyle) {
|
||||
this._haStyle = document.importNode(
|
||||
(document.getElementById("ha-style")!
|
||||
.children[0] as HTMLTemplateElement).content,
|
||||
true
|
||||
);
|
||||
}
|
||||
if (!this._ironFlex) {
|
||||
this._ironFlex = document.importNode(
|
||||
(document.getElementById("iron-flex")!
|
||||
.children[0] as HTMLTemplateElement).content,
|
||||
true
|
||||
);
|
||||
}
|
||||
return html`
|
||||
${this._ironFlex} ${this._haStyle}
|
||||
`;
|
||||
}
|
||||
|
||||
private _onBackTapped(): void {
|
||||
history.back();
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-config-zha": HaConfigZha;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-config-zha", HaConfigZha);
|
||||
129
src/panels/config/zha/zha-network.ts
Normal file
129
src/panels/config/zha/zha-network.ts
Normal file
@ -0,0 +1,129 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import "@polymer/paper-button/paper-button";
|
||||
import "@polymer/paper-icon-button/paper-icon-button";
|
||||
import "@polymer/paper-card/paper-card";
|
||||
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
||||
import "../../../components/buttons/ha-call-service-button";
|
||||
import "../../../components/ha-service-description";
|
||||
import "../ha-config-section";
|
||||
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import "../../../resources/ha-style";
|
||||
|
||||
export class ZHANetwork extends LitElement {
|
||||
public hass?: HomeAssistant;
|
||||
public isWide?: boolean;
|
||||
public showDescription: boolean;
|
||||
private _haStyle?: DocumentFragment;
|
||||
private _ironFlex?: DocumentFragment;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.showDescription = false;
|
||||
}
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return {
|
||||
hass: {},
|
||||
isWide: {},
|
||||
showDescription: {},
|
||||
};
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
${this.renderStyle()}
|
||||
<ha-config-section .is-wide="${this.isWide}">
|
||||
<div style="position: relative" slot="header">
|
||||
<span>Zigbee Home Automation network management</span>
|
||||
<paper-icon-button class="toggle-help-icon" @click="${
|
||||
this._onHelpTap
|
||||
}" icon="hass:help-circle"></paper-icon-button>
|
||||
</div>
|
||||
<span slot="introduction">Commands that affect entire network</span>
|
||||
|
||||
<paper-card class="content">
|
||||
<div class="card-actions">
|
||||
<ha-call-service-button .hass="${
|
||||
this.hass
|
||||
}" domain="zha" service="permit">Permit</ha-call-service-button>
|
||||
${
|
||||
this.showDescription
|
||||
? html`
|
||||
<ha-service-description
|
||||
.hass="${this.hass}"
|
||||
domain="zha"
|
||||
service="permit"
|
||||
/>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
</paper-card>
|
||||
</ha-config-section>
|
||||
`;
|
||||
}
|
||||
|
||||
private _onHelpTap(): void {
|
||||
this.showDescription = !this.showDescription;
|
||||
}
|
||||
|
||||
private renderStyle(): TemplateResult {
|
||||
if (!this._haStyle) {
|
||||
this._haStyle = document.importNode(
|
||||
(document.getElementById("ha-style")!
|
||||
.children[0] as HTMLTemplateElement).content,
|
||||
true
|
||||
);
|
||||
}
|
||||
if (!this._ironFlex) {
|
||||
this._ironFlex = document.importNode(
|
||||
(document.getElementById("iron-flex")!
|
||||
.children[0] as HTMLTemplateElement).content,
|
||||
true
|
||||
);
|
||||
}
|
||||
return html`
|
||||
${this._ironFlex} ${this._haStyle}
|
||||
<style>
|
||||
.content {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
paper-card {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.card-actions.warning ha-call-service-button {
|
||||
color: var(--google-red-500);
|
||||
}
|
||||
|
||||
.toggle-help-icon {
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
right: 0;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
ha-service-description {
|
||||
display: block;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"zha-network": ZHANetwork;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("zha-network", ZHANetwork);
|
||||
@ -108,6 +108,16 @@ class ZwaveGroups extends PolymerElement {
|
||||
Remove From Group
|
||||
</ha-call-service-button>
|
||||
</template>
|
||||
<template is="dom-if" if="[[_isBroadcastNodeInGroup]]">
|
||||
<ha-call-service-button
|
||||
hass="[[hass]]"
|
||||
domain="zwave"
|
||||
service="change_association"
|
||||
service-data="[[_removeBroadcastNodeServiceData]]"
|
||||
>
|
||||
Remove Broadcast
|
||||
</ha-call-service-button>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</paper-card>
|
||||
@ -165,6 +175,16 @@ class ZwaveGroups extends PolymerElement {
|
||||
type: String,
|
||||
value: "",
|
||||
},
|
||||
|
||||
_removeBroadcastNodeServiceData: {
|
||||
type: String,
|
||||
value: "",
|
||||
},
|
||||
|
||||
_isBroadcastNodeInGroup: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -201,6 +221,7 @@ class ZwaveGroups extends PolymerElement {
|
||||
|
||||
_computeOtherGroupNodes(selectedGroup) {
|
||||
if (selectedGroup === -1) return -1;
|
||||
this.setProperties({ _isBroadcastNodeInGroup: false });
|
||||
const associations = Object.values(
|
||||
this.groups[selectedGroup].value.association_instances
|
||||
);
|
||||
@ -212,6 +233,17 @@ class ZwaveGroups extends PolymerElement {
|
||||
const id = assoc[0];
|
||||
const instance = assoc[1];
|
||||
const node = this.nodes.find((n) => n.attributes.node_id === id);
|
||||
if (id === 255) {
|
||||
this.setProperties({
|
||||
_isBroadcastNodeInGroup: true,
|
||||
_removeBroadcastNodeServiceData: {
|
||||
node_id: this.nodes[this.selectedNode].attributes.node_id,
|
||||
association: "remove",
|
||||
target_node_id: 255,
|
||||
group: this.groups[selectedGroup].key,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (!node) {
|
||||
return `Unknown Node (${id}: (${instance} ? ${id}.${instance} : ${id}))`;
|
||||
}
|
||||
@ -288,6 +320,7 @@ class ZwaveGroups extends PolymerElement {
|
||||
_otherGroupNodes: Object.values(
|
||||
groupData[this._selectedGroup].value.associations
|
||||
),
|
||||
_isBroadcastNodeInGroup: false,
|
||||
});
|
||||
const oldGroup = this._selectedGroup;
|
||||
this.setProperties({ _selectedGroup: -1 });
|
||||
|
||||
@ -133,7 +133,8 @@ class OzwLog extends EventsMixin(PolymerElement) {
|
||||
this.fire("register-dialog", {
|
||||
dialogShowEvent: "show-ozwlog-dialog",
|
||||
dialogTag: "zwave-log-dialog",
|
||||
dialogImport: () => import("./zwave-log-dialog"),
|
||||
dialogImport: () =>
|
||||
import(/* webpackChunkName: "zwave-log-dialog" */ "./zwave-log-dialog"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -312,7 +312,8 @@ class HaPanelDevInfo extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
||||
this.fire("register-dialog", {
|
||||
dialogShowEvent: "show-loaded-components",
|
||||
dialogTag: "ha-loaded-components",
|
||||
dialogImport: () => import("./ha-loaded-components"),
|
||||
dialogImport: () =>
|
||||
import(/* webpackChunkName: "ha-loaded-components" */ "./ha-loaded-components"),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -28,6 +28,7 @@ class HaPanelDevState extends EventsMixin(PolymerElement) {
|
||||
|
||||
.content {
|
||||
padding: 16px;
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
ha-entity-picker,
|
||||
|
||||
@ -1,258 +0,0 @@
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
|
||||
import "../../../components/ha-card";
|
||||
|
||||
import EventsMixin from "../../../mixins/events-mixin";
|
||||
import LocalizeMixin from "../../../mixins/localize-mixin";
|
||||
import "../../../components/ha-label-badge";
|
||||
|
||||
/*
|
||||
* @appliesMixin EventsMixin
|
||||
*/
|
||||
|
||||
const Icons = {
|
||||
armed_away: "hass:security-lock",
|
||||
armed_custom_bypass: "hass:security",
|
||||
armed_home: "hass:security-home",
|
||||
armed_night: "hass:security-home",
|
||||
disarmed: "hass:verified",
|
||||
pending: "hass:shield-outline",
|
||||
triggered: "hass:bell-ring",
|
||||
};
|
||||
|
||||
class HuiAlarmPanelCard extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||
static get template() {
|
||||
return html`
|
||||
<style>
|
||||
ha-card {
|
||||
padding-bottom: 16px;
|
||||
position: relative;
|
||||
--alarm-color-disarmed: var(--label-badge-green);
|
||||
--alarm-color-pending: var(--label-badge-yellow);
|
||||
--alarm-color-triggered: var(--label-badge-red);
|
||||
--alarm-color-armed: var(--label-badge-red);
|
||||
--alarm-color-autoarm: rgba(0, 153, 255, .1);
|
||||
--alarm-state-color: var(--alarm-color-armed);
|
||||
--base-unit: 15px;
|
||||
font-size: calc(var(--base-unit));
|
||||
}
|
||||
ha-label-badge {
|
||||
--ha-label-badge-color: var(--alarm-state-color);
|
||||
--label-badge-text-color: var(--alarm-state-color);
|
||||
color: var(--alarm-state-color);
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 12px;
|
||||
}
|
||||
.disarmed {
|
||||
--alarm-state-color: var(--alarm-color-disarmed);
|
||||
}
|
||||
.triggered {
|
||||
--alarm-state-color: var(--alarm-color-triggered);
|
||||
animation: pulse 1s infinite;
|
||||
}
|
||||
.arming {
|
||||
--alarm-state-color: var(--alarm-color-pending);
|
||||
animation: pulse 1s infinite;
|
||||
}
|
||||
.pending {
|
||||
--alarm-state-color: var(--alarm-color-pending);
|
||||
animation: pulse 1s infinite;
|
||||
}
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
--ha-label-badge-color: var(--alarm-state-color);
|
||||
}
|
||||
100% {
|
||||
--ha-label-badge-color: rgba(255, 153, 0, 0.3);
|
||||
}
|
||||
}
|
||||
paper-input {
|
||||
margin: auto;
|
||||
max-width: 200px;
|
||||
font-size: calc(var(--base-unit));
|
||||
}
|
||||
.state {
|
||||
margin-left: 16px;
|
||||
font-size: calc(var(--base-unit) * 0.9);
|
||||
position: relative;
|
||||
bottom: 16px;
|
||||
color: var(--alarm-state-color);
|
||||
animation: none;
|
||||
}
|
||||
#keypad {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
#keypad div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
#keypad paper-button {
|
||||
margin-bottom: 10%;
|
||||
position: relative;
|
||||
padding: calc(var(--base-unit));
|
||||
font-size: calc(var(--base-unit) * 1.1);
|
||||
}
|
||||
.actions {
|
||||
margin: 0 8px;
|
||||
padding-top: 20px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
font-size: calc(var(--base-unit) * 1);
|
||||
}
|
||||
.actions paper-button {
|
||||
min-width: calc(var(--base-unit) * 9);
|
||||
color: var(--primary-color);
|
||||
}
|
||||
paper-button#disarm {
|
||||
color: var(--google-red-500);
|
||||
}
|
||||
.not-found {
|
||||
flex: 1;
|
||||
background-color: yellow;
|
||||
padding: 8px;
|
||||
}
|
||||
</style>
|
||||
<ha-card
|
||||
header$="[[_computeHeader(localize, _stateObj)]]"
|
||||
class$="[[_computeClassName(_stateObj)]]"
|
||||
>
|
||||
<template is="dom-if" if="[[_stateObj]]">
|
||||
<ha-label-badge
|
||||
class$="[[_stateObj.state]]"
|
||||
icon="[[_computeIcon(_stateObj)]]"
|
||||
label="[[_stateIconLabel(_stateObj.state)]]"
|
||||
></ha-label-badge>
|
||||
<template is="dom-if" if="[[_showActionToggle(_stateObj.state)]]">
|
||||
<div id="armActions" class="actions">
|
||||
<template is="dom-repeat" items="[[_config.states]]">
|
||||
<paper-button noink raised id="[[item]]" on-click="_handleActionClick">[[_label(localize, item)]]</paper-button>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<template is="dom-if" if="[[!_showActionToggle(_stateObj.state)]]">
|
||||
<div id="disarmActions" class="actions">
|
||||
<paper-button noink raised id="disarm" on-click="_handleActionClick">[[_label(localize, "disarm")]]</paper-button>
|
||||
</div>
|
||||
</template>
|
||||
<paper-input label="Alarm Code" type="password" value="[[_value]]"></paper-input>
|
||||
<div id="keypad">
|
||||
<div>
|
||||
<paper-button noink raised value="1" on-click="_handlePadClick">1</paper-button>
|
||||
<paper-button noink raised value="4" on-click="_handlePadClick">4</paper-button>
|
||||
<paper-button noink raised value="7" on-click="_handlePadClick">7</paper-button>
|
||||
</div>
|
||||
<div>
|
||||
<paper-button noink raised value="2" on-click="_handlePadClick">2</paper-button>
|
||||
<paper-button noink raised value="5" on-click="_handlePadClick">5</paper-button>
|
||||
<paper-button noink raised value="8" on-click="_handlePadClick">8</paper-button>
|
||||
<paper-button noink raised value="0" on-click="_handlePadClick">0</paper-button>
|
||||
</div>
|
||||
<div>
|
||||
<paper-button noink raised value="3" on-click="_handlePadClick">3</paper-button>
|
||||
<paper-button noink raised value="6" on-click="_handlePadClick">6</paper-button>
|
||||
<paper-button noink raised value="9" on-click="_handlePadClick">9</paper-button>
|
||||
<paper-button noink raised value="clear" on-click="_handlePadClick">[[_label(localize, "clear_code")]]</paper-button>
|
||||
</div>
|
||||
</template>
|
||||
<template is="dom-if" if="[[!_stateObj]]">
|
||||
<div>Entity not available: [[_config.entity]]</div>
|
||||
</template>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
_config: Object,
|
||||
_stateObj: {
|
||||
type: Object,
|
||||
computed: "_computeStateObj(hass.states, _config.entity)",
|
||||
},
|
||||
_value: {
|
||||
type: String,
|
||||
value: "",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
getCardSize() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
setConfig(config) {
|
||||
if (
|
||||
!config ||
|
||||
!config.entity ||
|
||||
config.entity.split(".")[0] !== "alarm_control_panel"
|
||||
) {
|
||||
throw new Error("Invalid card configuration");
|
||||
}
|
||||
|
||||
const defaults = {
|
||||
states: ["arm_away", "arm_home"],
|
||||
};
|
||||
|
||||
this._config = { ...defaults, ...config };
|
||||
this._icons = Icons;
|
||||
}
|
||||
|
||||
_computeStateObj(states, entityId) {
|
||||
return states && entityId in states ? states[entityId] : null;
|
||||
}
|
||||
|
||||
_computeHeader(localize, stateObj) {
|
||||
if (!stateObj) return "";
|
||||
return this._config.name
|
||||
? this._config.name
|
||||
: this._label(localize, stateObj.state);
|
||||
}
|
||||
|
||||
_computeIcon(stateObj) {
|
||||
return this._icons[stateObj.state] || "hass:shield-outline";
|
||||
}
|
||||
|
||||
_label(localize, state) {
|
||||
return (
|
||||
localize(`state.alarm_control_panel.${state}`) ||
|
||||
localize(`ui.card.alarm_control_panel.${state}`)
|
||||
);
|
||||
}
|
||||
|
||||
_stateIconLabel(state) {
|
||||
const stateLabel = state.split("_").pop();
|
||||
return stateLabel === "disarmed" || stateLabel === "triggered"
|
||||
? ""
|
||||
: stateLabel;
|
||||
}
|
||||
|
||||
_showActionToggle(state) {
|
||||
return state === "disarmed";
|
||||
}
|
||||
|
||||
_computeClassName(stateObj) {
|
||||
if (!stateObj) return "not-found";
|
||||
return "";
|
||||
}
|
||||
|
||||
_handlePadClick(e) {
|
||||
const val = e.target.getAttribute("value");
|
||||
this._value = val === "clear" ? "" : this._value + val;
|
||||
}
|
||||
|
||||
_handleActionClick(e) {
|
||||
this.hass.callService("alarm_control_panel", "alarm_" + e.target.id, {
|
||||
entity_id: this._stateObj.entity_id,
|
||||
code: this._value,
|
||||
});
|
||||
this._value = "";
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-alarm-panel-card", HuiAlarmPanelCard);
|
||||
316
src/panels/lovelace/cards/hui-alarm-panel-card.ts
Normal file
316
src/panels/lovelace/cards/hui-alarm-panel-card.ts
Normal file
@ -0,0 +1,316 @@
|
||||
import {
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
PropertyDeclarations,
|
||||
} from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import { classMap } from "lit-html/directives/classMap";
|
||||
|
||||
import { LovelaceCard } from "../types";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceCardConfig } from "../../../data/lovelace";
|
||||
import { callAlarmAction } from "../../../data/alarm_control_panel";
|
||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
||||
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-label-badge";
|
||||
import {
|
||||
createErrorCardConfig,
|
||||
createErrorCardElement,
|
||||
} from "./hui-error-card";
|
||||
|
||||
const ICONS = {
|
||||
armed_away: "hass:security-lock",
|
||||
armed_custom_bypass: "hass:security",
|
||||
armed_home: "hass:security-home",
|
||||
armed_night: "hass:security-home",
|
||||
disarmed: "hass:verified",
|
||||
pending: "hass:shield-outline",
|
||||
triggered: "hass:bell-ring",
|
||||
};
|
||||
|
||||
const BUTTONS = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "", "0", "clear"];
|
||||
|
||||
export interface Config extends LovelaceCardConfig {
|
||||
entity: string;
|
||||
name?: string;
|
||||
states?: string[];
|
||||
}
|
||||
|
||||
class HuiAlarmPanelCard extends hassLocalizeLitMixin(LitElement)
|
||||
implements LovelaceCard {
|
||||
public static async getConfigElement() {
|
||||
await import(/* webpackChunkName: "hui-alarm-panel-card-editor" */ "../editor/config-elements/hui-alarm-panel-card-editor");
|
||||
return document.createElement("hui-alarm-panel-card-editor");
|
||||
}
|
||||
|
||||
public static getStubConfig() {
|
||||
return { states: ["arm_home", "arm_away"] };
|
||||
}
|
||||
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
private _code?: string;
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return {
|
||||
hass: {},
|
||||
_config: {},
|
||||
_code: {},
|
||||
};
|
||||
}
|
||||
|
||||
public getCardSize(): number {
|
||||
return 4;
|
||||
}
|
||||
|
||||
public setConfig(config: Config): void {
|
||||
if (
|
||||
!config ||
|
||||
!config.entity ||
|
||||
config.entity.split(".")[0] !== "alarm_control_panel"
|
||||
) {
|
||||
throw new Error("Invalid card configuration");
|
||||
}
|
||||
|
||||
const defaults = {
|
||||
states: ["arm_away", "arm_home"],
|
||||
};
|
||||
|
||||
this._config = { ...defaults, ...config };
|
||||
this._code = "";
|
||||
}
|
||||
|
||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||
if (changedProps.has("_config") || changedProps.has("_code")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||
if (oldHass) {
|
||||
return (
|
||||
oldHass.states[this._config!.entity] !==
|
||||
this.hass!.states[this._config!.entity]
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._config || !this.hass) {
|
||||
return html``;
|
||||
}
|
||||
const stateObj = this.hass.states[this._config.entity];
|
||||
|
||||
if (!stateObj) {
|
||||
const element = createErrorCardElement(
|
||||
createErrorCardConfig("Entity not Found!", this._config)
|
||||
);
|
||||
return html`
|
||||
${element}
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
${this.renderStyle()}
|
||||
<ha-card .header="${this._config.name || this._label(stateObj.state)}">
|
||||
<ha-label-badge
|
||||
class="${classMap({ [stateObj.state]: true })}"
|
||||
.icon="${ICONS[stateObj.state] || "hass:shield-outline"}"
|
||||
.label="${this._stateIconLabel(stateObj.state)}"
|
||||
></ha-label-badge>
|
||||
<div id="armActions" class="actions">
|
||||
${
|
||||
(stateObj.state === "disarmed"
|
||||
? this._config.states!
|
||||
: ["disarm"]
|
||||
).map((state) => {
|
||||
return html`
|
||||
<paper-button
|
||||
noink
|
||||
raised
|
||||
.action="${state}"
|
||||
@click="${this._handleActionClick}"
|
||||
>${this._label(state)}</paper-button
|
||||
>
|
||||
`;
|
||||
})
|
||||
}
|
||||
</div>
|
||||
${
|
||||
!stateObj.attributes.code_format
|
||||
? html``
|
||||
: html`
|
||||
<paper-input
|
||||
label="Alarm Code"
|
||||
type="password"
|
||||
.value="${this._code}"
|
||||
></paper-input>
|
||||
`
|
||||
}
|
||||
${
|
||||
stateObj.attributes.code_format !== "Number"
|
||||
? html``
|
||||
: html`
|
||||
<div id="keypad">
|
||||
${
|
||||
BUTTONS.map((value) => {
|
||||
return value === ""
|
||||
? html`
|
||||
<paper-button disabled></paper-button>
|
||||
`
|
||||
: html`
|
||||
<paper-button
|
||||
noink
|
||||
raised
|
||||
.value="${value}"
|
||||
@click="${this._handlePadClick}"
|
||||
>${
|
||||
value === "clear"
|
||||
? this._label("clear_code")
|
||||
: value
|
||||
}</paper-button
|
||||
>
|
||||
`;
|
||||
})
|
||||
}
|
||||
</div>
|
||||
`
|
||||
}
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
private _stateIconLabel(state: string): string {
|
||||
const stateLabel = state.split("_").pop();
|
||||
return stateLabel === "disarmed" ||
|
||||
stateLabel === "triggered" ||
|
||||
!stateLabel
|
||||
? ""
|
||||
: stateLabel;
|
||||
}
|
||||
|
||||
private _label(state: string): string {
|
||||
return (
|
||||
this.localize(`state.alarm_control_panel.${state}`) ||
|
||||
this.localize(`ui.card.alarm_control_panel.${state}`)
|
||||
);
|
||||
}
|
||||
|
||||
private _handlePadClick(e: MouseEvent): void {
|
||||
const val = (e.currentTarget! as any).value;
|
||||
this._code = val === "clear" ? "" : this._code + val;
|
||||
}
|
||||
|
||||
private _handleActionClick(e: MouseEvent): void {
|
||||
callAlarmAction(
|
||||
this.hass!,
|
||||
this._config!.entity_id,
|
||||
(e.currentTarget! as any).action,
|
||||
this._code!
|
||||
);
|
||||
this._code = "";
|
||||
}
|
||||
|
||||
private renderStyle(): TemplateResult {
|
||||
return html`
|
||||
<style>
|
||||
ha-card {
|
||||
padding-bottom: 16px;
|
||||
position: relative;
|
||||
--alarm-color-disarmed: var(--label-badge-green);
|
||||
--alarm-color-pending: var(--label-badge-yellow);
|
||||
--alarm-color-triggered: var(--label-badge-red);
|
||||
--alarm-color-armed: var(--label-badge-red);
|
||||
--alarm-color-autoarm: rgba(0, 153, 255, 0.1);
|
||||
--alarm-state-color: var(--alarm-color-armed);
|
||||
--base-unit: 15px;
|
||||
font-size: calc(var(--base-unit));
|
||||
}
|
||||
ha-label-badge {
|
||||
--ha-label-badge-color: var(--alarm-state-color);
|
||||
--label-badge-text-color: var(--alarm-state-color);
|
||||
--label-badge-background-color: var(--paper-card-background-color);
|
||||
color: var(--alarm-state-color);
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 12px;
|
||||
}
|
||||
.disarmed {
|
||||
--alarm-state-color: var(--alarm-color-disarmed);
|
||||
}
|
||||
.triggered {
|
||||
--alarm-state-color: var(--alarm-color-triggered);
|
||||
animation: pulse 1s infinite;
|
||||
}
|
||||
.arming {
|
||||
--alarm-state-color: var(--alarm-color-pending);
|
||||
animation: pulse 1s infinite;
|
||||
}
|
||||
.pending {
|
||||
--alarm-state-color: var(--alarm-color-pending);
|
||||
animation: pulse 1s infinite;
|
||||
}
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
--ha-label-badge-color: var(--alarm-state-color);
|
||||
}
|
||||
100% {
|
||||
--ha-label-badge-color: rgba(255, 153, 0, 0.3);
|
||||
}
|
||||
}
|
||||
paper-input {
|
||||
margin: 0 auto 8px;
|
||||
max-width: 150px;
|
||||
font-size: calc(var(--base-unit));
|
||||
text-align: center;
|
||||
}
|
||||
.state {
|
||||
margin-left: 16px;
|
||||
font-size: calc(var(--base-unit) * 0.9);
|
||||
position: relative;
|
||||
bottom: 16px;
|
||||
color: var(--alarm-state-color);
|
||||
animation: none;
|
||||
}
|
||||
#keypad {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
margin: auto;
|
||||
width: 300px;
|
||||
}
|
||||
#keypad paper-button {
|
||||
margin-bottom: 5%;
|
||||
width: 30%;
|
||||
padding: calc(var(--base-unit));
|
||||
font-size: calc(var(--base-unit) * 1.1);
|
||||
}
|
||||
.actions {
|
||||
margin: 0 8px;
|
||||
padding-top: 20px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
font-size: calc(var(--base-unit) * 1);
|
||||
}
|
||||
.actions paper-button {
|
||||
min-width: calc(var(--base-unit) * 9);
|
||||
color: var(--primary-color);
|
||||
}
|
||||
paper-button#disarm {
|
||||
color: var(--google-red-500);
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-alarm-panel-card": HuiAlarmPanelCard;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-alarm-panel-card", HuiAlarmPanelCard);
|
||||
@ -1,4 +1,4 @@
|
||||
import createCardElement from "../common/create-card-element";
|
||||
import { createCardElement } from "../common/create-card-element";
|
||||
import { computeCardSize } from "../common/compute-card-size";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceCard } from "../types";
|
||||
|
||||
@ -17,7 +17,7 @@ import { EntityConfig, EntityRow } from "../entity-rows/types";
|
||||
import { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||
import { LovelaceCardConfig } from "../../../data/lovelace";
|
||||
import { processConfigEntities } from "../common/process-config-entities";
|
||||
import createRowElement from "../common/create-row-element";
|
||||
import { createRowElement } from "../common/create-row-element";
|
||||
import computeDomain from "../../../common/entity/compute_domain";
|
||||
import applyThemesOnElement from "../../../common/dom/apply_themes_on_element";
|
||||
|
||||
@ -40,7 +40,7 @@ export interface Config extends LovelaceCardConfig {
|
||||
class HuiEntitiesCard extends hassLocalizeLitMixin(LitElement)
|
||||
implements LovelaceCard {
|
||||
public static async getConfigElement(): Promise<LovelaceCardEditor> {
|
||||
await import("../editor/config-elements/hui-entities-card-editor");
|
||||
await import(/* webpackChunkName: "hui-entities-card-editor" */ "../editor/config-elements/hui-entities-card-editor");
|
||||
return document.createElement("hui-entities-card-editor");
|
||||
}
|
||||
|
||||
|
||||
@ -17,12 +17,12 @@ import computeStateName from "../../../common/entity/compute_state_name";
|
||||
import applyThemesOnElement from "../../../common/dom/apply_themes_on_element";
|
||||
import { HomeAssistant, LightEntity } from "../../../types";
|
||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
||||
import { LovelaceCard } from "../types";
|
||||
import { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||
import { LovelaceCardConfig, ActionConfig } from "../../../data/lovelace";
|
||||
import { longPress } from "../common/directives/long-press-directive";
|
||||
import { handleClick } from "../common/handle-click";
|
||||
|
||||
interface Config extends LovelaceCardConfig {
|
||||
export interface Config extends LovelaceCardConfig {
|
||||
entity: string;
|
||||
name?: string;
|
||||
icon?: string;
|
||||
@ -33,6 +33,18 @@ interface Config extends LovelaceCardConfig {
|
||||
|
||||
class HuiEntityButtonCard extends hassLocalizeLitMixin(LitElement)
|
||||
implements LovelaceCard {
|
||||
public static async getConfigElement(): Promise<LovelaceCardEditor> {
|
||||
await import(/* webpackChunkName: "hui-entity-button-card-editor" */ "../editor/config-elements/hui-entity-button-card-editor");
|
||||
return document.createElement("hui-entity-button-card-editor");
|
||||
}
|
||||
|
||||
public static getStubConfig(): object {
|
||||
return {
|
||||
tap_action: { action: "more-info" },
|
||||
hold_action: { action: "none" },
|
||||
};
|
||||
}
|
||||
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
|
||||
import createCardElement from "../common/create-card-element";
|
||||
import { createCardElement } from "../common/create-card-element";
|
||||
import { processConfigEntities } from "../common/process-config-entities";
|
||||
|
||||
function getEntities(hass, filterState, entities) {
|
||||
|
||||
@ -3,13 +3,27 @@ import { html, LitElement } from "@polymer/lit-element";
|
||||
import { LovelaceCard } from "../types";
|
||||
import { LovelaceCardConfig } from "../../../data/lovelace";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
|
||||
interface Config extends LovelaceCardConfig {
|
||||
error: string;
|
||||
origConfig: LovelaceCardConfig;
|
||||
}
|
||||
|
||||
class HuiErrorCard extends LitElement implements LovelaceCard {
|
||||
export const createErrorCardElement = (config) => {
|
||||
const el = document.createElement("hui-error-card");
|
||||
el.setConfig(config);
|
||||
return el;
|
||||
};
|
||||
|
||||
export const createErrorCardConfig = (error, origConfig) => ({
|
||||
type: "error",
|
||||
error,
|
||||
origConfig,
|
||||
});
|
||||
|
||||
export class HuiErrorCard extends LitElement implements LovelaceCard {
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
|
||||
static get properties() {
|
||||
|
||||
@ -5,30 +5,40 @@ import {
|
||||
PropertyValues,
|
||||
} from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import { styleMap } from "lit-html/directives/styleMap";
|
||||
|
||||
import { LovelaceCard } from "../types";
|
||||
import "../../../components/ha-card";
|
||||
import { LovelaceCardConfig } from "../../../data/lovelace";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
||||
|
||||
import isValidEntityId from "../../../common/entity/valid_entity_id";
|
||||
import applyThemesOnElement from "../../../common/dom/apply_themes_on_element";
|
||||
import computeStateName from "../../../common/entity/compute_state_name";
|
||||
|
||||
import "../../../components/ha-card";
|
||||
import { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||
import {
|
||||
createErrorCardConfig,
|
||||
createErrorCardElement,
|
||||
} from "./hui-error-card";
|
||||
|
||||
interface Config extends LovelaceCardConfig {
|
||||
export interface SeverityConfig {
|
||||
green?: number;
|
||||
yellow?: number;
|
||||
red?: number;
|
||||
}
|
||||
|
||||
export interface Config extends LovelaceCardConfig {
|
||||
entity: string;
|
||||
name?: string;
|
||||
unit?: string;
|
||||
min?: number;
|
||||
max?: number;
|
||||
severity?: object;
|
||||
severity?: SeverityConfig;
|
||||
theme?: string;
|
||||
}
|
||||
|
||||
const severityMap = {
|
||||
export const severityMap = {
|
||||
red: "var(--label-badge-red)",
|
||||
green: "var(--label-badge-green)",
|
||||
yellow: "var(--label-badge-yellow)",
|
||||
@ -36,6 +46,14 @@ const severityMap = {
|
||||
};
|
||||
|
||||
class HuiGaugeCard extends LitElement implements LovelaceCard {
|
||||
public static async getConfigElement(): Promise<LovelaceCardEditor> {
|
||||
await import(/* webpackChunkName: "hui-gauge-card-editor" */ "../editor/config-elements/hui-gauge-card-editor");
|
||||
return document.createElement("hui-gauge-card-editor");
|
||||
}
|
||||
public static getStubConfig(): object {
|
||||
return {};
|
||||
}
|
||||
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
|
||||
@ -65,11 +83,23 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
|
||||
return html``;
|
||||
}
|
||||
const stateObj = this.hass.states[this._config.entity];
|
||||
let state;
|
||||
let error;
|
||||
|
||||
if (!stateObj) {
|
||||
error = "Entity not available: " + this._config.entity;
|
||||
} else if (isNaN(Number(stateObj.state))) {
|
||||
error = "Entity is non-numeric: " + this._config.entity;
|
||||
} else {
|
||||
state = Number(stateObj.state);
|
||||
|
||||
if (isNaN(state)) {
|
||||
error = "Entity is non-numeric: " + this._config.entity;
|
||||
}
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return html`
|
||||
${createErrorCardElement(createErrorCardConfig(error, this._config))}
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
@ -84,7 +114,15 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
|
||||
<div class="container">
|
||||
<div class="gauge-a"></div>
|
||||
<div class="gauge-b"></div>
|
||||
<div class="gauge-c" id="gauge"></div>
|
||||
<div
|
||||
class="gauge-c"
|
||||
style="${
|
||||
styleMap({
|
||||
transform: `rotate(${this._translateTurn(state)}turn)`,
|
||||
"background-color": this._computeSeverity(state),
|
||||
})
|
||||
}"
|
||||
></div>
|
||||
<div class="gauge-data">
|
||||
<div id="percent">
|
||||
${stateObj.state}
|
||||
@ -109,38 +147,74 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
|
||||
return hasConfigOrEntityChanged(this, changedProps);
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues): void {
|
||||
super.updated(changedProps);
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const stateObj = this.hass.states[this._config.entity];
|
||||
if (isNaN(Number(stateObj.state))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const turn = this._translateTurn(Number(stateObj.state), this._config);
|
||||
|
||||
this.shadowRoot!.getElementById(
|
||||
"gauge"
|
||||
)!.style.cssText = `transform: rotate(${turn}turn); background-color: ${this._computeSeverity(
|
||||
stateObj.state,
|
||||
this._config.severity!
|
||||
)}`;
|
||||
|
||||
protected firstUpdated(): void {
|
||||
(this.shadowRoot!.querySelector(
|
||||
"ha-card"
|
||||
)! as HTMLElement).style.setProperty(
|
||||
"--base-unit",
|
||||
this._computeBaseUnit()
|
||||
);
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues): void {
|
||||
super.updated(changedProps);
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
|
||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||
|
||||
if (!oldHass || oldHass.themes !== this.hass.themes) {
|
||||
applyThemesOnElement(this, this.hass.themes, this._config.theme);
|
||||
}
|
||||
}
|
||||
|
||||
private _computeSeverity(numberValue: number): string {
|
||||
const sections = this._config!.severity;
|
||||
|
||||
if (!sections) {
|
||||
return severityMap.normal;
|
||||
}
|
||||
|
||||
const sectionsArray = Object.keys(sections);
|
||||
const sortable = sectionsArray.map((severity) => [
|
||||
severity,
|
||||
sections[severity],
|
||||
]);
|
||||
|
||||
for (const severity of sortable) {
|
||||
if (severityMap[severity[0]] == null || isNaN(severity[1])) {
|
||||
return severityMap.normal;
|
||||
}
|
||||
}
|
||||
sortable.sort((a, b) => a[1] - b[1]);
|
||||
|
||||
if (numberValue >= sortable[0][1] && numberValue < sortable[1][1]) {
|
||||
return severityMap[sortable[0][0]];
|
||||
}
|
||||
if (numberValue >= sortable[1][1] && numberValue < sortable[2][1]) {
|
||||
return severityMap[sortable[1][0]];
|
||||
}
|
||||
if (numberValue >= sortable[2][1]) {
|
||||
return severityMap[sortable[2][0]];
|
||||
}
|
||||
return severityMap.normal;
|
||||
}
|
||||
|
||||
private _translateTurn(value: number): number {
|
||||
const { min, max } = this._config!;
|
||||
const maxTurnValue = Math.min(Math.max(value, min!), max!);
|
||||
return (5 * (maxTurnValue - min!)) / (max! - min!) / 10;
|
||||
}
|
||||
|
||||
private _computeBaseUnit(): string {
|
||||
return this.clientWidth < 200 ? this.clientWidth / 5 + "px" : "50px";
|
||||
}
|
||||
|
||||
private _handleClick(): void {
|
||||
fireEvent(this, "hass-more-info", { entityId: this._config!.entity });
|
||||
}
|
||||
|
||||
private renderStyle(): TemplateResult {
|
||||
return html`
|
||||
<style>
|
||||
@ -223,53 +297,6 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
|
||||
</style>
|
||||
`;
|
||||
}
|
||||
|
||||
private _computeSeverity(stateValue: string, sections: object): string {
|
||||
const numberValue = Number(stateValue);
|
||||
|
||||
if (!sections) {
|
||||
return severityMap.normal;
|
||||
}
|
||||
|
||||
const sectionsArray = Object.keys(sections);
|
||||
const sortable = sectionsArray.map((severity) => [
|
||||
severity,
|
||||
sections[severity],
|
||||
]);
|
||||
|
||||
for (const severity of sortable) {
|
||||
if (severityMap[severity[0]] == null || isNaN(severity[1])) {
|
||||
return severityMap.normal;
|
||||
}
|
||||
}
|
||||
sortable.sort((a, b) => a[1] - b[1]);
|
||||
|
||||
if (numberValue >= sortable[0][1] && numberValue < sortable[1][1]) {
|
||||
return severityMap[sortable[0][0]];
|
||||
}
|
||||
if (numberValue >= sortable[1][1] && numberValue < sortable[2][1]) {
|
||||
return severityMap[sortable[1][0]];
|
||||
}
|
||||
if (numberValue >= sortable[2][1]) {
|
||||
return severityMap[sortable[2][0]];
|
||||
}
|
||||
return severityMap.normal;
|
||||
}
|
||||
|
||||
private _translateTurn(value: number, config: Config): number {
|
||||
const maxTurnValue = Math.min(Math.max(value, config.min!), config.max!);
|
||||
return (
|
||||
(5 * (maxTurnValue - config.min!)) / (config.max! - config.min!) / 10
|
||||
);
|
||||
}
|
||||
|
||||
private _computeBaseUnit(): string {
|
||||
return this.clientWidth < 200 ? this.clientWidth / 5 + "px" : "50px";
|
||||
}
|
||||
|
||||
private _handleClick(): void {
|
||||
fireEvent(this, "hass-more-info", { entityId: this._config!.entity });
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@ -41,7 +41,7 @@ export interface Config extends LovelaceCardConfig {
|
||||
export class HuiGlanceCard extends hassLocalizeLitMixin(LitElement)
|
||||
implements LovelaceCard {
|
||||
public static async getConfigElement(): Promise<LovelaceCardEditor> {
|
||||
await import("../editor/config-elements/hui-glance-card-editor");
|
||||
await import(/* webpackChunkName: "hui-glance-card-editor" */ "../editor/config-elements/hui-glance-card-editor");
|
||||
return document.createElement("hui-glance-card-editor");
|
||||
}
|
||||
public static getStubConfig(): object {
|
||||
|
||||
@ -2,18 +2,26 @@ import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
|
||||
import "../../../components/ha-card";
|
||||
|
||||
import { LovelaceCard } from "../types";
|
||||
import { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||
import { LovelaceCardConfig } from "../../../data/lovelace";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import { styleMap } from "lit-html/directives/styleMap";
|
||||
|
||||
interface Config extends LovelaceCardConfig {
|
||||
export interface Config extends LovelaceCardConfig {
|
||||
aspect_ratio?: string;
|
||||
title?: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export class HuiIframeCard extends LitElement implements LovelaceCard {
|
||||
public static async getConfigElement(): Promise<LovelaceCardEditor> {
|
||||
await import(/* webpackChunkName: "hui-iframe-card-editor" */ "../editor/config-elements/hui-iframe-card-editor");
|
||||
return document.createElement("hui-iframe-card-editor");
|
||||
}
|
||||
public static getStubConfig(): object {
|
||||
return { url: "https://www.home-assistant.io", aspect_ratio: "50%" };
|
||||
}
|
||||
|
||||
protected _config?: Config;
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import createErrorCardConfig from "../common/create-error-card-config";
|
||||
import { createErrorCardConfig } from "./hui-error-card";
|
||||
import computeDomain from "../../../common/entity/compute_domain";
|
||||
|
||||
export default class LegacyWrapperCard extends HTMLElement {
|
||||
|
||||
@ -10,7 +10,7 @@ import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { styleMap } from "lit-html/directives/styleMap";
|
||||
import { HomeAssistant, LightEntity } from "../../../types";
|
||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
||||
import { LovelaceCard } from "../types";
|
||||
import { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||
import { LovelaceCardConfig } from "../../../data/lovelace";
|
||||
import { longPress } from "../common/directives/long-press-directive";
|
||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
||||
@ -39,7 +39,7 @@ const lightConfig = {
|
||||
animation: false,
|
||||
};
|
||||
|
||||
interface Config extends LovelaceCardConfig {
|
||||
export interface Config extends LovelaceCardConfig {
|
||||
entity: string;
|
||||
name?: string;
|
||||
theme?: string;
|
||||
@ -47,6 +47,14 @@ interface Config extends LovelaceCardConfig {
|
||||
|
||||
export class HuiLightCard extends hassLocalizeLitMixin(LitElement)
|
||||
implements LovelaceCard {
|
||||
public static async getConfigElement(): Promise<LovelaceCardEditor> {
|
||||
await import(/* webpackChunkName: "hui-light-card-editor" */ "../editor/config-elements/hui-light-card-editor");
|
||||
return document.createElement("hui-light-card-editor");
|
||||
}
|
||||
public static getStubConfig(): object {
|
||||
return {};
|
||||
}
|
||||
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
private _brightnessTimout?: number;
|
||||
|
||||
@ -11,7 +11,24 @@ import computeStateName from "../../../common/entity/compute_state_name";
|
||||
import debounce from "../../../common/util/debounce";
|
||||
import parseAspectRatio from "../../../common/util/parse-aspect-ratio";
|
||||
|
||||
// should be interface when converted to TS
|
||||
export const Config = {
|
||||
title: "",
|
||||
aspect_ratio: "",
|
||||
default_zoom: 14,
|
||||
entities: [],
|
||||
};
|
||||
|
||||
class HuiMapCard extends PolymerElement {
|
||||
static async getConfigElement() {
|
||||
await import(/* webpackChunkName: "hui-map-card-editor" */ "../editor/config-elements/hui-map-card-editor");
|
||||
return document.createElement("hui-map-card-editor");
|
||||
}
|
||||
|
||||
static getStubConfig() {
|
||||
return { entities: [] };
|
||||
}
|
||||
|
||||
static get template() {
|
||||
return html`
|
||||
<style>
|
||||
@ -111,8 +128,24 @@ class HuiMapCard extends PolymerElement {
|
||||
throw new Error("Error in card configuration.");
|
||||
}
|
||||
|
||||
this._configEntities = processConfigEntities(config.entities);
|
||||
if (!config.entities && !config.geo_location_sources) {
|
||||
throw new Error(
|
||||
"Either entities or geo_location_sources must be defined"
|
||||
);
|
||||
}
|
||||
if (config.entities && !Array.isArray(config.entities)) {
|
||||
throw new Error("Entities need to be an array");
|
||||
}
|
||||
if (
|
||||
config.geo_location_sources &&
|
||||
!Array.isArray(config.geo_location_sources)
|
||||
) {
|
||||
throw new Error("Geo_location_sources needs to be an array");
|
||||
}
|
||||
|
||||
this._config = config;
|
||||
this._configGeoLocationSources = config.geo_location_sources;
|
||||
this._configEntities = config.entities;
|
||||
}
|
||||
|
||||
getCardSize() {
|
||||
@ -205,7 +238,24 @@ class HuiMapCard extends PolymerElement {
|
||||
}
|
||||
const mapItems = (this._mapItems = []);
|
||||
|
||||
this._configEntities.forEach((entity) => {
|
||||
let allEntities = [];
|
||||
if (this._configEntities) {
|
||||
allEntities = allEntities.concat(this._configEntities);
|
||||
}
|
||||
if (this._configGeoLocationSources) {
|
||||
Object.keys(this.hass.states).forEach((entityId) => {
|
||||
const stateObj = this.hass.states[entityId];
|
||||
if (
|
||||
computeStateDomain(stateObj) === "geo_location" &&
|
||||
this._configGeoLocationSources.includes(stateObj.attributes.source)
|
||||
) {
|
||||
allEntities.push(entityId);
|
||||
}
|
||||
});
|
||||
}
|
||||
allEntities = processConfigEntities(allEntities);
|
||||
|
||||
allEntities.forEach((entity) => {
|
||||
const entityId = entity.entity;
|
||||
if (!(entityId in hass.states)) {
|
||||
return;
|
||||
|
||||
@ -4,16 +4,24 @@ import { classMap } from "lit-html/directives/classMap";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-markdown";
|
||||
|
||||
import { LovelaceCard } from "../types";
|
||||
import { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||
import { LovelaceCardConfig } from "../../../data/lovelace";
|
||||
import { TemplateResult } from "lit-html";
|
||||
|
||||
interface Config extends LovelaceCardConfig {
|
||||
export interface Config extends LovelaceCardConfig {
|
||||
content: string;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export class HuiMarkdownCard extends LitElement implements LovelaceCard {
|
||||
public static async getConfigElement(): Promise<LovelaceCardEditor> {
|
||||
await import(/* webpackChunkName: "hui-markdown-card-editor" */ "../editor/config-elements/hui-markdown-card-editor");
|
||||
return document.createElement("hui-markdown-card-editor");
|
||||
}
|
||||
public static getStubConfig(): object {
|
||||
return { content: " " };
|
||||
}
|
||||
|
||||
private _config?: Config;
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
|
||||
@ -2,7 +2,21 @@ import "../../../cards/ha-media_player-card";
|
||||
|
||||
import LegacyWrapperCard from "./hui-legacy-wrapper-card";
|
||||
|
||||
// should be interface when converted to TS
|
||||
export const Config = {
|
||||
entity: "",
|
||||
};
|
||||
|
||||
class HuiMediaControlCard extends LegacyWrapperCard {
|
||||
static async getConfigElement() {
|
||||
await import(/* webpackChunkName: "hui-media-control-card-editor" */ "../editor/config-elements/hui-media-control-card-editor");
|
||||
return document.createElement("hui-media-control-card-editor");
|
||||
}
|
||||
|
||||
static getStubConfig() {
|
||||
return {};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super("ha-media_player-card", "media_player");
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
|
||||
import "../../../components/ha-card";
|
||||
|
||||
import { LovelaceCard } from "../types";
|
||||
import { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||
import { LovelaceCardConfig, ActionConfig } from "../../../data/lovelace";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { TemplateResult } from "lit-html";
|
||||
@ -10,20 +10,31 @@ import { classMap } from "lit-html/directives/classMap";
|
||||
import { handleClick } from "../common/handle-click";
|
||||
import { longPress } from "../common/directives/long-press-directive";
|
||||
|
||||
interface Config extends LovelaceCardConfig {
|
||||
export interface Config extends LovelaceCardConfig {
|
||||
image?: string;
|
||||
tap_action?: ActionConfig;
|
||||
hold_action?: ActionConfig;
|
||||
}
|
||||
|
||||
export class HuiPictureCard extends LitElement implements LovelaceCard {
|
||||
public static async getConfigElement(): Promise<LovelaceCardEditor> {
|
||||
await import(/* webpackChunkName: "hui-picture-card-editor" */ "../editor/config-elements/hui-picture-card-editor");
|
||||
return document.createElement("hui-picture-card-editor");
|
||||
}
|
||||
public static getStubConfig(): object {
|
||||
return {
|
||||
image:
|
||||
"https://www.home-assistant.io/images/merchandise/shirt-frontpage.png",
|
||||
tap_action: { action: "none" },
|
||||
hold_action: { action: "none" },
|
||||
};
|
||||
}
|
||||
|
||||
public hass?: HomeAssistant;
|
||||
protected _config?: Config;
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return {
|
||||
_config: {},
|
||||
};
|
||||
return { _config: {} };
|
||||
}
|
||||
|
||||
public getCardSize(): number {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { html, LitElement } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
|
||||
import createHuiElement from "../common/create-hui-element";
|
||||
import { createHuiElement } from "../common/create-hui-element";
|
||||
|
||||
import { LovelaceCard } from "../types";
|
||||
import { LovelaceCardConfig } from "../../../data/lovelace";
|
||||
|
||||
@ -16,6 +16,10 @@ import { LovelaceCardConfig, ActionConfig } from "../../../data/lovelace";
|
||||
import { LovelaceCard } from "../types";
|
||||
import { handleClick } from "../common/handle-click";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
import {
|
||||
createErrorCardElement,
|
||||
createErrorCardConfig,
|
||||
} from "./hui-error-card";
|
||||
|
||||
interface Config extends LovelaceCardConfig {
|
||||
entity: string;
|
||||
@ -62,11 +66,25 @@ class HuiPictureEntityCard extends hassLocalizeLitMixin(LitElement)
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._config || !this.hass || !this.hass.states[this._config.entity]) {
|
||||
if (!this._config || !this.hass) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const stateObj = this.hass.states[this._config.entity];
|
||||
|
||||
if (!stateObj) {
|
||||
return html`
|
||||
${
|
||||
createErrorCardElement(
|
||||
createErrorCardConfig(
|
||||
`Entity not found: ${this._config.entity}`,
|
||||
this._config
|
||||
)
|
||||
)
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
const name = this._config.name || computeStateName(stateObj);
|
||||
const state = computeStateDisplay(
|
||||
this.localize,
|
||||
|
||||
@ -2,7 +2,22 @@ import "../../../cards/ha-plant-card";
|
||||
|
||||
import LegacyWrapperCard from "./hui-legacy-wrapper-card";
|
||||
|
||||
// should be interface when converted to TS
|
||||
export const Config = {
|
||||
name: "",
|
||||
entity: "",
|
||||
};
|
||||
|
||||
class HuiPlantStatusCard extends LegacyWrapperCard {
|
||||
static async getConfigElement() {
|
||||
await import(/* webpackChunkName: "hui-plant-status-card-editor" */ "../editor/config-elements/hui-plant-status-card-editor");
|
||||
return document.createElement("hui-plant-status-card-editor");
|
||||
}
|
||||
|
||||
static getStubConfig() {
|
||||
return {};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super("ha-plant-card", "plant");
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ import {
|
||||
import { TemplateResult } from "lit-html";
|
||||
import "@polymer/paper-spinner/paper-spinner";
|
||||
|
||||
import { LovelaceCard } from "../types";
|
||||
import { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||
import { LovelaceCardConfig } from "../../../data/lovelace";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
@ -133,7 +133,7 @@ const coordinates = (
|
||||
return calcPoints(history, hours, width, detail, min, max);
|
||||
};
|
||||
|
||||
interface Config extends LovelaceCardConfig {
|
||||
export interface Config extends LovelaceCardConfig {
|
||||
entity: string;
|
||||
name?: string;
|
||||
icon?: string;
|
||||
@ -145,6 +145,15 @@ interface Config extends LovelaceCardConfig {
|
||||
}
|
||||
|
||||
class HuiSensorCard extends LitElement implements LovelaceCard {
|
||||
public static async getConfigElement(): Promise<LovelaceCardEditor> {
|
||||
await import(/* webpackChunkName: "hui-sensor-card-editor" */ "../editor/config-elements/hui-sensor-card-editor");
|
||||
return document.createElement("hui-sensor-card-editor");
|
||||
}
|
||||
|
||||
public static getStubConfig(): object {
|
||||
return {};
|
||||
}
|
||||
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
private _history?: any;
|
||||
|
||||
@ -9,7 +9,7 @@ import "../../../components/ha-icon";
|
||||
|
||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceCard } from "../types";
|
||||
import { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||
import { LovelaceCardConfig } from "../../../data/lovelace";
|
||||
import {
|
||||
fetchItems,
|
||||
@ -19,12 +19,20 @@ import {
|
||||
addItem,
|
||||
} from "../../../data/shopping-list";
|
||||
|
||||
interface Config extends LovelaceCardConfig {
|
||||
export interface Config extends LovelaceCardConfig {
|
||||
title?: string;
|
||||
}
|
||||
|
||||
class HuiShoppingListCard extends hassLocalizeLitMixin(LitElement)
|
||||
implements LovelaceCard {
|
||||
public static async getConfigElement(): Promise<LovelaceCardEditor> {
|
||||
await import(/* webpackChunkName: "hui-shopping-list-editor" */ "../editor/config-elements/hui-shopping-list-editor");
|
||||
return document.createElement("hui-shopping-list-card-editor");
|
||||
}
|
||||
public static getStubConfig(): object {
|
||||
return {};
|
||||
}
|
||||
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
private _uncheckedItems?: ShoppingListItem[];
|
||||
@ -33,6 +41,7 @@ class HuiShoppingListCard extends hassLocalizeLitMixin(LitElement)
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
hass: {},
|
||||
_config: {},
|
||||
_uncheckedItems: {},
|
||||
_checkedItems: {},
|
||||
@ -117,7 +126,7 @@ class HuiShoppingListCard extends hassLocalizeLitMixin(LitElement)
|
||||
<paper-item-body>
|
||||
<paper-input
|
||||
no-label-float
|
||||
value="${item.name}"
|
||||
.value="${item.name}"
|
||||
.itemId="${item.id}"
|
||||
@change="${this._saveEdit}"
|
||||
></paper-input>
|
||||
@ -168,7 +177,7 @@ class HuiShoppingListCard extends hassLocalizeLitMixin(LitElement)
|
||||
<paper-item-body>
|
||||
<paper-input
|
||||
no-label-float
|
||||
value="${item.name}"
|
||||
.value="${item.name}"
|
||||
.itemId="${item.id}"
|
||||
@change="${this._saveEdit}"
|
||||
></paper-input>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { html, LitElement } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
|
||||
import createCardElement from "../common/create-card-element";
|
||||
import { createCardElement } from "../common/create-card-element";
|
||||
|
||||
import { LovelaceCard } from "../types";
|
||||
import { LovelaceCardConfig } from "../../../data/lovelace";
|
||||
|
||||
@ -7,22 +7,23 @@ import {
|
||||
import { classMap } from "lit-html/directives/classMap";
|
||||
import { TemplateResult } from "lit-html";
|
||||
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-icon";
|
||||
|
||||
import applyThemesOnElement from "../../../common/dom/apply_themes_on_element";
|
||||
import computeStateName from "../../../common/entity/compute_state_name";
|
||||
|
||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
||||
import { HomeAssistant, ClimateEntity } from "../../../types";
|
||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
||||
import { LovelaceCard } from "../types";
|
||||
import { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||
import { LovelaceCardConfig } from "../../../data/lovelace";
|
||||
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-icon";
|
||||
import { loadRoundslider } from "../../../resources/jquery.roundslider.ondemand";
|
||||
import { afterNextRender } from "../../../common/util/render-status";
|
||||
import { UNIT_F } from "../../../common/const";
|
||||
|
||||
const thermostatConfig = {
|
||||
radius: 150,
|
||||
step: 1,
|
||||
circleShape: "pie",
|
||||
startAngle: 315,
|
||||
width: 5,
|
||||
@ -44,7 +45,7 @@ const modeIcons = {
|
||||
idle: "hass:power-sleep",
|
||||
};
|
||||
|
||||
interface Config extends LovelaceCardConfig {
|
||||
export interface Config extends LovelaceCardConfig {
|
||||
entity: string;
|
||||
theme?: string;
|
||||
name?: string;
|
||||
@ -54,12 +55,30 @@ function formatTemp(temps: string[]): string {
|
||||
return temps.filter(Boolean).join("-");
|
||||
}
|
||||
|
||||
function computeTemperatureStepSize(hass: HomeAssistant, config: Config) {
|
||||
const stateObj = hass.states[config.entity];
|
||||
if (stateObj.attributes.target_temp_step) {
|
||||
return stateObj.attributes.target_temp_step;
|
||||
}
|
||||
return hass.config.unit_system.temperature === UNIT_F ? 1 : 0.5;
|
||||
}
|
||||
|
||||
export class HuiThermostatCard extends hassLocalizeLitMixin(LitElement)
|
||||
implements LovelaceCard {
|
||||
public static async getConfigElement(): Promise<LovelaceCardEditor> {
|
||||
await import(/* webpackChunkName: "hui-thermostat-card-editor" */ "../editor/config-elements/hui-thermostat-card-editor");
|
||||
return document.createElement("hui-thermostat-card-editor");
|
||||
}
|
||||
|
||||
public static getStubConfig(): object {
|
||||
return { entity: "" };
|
||||
}
|
||||
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
private _roundSliderStyle?: TemplateResult;
|
||||
private _jQuery?: any;
|
||||
private _broadCard?: boolean;
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return {
|
||||
@ -87,7 +106,6 @@ export class HuiThermostatCard extends hassLocalizeLitMixin(LitElement)
|
||||
return html``;
|
||||
}
|
||||
const stateObj = this.hass.states[this._config.entity] as ClimateEntity;
|
||||
const broadCard = this.clientWidth > 390;
|
||||
const mode = modeIcons[stateObj.attributes.operation_mode || ""]
|
||||
? stateObj.attributes.operation_mode!
|
||||
: "unknown-mode";
|
||||
@ -96,8 +114,8 @@ export class HuiThermostatCard extends hassLocalizeLitMixin(LitElement)
|
||||
<ha-card
|
||||
class="${classMap({
|
||||
[mode]: true,
|
||||
large: broadCard,
|
||||
small: !broadCard,
|
||||
large: this._broadCard!,
|
||||
small: !this._broadCard,
|
||||
})}">
|
||||
<div id="root">
|
||||
<div id="thermostat"></div>
|
||||
@ -138,8 +156,49 @@ export class HuiThermostatCard extends hassLocalizeLitMixin(LitElement)
|
||||
return hasConfigOrEntityChanged(this, changedProps);
|
||||
}
|
||||
|
||||
protected async firstUpdated(): Promise<void> {
|
||||
protected firstUpdated(): void {
|
||||
this._initialLoad();
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues): void {
|
||||
super.updated(changedProps);
|
||||
if (!this._config || !this.hass || !changedProps.has("hass")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||
|
||||
if (!oldHass || oldHass.themes !== this.hass.themes) {
|
||||
applyThemesOnElement(this, this.hass.themes, this._config.theme);
|
||||
}
|
||||
|
||||
const stateObj = this.hass.states[this._config.entity] as ClimateEntity;
|
||||
|
||||
if (
|
||||
this._jQuery &&
|
||||
// If jQuery changed, we just rendered in firstUpdated
|
||||
!changedProps.has("_jQuery") &&
|
||||
(!oldHass || oldHass.states[this._config.entity] !== stateObj)
|
||||
) {
|
||||
const [sliderValue, uiValue] = this._genSliderValue(stateObj);
|
||||
|
||||
this._jQuery("#thermostat", this.shadowRoot).roundSlider({
|
||||
value: sliderValue,
|
||||
});
|
||||
this._updateSetTemp(uiValue);
|
||||
}
|
||||
}
|
||||
|
||||
private async _initialLoad(): Promise<void> {
|
||||
const radius = this.clientWidth / 3;
|
||||
this._broadCard = this.clientWidth > 390;
|
||||
|
||||
(this.shadowRoot!.querySelector(
|
||||
"#thermostat"
|
||||
)! as HTMLElement).style.minHeight = radius * 2 + "px";
|
||||
|
||||
const loaded = await loadRoundslider();
|
||||
await new Promise((resolve) => afterNextRender(resolve));
|
||||
|
||||
this._roundSliderStyle = loaded.roundSliderStyle;
|
||||
this._jQuery = loaded.jQuery;
|
||||
@ -152,26 +211,27 @@ export class HuiThermostatCard extends hassLocalizeLitMixin(LitElement)
|
||||
? "range"
|
||||
: "min-range";
|
||||
|
||||
const [sliderValue, uiValue] = this._genSliderValue(stateObj);
|
||||
const step = computeTemperatureStepSize(this.hass!, this._config!);
|
||||
|
||||
this._jQuery("#thermostat", this.shadowRoot).roundSlider({
|
||||
...thermostatConfig,
|
||||
radius: this.clientWidth / 3,
|
||||
radius,
|
||||
min: stateObj.attributes.min_temp,
|
||||
max: stateObj.attributes.max_temp,
|
||||
sliderType: _sliderType,
|
||||
create: () => this._loaded(),
|
||||
change: (value) => this._setTemperature(value),
|
||||
drag: (value) => this._dragEvent(value),
|
||||
value: sliderValue,
|
||||
step,
|
||||
});
|
||||
this._updateSetTemp(uiValue);
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues): void {
|
||||
if (!this._config || !this.hass || !this._jQuery) {
|
||||
return;
|
||||
}
|
||||
|
||||
const stateObj = this.hass.states[this._config.entity] as ClimateEntity;
|
||||
|
||||
let sliderValue;
|
||||
let uiValue;
|
||||
private _genSliderValue(stateObj: ClimateEntity): [string | number, string] {
|
||||
let sliderValue: string | number;
|
||||
let uiValue: string;
|
||||
|
||||
if (
|
||||
stateObj.attributes.target_temp_low &&
|
||||
@ -185,18 +245,73 @@ export class HuiThermostatCard extends hassLocalizeLitMixin(LitElement)
|
||||
String(stateObj.attributes.target_temp_high),
|
||||
]);
|
||||
} else {
|
||||
sliderValue = uiValue = stateObj.attributes.temperature;
|
||||
sliderValue = stateObj.attributes.temperature;
|
||||
uiValue = "" + stateObj.attributes.temperature;
|
||||
}
|
||||
|
||||
this._jQuery("#thermostat", this.shadowRoot).roundSlider({
|
||||
value: sliderValue,
|
||||
return [sliderValue, uiValue];
|
||||
}
|
||||
|
||||
private _loaded(): void {
|
||||
(this.shadowRoot!.querySelector(
|
||||
"#thermostat"
|
||||
)! as HTMLElement).style.minHeight = null;
|
||||
}
|
||||
|
||||
private _updateSetTemp(value: string): void {
|
||||
this.shadowRoot!.querySelector("#set-temperature")!.innerHTML = value;
|
||||
}
|
||||
|
||||
private _dragEvent(e): void {
|
||||
this._updateSetTemp(formatTemp(String(e.value).split(",")));
|
||||
}
|
||||
|
||||
private _setTemperature(e): void {
|
||||
const stateObj = this.hass!.states[this._config!.entity] as ClimateEntity;
|
||||
if (
|
||||
stateObj.attributes.target_temp_low &&
|
||||
stateObj.attributes.target_temp_high
|
||||
) {
|
||||
if (e.handle.index === 1) {
|
||||
this.hass!.callService("climate", "set_temperature", {
|
||||
entity_id: this._config!.entity,
|
||||
target_temp_low: e.handle.value,
|
||||
target_temp_high: stateObj.attributes.target_temp_high,
|
||||
});
|
||||
} else {
|
||||
this.hass!.callService("climate", "set_temperature", {
|
||||
entity_id: this._config!.entity,
|
||||
target_temp_low: stateObj.attributes.target_temp_low,
|
||||
target_temp_high: e.handle.value,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.hass!.callService("climate", "set_temperature", {
|
||||
entity_id: this._config!.entity,
|
||||
temperature: e.value,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _renderIcon(mode: string, currentMode: string): TemplateResult {
|
||||
if (!modeIcons[mode]) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<ha-icon
|
||||
class="${classMap({ "selected-icon": currentMode === mode })}"
|
||||
.mode="${mode}"
|
||||
.icon="${modeIcons[mode]}"
|
||||
@click="${this._handleModeClick}"
|
||||
></ha-icon>
|
||||
`;
|
||||
}
|
||||
|
||||
private _handleModeClick(e: MouseEvent): void {
|
||||
this.hass!.callService("climate", "set_operation_mode", {
|
||||
entity_id: this._config!.entity,
|
||||
operation_mode: (e.currentTarget as any).mode,
|
||||
});
|
||||
this.shadowRoot!.querySelector("#set-temperature")!.innerHTML = uiValue;
|
||||
|
||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||
if (!oldHass || oldHass.themes !== this.hass.themes) {
|
||||
applyThemesOnElement(this, this.hass.themes, this._config.theme);
|
||||
}
|
||||
}
|
||||
|
||||
private renderStyle(): TemplateResult {
|
||||
@ -374,60 +489,6 @@ export class HuiThermostatCard extends hassLocalizeLitMixin(LitElement)
|
||||
</style>
|
||||
`;
|
||||
}
|
||||
|
||||
private _dragEvent(e): void {
|
||||
this.shadowRoot!.querySelector("#set-temperature")!.innerHTML = formatTemp(
|
||||
String(e.value).split(",")
|
||||
);
|
||||
}
|
||||
|
||||
private _setTemperature(e): void {
|
||||
const stateObj = this.hass!.states[this._config!.entity] as ClimateEntity;
|
||||
if (
|
||||
stateObj.attributes.target_temp_low &&
|
||||
stateObj.attributes.target_temp_high
|
||||
) {
|
||||
if (e.handle.index === 1) {
|
||||
this.hass!.callService("climate", "set_temperature", {
|
||||
entity_id: this._config!.entity,
|
||||
target_temp_low: e.handle.value,
|
||||
target_temp_high: stateObj.attributes.target_temp_high,
|
||||
});
|
||||
} else {
|
||||
this.hass!.callService("climate", "set_temperature", {
|
||||
entity_id: this._config!.entity,
|
||||
target_temp_low: stateObj.attributes.target_temp_low,
|
||||
target_temp_high: e.handle.value,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.hass!.callService("climate", "set_temperature", {
|
||||
entity_id: this._config!.entity,
|
||||
temperature: e.value,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _renderIcon(mode: string, currentMode: string): TemplateResult {
|
||||
if (!modeIcons[mode]) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<ha-icon
|
||||
class="${classMap({ "selected-icon": currentMode === mode })}"
|
||||
.mode="${mode}"
|
||||
.icon="${modeIcons[mode]}"
|
||||
@click="${this._handleModeClick}"
|
||||
></ha-icon>
|
||||
`;
|
||||
}
|
||||
|
||||
private _handleModeClick(e: MouseEvent): void {
|
||||
this.hass!.callService("climate", "set_operation_mode", {
|
||||
entity_id: this._config!.entity,
|
||||
operation_mode: (e.currentTarget as any).mode,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@ -2,7 +2,22 @@ import "../../../cards/ha-weather-card";
|
||||
|
||||
import LegacyWrapperCard from "./hui-legacy-wrapper-card";
|
||||
|
||||
// should be interface when converted to TS
|
||||
export const Config = {
|
||||
entity: "",
|
||||
name: "",
|
||||
};
|
||||
|
||||
class HuiWeatherForecastCard extends LegacyWrapperCard {
|
||||
static async getConfigElement() {
|
||||
await import(/* webpackChunkName: "hui-weather-forecast-card-editor" */ "../editor/config-elements/hui-weather-forecast-card-editor");
|
||||
return document.createElement("hui-weather-forecast-card-editor");
|
||||
}
|
||||
|
||||
static getStubConfig() {
|
||||
return {};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super("ha-weather-card", "weather");
|
||||
}
|
||||
|
||||
@ -1,38 +0,0 @@
|
||||
const EXCLUDED_DOMAINS = ["zone"];
|
||||
|
||||
function computeUsedEntities(config) {
|
||||
const entities = new Set();
|
||||
|
||||
function addEntityId(entity) {
|
||||
if (typeof entity === "string") {
|
||||
entities.add(entity);
|
||||
} else if (entity.entity) {
|
||||
entities.add(entity.entity);
|
||||
}
|
||||
}
|
||||
|
||||
function addEntities(obj) {
|
||||
if (obj.entity) addEntityId(obj.entity);
|
||||
if (obj.entities) obj.entities.forEach((entity) => addEntityId(entity));
|
||||
if (obj.card) addEntities(obj.card);
|
||||
if (obj.cards) obj.cards.forEach((card) => addEntities(card));
|
||||
if (obj.badges) obj.badges.forEach((badge) => addEntityId(badge));
|
||||
}
|
||||
|
||||
config.views.forEach((view) => addEntities(view));
|
||||
return entities;
|
||||
}
|
||||
|
||||
export default function computeUnusedEntities(hass, config) {
|
||||
const usedEntities = computeUsedEntities(config);
|
||||
return Object.keys(hass.states)
|
||||
.filter(
|
||||
(entity) =>
|
||||
!usedEntities.has(entity) &&
|
||||
!(
|
||||
config.excluded_entities && config.excluded_entities.includes(entity)
|
||||
) &&
|
||||
!EXCLUDED_DOMAINS.includes(entity.split(".", 1)[0])
|
||||
)
|
||||
.sort();
|
||||
}
|
||||
54
src/panels/lovelace/common/compute-unused-entities.ts
Normal file
54
src/panels/lovelace/common/compute-unused-entities.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { LovelaceConfig } from "../../../data/lovelace";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
|
||||
const EXCLUDED_DOMAINS = ["zone"];
|
||||
|
||||
const computeUsedEntities = (config) => {
|
||||
const entities = new Set();
|
||||
|
||||
const addEntityId = (entity) => {
|
||||
if (typeof entity === "string") {
|
||||
entities.add(entity);
|
||||
} else if (entity.entity) {
|
||||
entities.add(entity.entity);
|
||||
}
|
||||
};
|
||||
|
||||
const addEntities = (obj) => {
|
||||
if (obj.entity) {
|
||||
addEntityId(obj.entity);
|
||||
}
|
||||
if (obj.entities) {
|
||||
obj.entities.forEach((entity) => addEntityId(entity));
|
||||
}
|
||||
if (obj.card) {
|
||||
addEntities(obj.card);
|
||||
}
|
||||
if (obj.cards) {
|
||||
obj.cards.forEach((card) => addEntities(card));
|
||||
}
|
||||
if (obj.badges) {
|
||||
obj.badges.forEach((badge) => addEntityId(badge));
|
||||
}
|
||||
};
|
||||
|
||||
config.views.forEach((view) => addEntities(view));
|
||||
return entities;
|
||||
};
|
||||
|
||||
export const computeUnusedEntities = (
|
||||
hass: HomeAssistant,
|
||||
config: LovelaceConfig
|
||||
): string[] => {
|
||||
const usedEntities = computeUsedEntities(config);
|
||||
return Object.keys(hass.states)
|
||||
.filter(
|
||||
(entity) =>
|
||||
!usedEntities.has(entity) &&
|
||||
!(
|
||||
config.excluded_entities && config.excluded_entities.includes(entity)
|
||||
) &&
|
||||
!EXCLUDED_DOMAINS.includes(entity.split(".", 1)[0])
|
||||
)
|
||||
.sort();
|
||||
};
|
||||
@ -5,7 +5,11 @@ import "../cards/hui-conditional-card";
|
||||
import "../cards/hui-entities-card";
|
||||
import "../cards/hui-entity-button-card";
|
||||
import "../cards/hui-entity-filter-card";
|
||||
import "../cards/hui-error-card";
|
||||
import {
|
||||
createErrorCardElement,
|
||||
createErrorCardConfig,
|
||||
HuiErrorCard,
|
||||
} from "../cards/hui-error-card";
|
||||
import "../cards/hui-glance-card";
|
||||
import "../cards/hui-history-graph-card";
|
||||
import "../cards/hui-horizontal-stack-card";
|
||||
@ -25,8 +29,8 @@ import "../cards/hui-shopping-list-card";
|
||||
import "../cards/hui-thermostat-card";
|
||||
import "../cards/hui-weather-forecast-card";
|
||||
import "../cards/hui-gauge-card";
|
||||
|
||||
import createErrorCardConfig from "./create-error-card-config";
|
||||
import { LovelaceCard } from "../types";
|
||||
import { LovelaceCardConfig } from "../../../data/lovelace";
|
||||
|
||||
const CARD_TYPES = new Set([
|
||||
"alarm-panel",
|
||||
@ -58,24 +62,29 @@ const CARD_TYPES = new Set([
|
||||
const CUSTOM_TYPE_PREFIX = "custom:";
|
||||
const TIMEOUT = 2000;
|
||||
|
||||
function _createElement(tag, config) {
|
||||
const element = document.createElement(tag);
|
||||
const _createElement = (
|
||||
tag: string,
|
||||
config: LovelaceCardConfig
|
||||
): LovelaceCard | HuiErrorCard => {
|
||||
const element = document.createElement(tag) as LovelaceCard;
|
||||
try {
|
||||
element.setConfig(config);
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line
|
||||
// tslint:disable-next-line
|
||||
console.error(tag, err);
|
||||
// eslint-disable-next-line
|
||||
return _createErrorElement(err.message, config);
|
||||
}
|
||||
return element;
|
||||
}
|
||||
};
|
||||
|
||||
function _createErrorElement(error, config) {
|
||||
return _createElement("hui-error-card", createErrorCardConfig(error, config));
|
||||
}
|
||||
const _createErrorElement = (
|
||||
error: string,
|
||||
config: LovelaceCardConfig
|
||||
): HuiErrorCard => createErrorCardElement(createErrorCardConfig(error, config));
|
||||
|
||||
export default function createCardElement(config) {
|
||||
export const createCardElement = (
|
||||
config: LovelaceCardConfig
|
||||
): LovelaceCard | HuiErrorCard => {
|
||||
if (!config || typeof config !== "object" || !config.type) {
|
||||
return _createErrorElement("No card type configured.", config);
|
||||
}
|
||||
@ -111,4 +120,4 @@ export default function createCardElement(config) {
|
||||
}
|
||||
|
||||
return _createElement(`hui-${config.type}-card`, config);
|
||||
}
|
||||
};
|
||||
@ -1,7 +0,0 @@
|
||||
export default function createErrorConfig(error, origConfig) {
|
||||
return {
|
||||
type: "error",
|
||||
error,
|
||||
origConfig,
|
||||
};
|
||||
}
|
||||
@ -6,7 +6,12 @@ import "../elements/hui-state-icon-element";
|
||||
import "../elements/hui-state-label-element";
|
||||
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import createErrorCardConfig from "./create-error-card-config";
|
||||
import {
|
||||
createErrorCardElement,
|
||||
createErrorCardConfig,
|
||||
HuiErrorCard,
|
||||
} from "../cards/hui-error-card";
|
||||
import { LovelaceElementConfig, LovelaceElement } from "../elements/types";
|
||||
|
||||
const CUSTOM_TYPE_PREFIX = "custom:";
|
||||
const ELEMENT_TYPES = new Set([
|
||||
@ -19,22 +24,25 @@ const ELEMENT_TYPES = new Set([
|
||||
]);
|
||||
const TIMEOUT = 2000;
|
||||
|
||||
function _createElement(tag, config) {
|
||||
const element = document.createElement(tag);
|
||||
const _createElement = (
|
||||
tag: string,
|
||||
config: LovelaceElementConfig
|
||||
): LovelaceElement | HuiErrorCard => {
|
||||
const element = document.createElement(tag) as LovelaceElement;
|
||||
try {
|
||||
element.setConfig(config);
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line
|
||||
// tslint:disable-next-line
|
||||
console.error(tag, err);
|
||||
// eslint-disable-next-line
|
||||
return _createErrorElement(err.message, config);
|
||||
}
|
||||
return element;
|
||||
}
|
||||
};
|
||||
|
||||
function _createErrorElement(error, config) {
|
||||
return _createElement("hui-error-card", createErrorCardConfig(error, config));
|
||||
}
|
||||
const _createErrorElement = (
|
||||
error: string,
|
||||
config: LovelaceElementConfig
|
||||
): HuiErrorCard => createErrorCardElement(createErrorCardConfig(error, config));
|
||||
|
||||
function _hideErrorElement(element) {
|
||||
element.style.display = "None";
|
||||
@ -43,7 +51,9 @@ function _hideErrorElement(element) {
|
||||
}, TIMEOUT);
|
||||
}
|
||||
|
||||
export default function createHuiElement(config) {
|
||||
export const createHuiElement = (
|
||||
config: LovelaceElementConfig
|
||||
): LovelaceElement | HuiErrorCard => {
|
||||
if (!config || typeof config !== "object" || !config.type) {
|
||||
return _createErrorElement("No element type configured.", config);
|
||||
}
|
||||
@ -76,4 +86,4 @@ export default function createHuiElement(config) {
|
||||
}
|
||||
|
||||
return _createElement(`hui-${config.type}-element`, config);
|
||||
}
|
||||
};
|
||||
@ -1,5 +1,10 @@
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
|
||||
import {
|
||||
createErrorCardElement,
|
||||
createErrorCardConfig,
|
||||
HuiErrorCard,
|
||||
} from "../cards/hui-error-card";
|
||||
import "../entity-rows/hui-climate-entity-row";
|
||||
import "../entity-rows/hui-cover-entity-row";
|
||||
import "../entity-rows/hui-group-entity-row";
|
||||
@ -18,8 +23,7 @@ import "../special-rows/hui-call-service-row";
|
||||
import "../special-rows/hui-divider-row";
|
||||
import "../special-rows/hui-section-row";
|
||||
import "../special-rows/hui-weblink-row";
|
||||
|
||||
import createErrorCardConfig from "./create-error-card-config";
|
||||
import { EntityConfig, EntityRow } from "../entity-rows/types";
|
||||
|
||||
const CUSTOM_TYPE_PREFIX = "custom:";
|
||||
const SPECIAL_TYPES = new Set([
|
||||
@ -51,32 +55,37 @@ const DOMAIN_TO_ELEMENT_TYPE = {
|
||||
};
|
||||
const TIMEOUT = 2000;
|
||||
|
||||
function _createElement(tag, config) {
|
||||
const element = document.createElement(tag);
|
||||
const _createElement = (
|
||||
tag: string,
|
||||
config: EntityConfig
|
||||
): EntityRow | HuiErrorCard => {
|
||||
const element = document.createElement(tag) as EntityRow;
|
||||
try {
|
||||
if ("setConfig" in element) element.setConfig(config);
|
||||
element.setConfig(config);
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line
|
||||
// tslint:disable-next-line
|
||||
console.error(tag, err);
|
||||
// eslint-disable-next-line
|
||||
return _createErrorElement(err.message, config);
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
};
|
||||
|
||||
function _createErrorElement(error, config) {
|
||||
return _createElement("hui-error-card", createErrorCardConfig(error, config));
|
||||
}
|
||||
const _createErrorElement = (
|
||||
error: string,
|
||||
config: EntityConfig
|
||||
): HuiErrorCard => createErrorCardElement(createErrorCardConfig(error, config));
|
||||
|
||||
function _hideErrorElement(element) {
|
||||
const _hideErrorElement = (element) => {
|
||||
element.style.display = "None";
|
||||
return window.setTimeout(() => {
|
||||
element.style.display = "";
|
||||
}, TIMEOUT);
|
||||
}
|
||||
};
|
||||
|
||||
export default function createRowElement(config) {
|
||||
export const createRowElement = (
|
||||
config: EntityConfig
|
||||
): EntityRow | HuiErrorCard => {
|
||||
let tag;
|
||||
|
||||
if (
|
||||
@ -116,4 +125,4 @@ export default function createRowElement(config) {
|
||||
tag = `hui-${DOMAIN_TO_ELEMENT_TYPE[domain] || "text"}-entity-row`;
|
||||
|
||||
return _createElement(tag, config);
|
||||
}
|
||||
};
|
||||
@ -42,6 +42,11 @@ const computeCards = (
|
||||
type: "alarm-panel",
|
||||
entity: entityId,
|
||||
});
|
||||
} else if (domain === "camera") {
|
||||
cards.push({
|
||||
type: "picture-entity",
|
||||
entity: entityId,
|
||||
});
|
||||
} else if (domain === "climate") {
|
||||
cards.push({
|
||||
type: "thermostat",
|
||||
|
||||
132
src/panels/lovelace/components/hui-action-editor.ts
Normal file
132
src/panels/lovelace/components/hui-action-editor.ts
Normal file
@ -0,0 +1,132 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import "@polymer/paper-input/paper-textarea";
|
||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
|
||||
import "../../../components/ha-service-picker";
|
||||
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { fireEvent, HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { EditorTarget } from "../editor/types";
|
||||
import {
|
||||
ActionConfig,
|
||||
NavigateActionConfig,
|
||||
CallServiceActionConfig,
|
||||
} from "../../../data/lovelace";
|
||||
|
||||
declare global {
|
||||
// for fire event
|
||||
interface HASSDomEvents {
|
||||
"action-changed": undefined;
|
||||
}
|
||||
// for add event listener
|
||||
interface HTMLElementEventMap {
|
||||
"action-changed": HASSDomEvent<undefined>;
|
||||
}
|
||||
}
|
||||
|
||||
export class HuiActionEditor extends LitElement {
|
||||
public config?: ActionConfig;
|
||||
public label?: string;
|
||||
public actions?: string[];
|
||||
protected hass?: HomeAssistant;
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return { hass: {}, config: {}, label: {}, actions: {} };
|
||||
}
|
||||
|
||||
get _action(): string {
|
||||
return this.config!.action || "";
|
||||
}
|
||||
|
||||
get _navigation_path(): string {
|
||||
const config = this.config! as NavigateActionConfig;
|
||||
return config.navigation_path || "";
|
||||
}
|
||||
|
||||
get _service(): string {
|
||||
const config = this.config! as CallServiceActionConfig;
|
||||
return config.service || "";
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass || !this.actions) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<paper-dropdown-menu
|
||||
.label="${this.label}"
|
||||
.configValue="${"action"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
>
|
||||
<paper-listbox
|
||||
slot="dropdown-content"
|
||||
.selected="${this.actions.indexOf(this._action)}"
|
||||
>
|
||||
${
|
||||
this.actions.map((action) => {
|
||||
return html`
|
||||
<paper-item>${action}</paper-item>
|
||||
`;
|
||||
})
|
||||
}
|
||||
</paper-listbox>
|
||||
</paper-dropdown-menu>
|
||||
${
|
||||
this._action === "navigate"
|
||||
? html`
|
||||
<paper-input
|
||||
label="Navigation Path"
|
||||
.value="${this._navigation_path}"
|
||||
.configValue="${"navigation_path"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
${
|
||||
this.config && this.config.action === "call-service"
|
||||
? html`
|
||||
<ha-service-picker
|
||||
.hass="${this.hass}"
|
||||
.value="${this._service}"
|
||||
.configValue="${"service"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></ha-service-picker>
|
||||
<h3>Toggle Editor to input Service Data</h3>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: Event): void {
|
||||
if (!this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
if (
|
||||
this.config &&
|
||||
this.config[this[`${target.configValue}`]] === target.value
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (target.configValue === "action") {
|
||||
this.config = { action: "none" };
|
||||
}
|
||||
if (target.configValue) {
|
||||
this.config = { ...this.config!, [target.configValue!]: target.value };
|
||||
fireEvent(this, "action-changed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-action-editor": HuiActionEditor;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-action-editor", HuiActionEditor);
|
||||
@ -1,20 +1,23 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import "@polymer/paper-button/paper-button";
|
||||
import "@polymer/paper-menu-button/paper-menu-button";
|
||||
import "@polymer/paper-icon-button/paper-icon-button";
|
||||
import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
|
||||
import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog";
|
||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
||||
import { confDeleteCard } from "../editor/delete-card";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceCardConfig } from "../../../data/lovelace";
|
||||
import { Lovelace } from "../types";
|
||||
import { swapCard } from "../editor/config-util";
|
||||
import { showMoveCardViewDialog } from "../editor/card-editor/show-move-card-view-dialog";
|
||||
|
||||
export class HuiCardOptions extends hassLocalizeLitMixin(LitElement) {
|
||||
public cardConfig?: LovelaceCardConfig;
|
||||
protected hass?: HomeAssistant;
|
||||
protected lovelace?: Lovelace;
|
||||
protected path?: [number, number];
|
||||
public hass?: HomeAssistant;
|
||||
public lovelace?: Lovelace;
|
||||
public path?: [number, number];
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return { hass: {}, lovelace: {}, path: {} };
|
||||
@ -23,49 +26,93 @@ export class HuiCardOptions extends hassLocalizeLitMixin(LitElement) {
|
||||
protected render() {
|
||||
return html`
|
||||
<style>
|
||||
div {
|
||||
div.options {
|
||||
border-top: 1px solid #e8e8e8;
|
||||
padding: 5px 16px;
|
||||
padding: 5px 8px;
|
||||
background: var(--paper-card-background-color, white);
|
||||
box-shadow: rgba(0, 0, 0, 0.14) 0px 2px 2px 0px,
|
||||
rgba(0, 0, 0, 0.12) 0px 1px 5px 0px,
|
||||
rgba(0, 0, 0, 0.12) 0px 1px 5px -4px,
|
||||
rgba(0, 0, 0, 0.2) 0px 3px 1px -2px;
|
||||
display: flex;
|
||||
}
|
||||
div.options .primary-actions {
|
||||
flex: 1;
|
||||
margin: auto;
|
||||
}
|
||||
div.options .secondary-actions {
|
||||
flex: 4;
|
||||
text-align: right;
|
||||
}
|
||||
paper-button {
|
||||
color: var(--primary-color);
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.05em;
|
||||
font-size: 16px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
paper-icon-button.delete {
|
||||
paper-icon-button {
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
paper-icon-button.move-arrow[disabled] {
|
||||
color: var(--disabled-text-color);
|
||||
}
|
||||
paper-menu-button {
|
||||
color: var(--secondary-text-color);
|
||||
float: right;
|
||||
padding: 0;
|
||||
}
|
||||
paper-item.header {
|
||||
color: var(--primary-text-color);
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
paper-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
<slot></slot>
|
||||
<div>
|
||||
<paper-button @click="${this._editCard}"
|
||||
>${
|
||||
this.localize("ui.panel.lovelace.editor.edit_card.edit")
|
||||
}</paper-button
|
||||
>
|
||||
<paper-icon-button
|
||||
icon="hass:arrow-up"
|
||||
@click="${this._cardUp}"
|
||||
?disabled="${this.path![1] === 0}"
|
||||
></paper-icon-button>
|
||||
<paper-icon-button
|
||||
icon="hass:arrow-down"
|
||||
@click="${this._cardDown}"
|
||||
?disabled="${
|
||||
this.lovelace!.config.views[this.path![0]].cards!.length ===
|
||||
this.path![1] + 1
|
||||
}"
|
||||
></paper-icon-button>
|
||||
<paper-icon-button
|
||||
class="delete"
|
||||
icon="hass:delete"
|
||||
@click="${this._deleteCard}"
|
||||
title="${this.localize("ui.panel.lovelace.editor.edit_card.delete")}"
|
||||
></paper-icon-button>
|
||||
<div class="options">
|
||||
<div class="primary-actions">
|
||||
<paper-button @click="${this._editCard}"
|
||||
>${
|
||||
this.localize("ui.panel.lovelace.editor.edit_card.edit")
|
||||
}</paper-button
|
||||
>
|
||||
</div>
|
||||
<div class="secondary-actions">
|
||||
<paper-icon-button
|
||||
title="Move card down"
|
||||
class="move-arrow"
|
||||
icon="hass:arrow-down"
|
||||
@click="${this._cardDown}"
|
||||
?disabled="${
|
||||
this.lovelace!.config.views[this.path![0]].cards!.length ===
|
||||
this.path![1] + 1
|
||||
}"
|
||||
></paper-icon-button>
|
||||
<paper-icon-button
|
||||
title="Move card up"
|
||||
class="move-arrow"
|
||||
icon="hass:arrow-up"
|
||||
@click="${this._cardUp}"
|
||||
?disabled="${this.path![1] === 0}"
|
||||
></paper-icon-button>
|
||||
<paper-menu-button>
|
||||
<paper-icon-button
|
||||
icon="hass:dots-vertical"
|
||||
slot="dropdown-trigger"
|
||||
></paper-icon-button>
|
||||
<paper-listbox slot="dropdown-content">
|
||||
<paper-item @click="${this._moveCard}">Move Card</paper-item>
|
||||
<paper-item @click="${this._deleteCard}"
|
||||
>${
|
||||
this.localize("ui.panel.lovelace.editor.edit_card.delete")
|
||||
}</paper-item
|
||||
>
|
||||
</paper-listbox>
|
||||
</paper-menu-button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@ -93,6 +140,13 @@ export class HuiCardOptions extends hassLocalizeLitMixin(LitElement) {
|
||||
);
|
||||
}
|
||||
|
||||
private _moveCard(): void {
|
||||
showMoveCardViewDialog(this, {
|
||||
path: this.path!,
|
||||
lovelace: this.lovelace!,
|
||||
});
|
||||
}
|
||||
|
||||
private _deleteCard(): void {
|
||||
confDeleteCard(this.lovelace!, this.path!);
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ declare global {
|
||||
|
||||
export class HuiThemeSelectionEditor extends hassLocalizeLitMixin(LitElement) {
|
||||
public value?: string;
|
||||
protected hass?: HomeAssistant;
|
||||
public hass?: HomeAssistant;
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return {
|
||||
|
||||
@ -46,7 +46,7 @@ export class HuiNotificationsButton extends EventsMixin(PolymerElement) {
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
notificationsOpen: {
|
||||
open: {
|
||||
type: Boolean,
|
||||
notify: true,
|
||||
},
|
||||
@ -58,7 +58,7 @@ export class HuiNotificationsButton extends EventsMixin(PolymerElement) {
|
||||
}
|
||||
|
||||
_clicked() {
|
||||
this.notificationsOpen = true;
|
||||
this.open = true;
|
||||
}
|
||||
|
||||
_hasNotifications(notifications) {
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import "@polymer/paper-input/paper-textarea";
|
||||
|
||||
import createCardElement from "../../common/create-card-element";
|
||||
import createErrorCardConfig from "../../common/create-error-card-config";
|
||||
import { createCardElement } from "../../common/create-card-element";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceCardConfig } from "../../../../data/lovelace";
|
||||
import { LovelaceCard } from "../../types";
|
||||
import { ConfigError } from "../types";
|
||||
import { getCardElementTag } from "../../common/get-card-element-tag";
|
||||
import { createErrorCardConfig } from "../../cards/hui-error-card";
|
||||
|
||||
export class HuiCardPreview extends HTMLElement {
|
||||
private _hass?: HomeAssistant;
|
||||
|
||||
@ -0,0 +1,106 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import "@polymer/paper-dialog/paper-dialog";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
// tslint:disable-next-line:no-duplicate-imports
|
||||
import { PaperDialogElement } from "@polymer/paper-dialog/paper-dialog";
|
||||
|
||||
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
|
||||
import { moveCard } from "../config-util";
|
||||
import { MoveCardViewDialogParams } from "./show-move-card-view-dialog";
|
||||
|
||||
export class HuiDialogMoveCardView extends hassLocalizeLitMixin(LitElement) {
|
||||
private _params?: MoveCardViewDialogParams;
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return {
|
||||
_params: {},
|
||||
};
|
||||
}
|
||||
|
||||
public async showDialog(params: MoveCardViewDialogParams): Promise<void> {
|
||||
this._params = params;
|
||||
await this.updateComplete;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._params) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<style>
|
||||
paper-item {
|
||||
margin: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
paper-item[active] {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
paper-item[active]:before {
|
||||
border-radius: 4px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
content: "";
|
||||
background-color: var(--primary-color);
|
||||
opacity: 0.12;
|
||||
transition: opacity 15ms linear;
|
||||
will-change: opacity;
|
||||
}
|
||||
</style>
|
||||
<paper-dialog
|
||||
with-backdrop
|
||||
opened
|
||||
@opened-changed="${this._openedChanged}"
|
||||
>
|
||||
<h2>Choose view to move card</h2>
|
||||
${
|
||||
this._params!.lovelace!.config.views.map((view, index) => {
|
||||
return html`
|
||||
<paper-item
|
||||
?active="${this._params!.path![0] === index}"
|
||||
@click="${this._moveCard}"
|
||||
.index="${index}"
|
||||
>${view.title}</paper-item
|
||||
>
|
||||
`;
|
||||
})
|
||||
}
|
||||
</paper-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private get _dialog(): PaperDialogElement {
|
||||
return this.shadowRoot!.querySelector("paper-dialog")!;
|
||||
}
|
||||
|
||||
private _moveCard(e: Event): void {
|
||||
const newView = (e.currentTarget! as any).index;
|
||||
const path = this._params!.path!;
|
||||
if (newView === path[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const lovelace = this._params!.lovelace!;
|
||||
|
||||
lovelace.saveConfig(moveCard(lovelace.config, path, [newView!]));
|
||||
this._dialog.close();
|
||||
}
|
||||
|
||||
private _openedChanged(ev: MouseEvent) {
|
||||
if (!(ev.detail as any).value) {
|
||||
this._params = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-dialog-move-card-view": HuiDialogMoveCardView;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-dialog-move-card-view", HuiDialogMoveCardView);
|
||||
@ -160,6 +160,7 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
? html`
|
||||
<div class="paper-dialog-buttons">
|
||||
<paper-button
|
||||
class="toggle-button"
|
||||
?hidden="${!this._configValue || !this._configValue.value}"
|
||||
?disabled="${
|
||||
this._configElement === null || this._configState !== "OK"
|
||||
@ -236,6 +237,9 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||
margin-bottom: 4px;
|
||||
display: block;
|
||||
}
|
||||
.toggle-button {
|
||||
margin-right: auto;
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -21,7 +21,8 @@ const registerEditCardDialog = (element: HTMLElement) =>
|
||||
fireEvent(element, "register-dialog", {
|
||||
dialogShowEvent,
|
||||
dialogTag,
|
||||
dialogImport: () => import("./hui-dialog-edit-card"),
|
||||
dialogImport: () =>
|
||||
import(/* webpackChunkName: "hui-dialog-edit-card" */ "./hui-dialog-edit-card"),
|
||||
});
|
||||
|
||||
export const showEditCardDialog = (
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { Lovelace } from "../../types";
|
||||
|
||||
declare global {
|
||||
// for fire event
|
||||
interface HASSDomEvents {
|
||||
"show-move-card-view": MoveCardViewDialogParams;
|
||||
}
|
||||
}
|
||||
|
||||
let registeredDialog = false;
|
||||
|
||||
export interface MoveCardViewDialogParams {
|
||||
path: [number, number];
|
||||
lovelace: Lovelace;
|
||||
}
|
||||
|
||||
const registerEditCardDialog = (element: HTMLElement) =>
|
||||
fireEvent(element, "register-dialog", {
|
||||
dialogShowEvent: "show-move-card-view",
|
||||
dialogTag: "hui-dialog-move-card-view",
|
||||
dialogImport: () =>
|
||||
import(/* webpackChunkName: "hui-dialog-move-card-view" */ "./hui-dialog-move-card-view"),
|
||||
});
|
||||
|
||||
export const showMoveCardViewDialog = (
|
||||
element: HTMLElement,
|
||||
moveCardViewDialogParams: MoveCardViewDialogParams
|
||||
) => {
|
||||
if (!registeredDialog) {
|
||||
registeredDialog = true;
|
||||
registerEditCardDialog(element);
|
||||
}
|
||||
fireEvent(element, "show-move-card-view", moveCardViewDialogParams);
|
||||
};
|
||||
@ -0,0 +1,195 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
|
||||
import { struct } from "../../common/structs/struct";
|
||||
import { EntitiesEditorEvent, EditorTarget } from "../types";
|
||||
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceCardEditor } from "../../types";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { Config } from "../../cards/hui-alarm-panel-card";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
|
||||
import "../../../../components/entity/ha-entity-picker";
|
||||
import "../../../../components/ha-icon";
|
||||
|
||||
const cardConfigStruct = struct({
|
||||
type: "string",
|
||||
entity: "string?",
|
||||
name: "string?",
|
||||
states: "array?",
|
||||
});
|
||||
|
||||
export class HuiAlarmPanelCardEditor extends hassLocalizeLitMixin(LitElement)
|
||||
implements LovelaceCardEditor {
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
|
||||
public setConfig(config: Config): void {
|
||||
config = cardConfigStruct(config);
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return { hass: {}, _config: {} };
|
||||
}
|
||||
|
||||
get _entity(): string {
|
||||
return this._config!.entity || "";
|
||||
}
|
||||
|
||||
get _name(): string {
|
||||
return this._config!.name || "";
|
||||
}
|
||||
|
||||
get _states(): string[] {
|
||||
return this._config!.states || [];
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const states = ["arm_home", "arm_away", "arm_night", "arm_custom_bypass"];
|
||||
|
||||
return html`
|
||||
${configElementStyle} ${this.renderStyle()}
|
||||
<div class="card-config">
|
||||
<div class="side-by-side">
|
||||
<paper-input
|
||||
label="Name"
|
||||
.value="${this._name}"
|
||||
.configValue="${"name"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
<ha-entity-picker
|
||||
.hass="${this.hass}"
|
||||
.value="${this._entity}"
|
||||
.configValue=${"entity"}
|
||||
domain-filter="alarm_control_panel"
|
||||
@change="${this._valueChanged}"
|
||||
allow-custom-entity
|
||||
></ha-entity-picker>
|
||||
</div>
|
||||
<span>Used States</span> ${
|
||||
this._states.map((state, index) => {
|
||||
return html`
|
||||
<div class="states">
|
||||
<paper-item>${state}</paper-item>
|
||||
<ha-icon
|
||||
class="deleteState"
|
||||
.value="${index}"
|
||||
icon="hass:close"
|
||||
@click=${this._stateRemoved}
|
||||
></ha-icon>
|
||||
</div>
|
||||
`;
|
||||
})
|
||||
}
|
||||
<paper-dropdown-menu
|
||||
label="Available States"
|
||||
@value-changed="${this._stateAdded}"
|
||||
>
|
||||
<paper-listbox slot="dropdown-content">
|
||||
${
|
||||
states.map((state) => {
|
||||
return html`
|
||||
<paper-item>${state}</paper-item>
|
||||
`;
|
||||
})
|
||||
}
|
||||
</paper-listbox>
|
||||
</paper-dropdown-menu>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private renderStyle(): TemplateResult {
|
||||
return html`
|
||||
<style>
|
||||
.states {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.deleteState {
|
||||
visibility: hidden;
|
||||
}
|
||||
.states:hover > .deleteState {
|
||||
visibility: visible;
|
||||
}
|
||||
ha-icon {
|
||||
padding-top: 12px;
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
}
|
||||
|
||||
private _stateRemoved(ev: EntitiesEditorEvent): void {
|
||||
if (!this._config || !this._states || !this.hass) {
|
||||
return;
|
||||
}
|
||||
|
||||
const target = ev.target! as EditorTarget;
|
||||
const index = Number(target.value);
|
||||
if (index > -1) {
|
||||
const newStates = this._states;
|
||||
newStates.splice(index, 1);
|
||||
this._config = {
|
||||
...this._config,
|
||||
states: newStates,
|
||||
};
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
}
|
||||
|
||||
private _stateAdded(ev: EntitiesEditorEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
if (!target.value || this._states.indexOf(target.value) >= 0) {
|
||||
return;
|
||||
}
|
||||
const newStates = this._states;
|
||||
newStates.push(target.value);
|
||||
this._config = {
|
||||
...this._config,
|
||||
states: newStates,
|
||||
};
|
||||
target.value = "";
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
|
||||
private _valueChanged(ev: EntitiesEditorEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
if (this[`_${target.configValue}`] === target.value) {
|
||||
return;
|
||||
}
|
||||
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-alarm-panel-card-editor": HuiAlarmPanelCardEditor;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-alarm-panel-card-editor", HuiAlarmPanelCardEditor);
|
||||
@ -1,13 +1,12 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import { struct } from "../../common/structs/struct";
|
||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import "@polymer/paper-toggle-button/paper-toggle-button";
|
||||
|
||||
import { processEditorEntities } from "../process-editor-entities";
|
||||
|
||||
import { struct } from "../../common/structs/struct";
|
||||
import { EntitiesEditorEvent, EditorTarget } from "../types";
|
||||
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
@ -59,8 +58,7 @@ export class HuiEntitiesCardEditor extends hassLocalizeLitMixin(LitElement)
|
||||
|
||||
public setConfig(config: Config): void {
|
||||
config = cardConfigStruct(config);
|
||||
|
||||
this._config = { type: "entities", ...config };
|
||||
this._config = config;
|
||||
this._configEntities = processEditorEntities(config.entities);
|
||||
}
|
||||
|
||||
@ -74,7 +72,7 @@ export class HuiEntitiesCardEditor extends hassLocalizeLitMixin(LitElement)
|
||||
<div class="card-config">
|
||||
<paper-input
|
||||
label="Title"
|
||||
value="${this._title}"
|
||||
.value="${this._title}"
|
||||
.configValue="${"title"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
@ -117,11 +115,15 @@ export class HuiEntitiesCardEditor extends hassLocalizeLitMixin(LitElement)
|
||||
this._config.entities = ev.detail.entities;
|
||||
this._configEntities = processEditorEntities(this._config.entities);
|
||||
} else if (target.configValue) {
|
||||
this._config = {
|
||||
...this._config,
|
||||
[target.configValue]:
|
||||
target.checked !== undefined ? target.checked : target.value,
|
||||
};
|
||||
if (target.value === "") {
|
||||
delete this._config[target.configValue!];
|
||||
} else {
|
||||
this._config = {
|
||||
...this._config,
|
||||
[target.configValue]:
|
||||
target.checked !== undefined ? target.checked : target.value,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
|
||||
@ -0,0 +1,165 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
|
||||
import { struct } from "../../common/structs/struct";
|
||||
import {
|
||||
EntitiesEditorEvent,
|
||||
EditorTarget,
|
||||
actionConfigStruct,
|
||||
} from "../types";
|
||||
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceCardEditor } from "../../types";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { Config } from "../../cards/hui-entity-button-card";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
import { ActionConfig } from "../../../../data/lovelace";
|
||||
|
||||
import "../../components/hui-action-editor";
|
||||
import "../../components/hui-theme-select-editor";
|
||||
import "../../components/hui-entity-editor";
|
||||
|
||||
const cardConfigStruct = struct({
|
||||
type: "string",
|
||||
entity: "string?",
|
||||
name: "string?",
|
||||
icon: "string?",
|
||||
tap_action: actionConfigStruct,
|
||||
hold_action: actionConfigStruct,
|
||||
theme: "string?",
|
||||
});
|
||||
|
||||
export class HuiEntityButtonCardEditor extends hassLocalizeLitMixin(LitElement)
|
||||
implements LovelaceCardEditor {
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
|
||||
public setConfig(config: Config): void {
|
||||
config = cardConfigStruct(config);
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return { hass: {}, _config: {} };
|
||||
}
|
||||
|
||||
get _entity(): string {
|
||||
return this._config!.entity || "";
|
||||
}
|
||||
|
||||
get _name(): string {
|
||||
return this._config!.name || "";
|
||||
}
|
||||
|
||||
get _icon(): string {
|
||||
return this._config!.icon || "";
|
||||
}
|
||||
|
||||
get _tap_action(): ActionConfig {
|
||||
return this._config!.tap_action || { action: "more-info" };
|
||||
}
|
||||
|
||||
get _hold_action(): ActionConfig {
|
||||
return this._config!.hold_action || { action: "none" };
|
||||
}
|
||||
|
||||
get _theme(): string {
|
||||
return this._config!.theme || "default";
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const actions = ["more-info", "toggle", "navigate", "call-service", "none"];
|
||||
|
||||
return html`
|
||||
${configElementStyle}
|
||||
<div class="card-config">
|
||||
<ha-entity-picker
|
||||
.hass="${this.hass}"
|
||||
.value="${this._entity}"
|
||||
.configValue=${"entity"}
|
||||
@change="${this._valueChanged}"
|
||||
allow-custom-entity
|
||||
></ha-entity-picker>
|
||||
<div class="side-by-side">
|
||||
<paper-input
|
||||
label="Name (Optional)"
|
||||
.value="${this._name}"
|
||||
.configValue="${"name"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
<paper-input
|
||||
label="Icon (Optional)"
|
||||
.value="${this._icon}"
|
||||
.configValue="${"icon"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
</div>
|
||||
<hui-theme-select-editor
|
||||
.hass="${this.hass}"
|
||||
.value="${this._theme}"
|
||||
.configValue="${"theme"}"
|
||||
@theme-changed="${this._valueChanged}"
|
||||
></hui-theme-select-editor>
|
||||
<div class="side-by-side">
|
||||
<hui-action-editor
|
||||
label="Tap Action"
|
||||
.hass="${this.hass}"
|
||||
.config="${this._tap_action}"
|
||||
.actions="${actions}"
|
||||
.configValue="${"tap_action"}"
|
||||
@action-changed="${this._valueChanged}"
|
||||
></hui-action-editor>
|
||||
<hui-action-editor
|
||||
label="Hold Action"
|
||||
.hass="${this.hass}"
|
||||
.config="${this._hold_action}"
|
||||
.actions="${actions}"
|
||||
.configValue="${"hold_action"}"
|
||||
@action-changed="${this._valueChanged}"
|
||||
></hui-action-editor>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: EntitiesEditorEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
|
||||
if (
|
||||
this[`_${target.configValue}`] === target.value ||
|
||||
this[`_${target.configValue}`] === target.config
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (target.configValue) {
|
||||
if (target.value === "") {
|
||||
delete this._config[target.configValue!];
|
||||
} else {
|
||||
this._config = {
|
||||
...this._config,
|
||||
[target.configValue!]: target.value ? target.value : target.config,
|
||||
};
|
||||
}
|
||||
}
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-entity-button-card-editor": HuiEntityButtonCardEditor;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define(
|
||||
"hui-entity-button-card-editor",
|
||||
HuiEntityButtonCardEditor
|
||||
);
|
||||
@ -0,0 +1,244 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import "@polymer/paper-toggle-button/paper-toggle-button";
|
||||
|
||||
import { struct } from "../../common/structs/struct";
|
||||
import { EntitiesEditorEvent, EditorTarget } from "../types";
|
||||
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceCardEditor } from "../../types";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { Config, SeverityConfig } from "../../cards/hui-gauge-card";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
|
||||
import "../../components/hui-theme-select-editor";
|
||||
import "../../components/hui-entity-editor";
|
||||
|
||||
const cardConfigStruct = struct({
|
||||
type: "string",
|
||||
name: "string?",
|
||||
entity: "string?",
|
||||
unit: "string?",
|
||||
min: "number?",
|
||||
max: "number?",
|
||||
severity: "object?",
|
||||
theme: "string?",
|
||||
});
|
||||
|
||||
export class HuiGaugeCardEditor extends hassLocalizeLitMixin(LitElement)
|
||||
implements LovelaceCardEditor {
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
private _useSeverity?: boolean;
|
||||
|
||||
public setConfig(config: Config): void {
|
||||
config = cardConfigStruct(config);
|
||||
this._useSeverity = config.severity ? true : false;
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return { hass: {}, _config: {} };
|
||||
}
|
||||
|
||||
get _name(): string {
|
||||
return this._config!.name || "";
|
||||
}
|
||||
|
||||
get _entity(): string {
|
||||
return this._config!.entity || "";
|
||||
}
|
||||
|
||||
get _unit(): string {
|
||||
return this._config!.unit || "";
|
||||
}
|
||||
|
||||
get _theme(): string {
|
||||
return this._config!.theme || "default";
|
||||
}
|
||||
|
||||
get _min(): number {
|
||||
return this._config!.number || 0;
|
||||
}
|
||||
|
||||
get _max(): number {
|
||||
return this._config!.max || 100;
|
||||
}
|
||||
|
||||
get _severity(): SeverityConfig | undefined {
|
||||
return this._config!.severity || undefined;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
${configElementStyle} ${this.renderStyle()}
|
||||
<div class="card-config">
|
||||
<div class="side-by-side">
|
||||
<paper-input
|
||||
label="Name"
|
||||
.value="${this._name}"
|
||||
.configValue=${"name"}
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
<ha-entity-picker
|
||||
.hass="${this.hass}"
|
||||
.value="${this._entity}"
|
||||
.configValue=${"entity"}
|
||||
domain-filter="sensor"
|
||||
@change="${this._valueChanged}"
|
||||
allow-custom-entity
|
||||
></ha-entity-picker>
|
||||
</div>
|
||||
<div class="side-by-side">
|
||||
<paper-input
|
||||
label="Unit"
|
||||
.value="${this._unit}"
|
||||
.configValue=${"unit"}
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
<hui-theme-select-editor
|
||||
.hass="${this.hass}"
|
||||
.value="${this._theme}"
|
||||
.configValue="${"theme"}"
|
||||
@theme-changed="${this._valueChanged}"
|
||||
></hui-theme-select-editor>
|
||||
</div>
|
||||
<div class="side-by-side">
|
||||
<paper-input
|
||||
type="number"
|
||||
label="Minimum"
|
||||
.value="${this._min}"
|
||||
.configValue=${"min"}
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
<paper-input
|
||||
type="number"
|
||||
label="Maximum"
|
||||
.value="${this._max}"
|
||||
.configValue=${"max"}
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
</div>
|
||||
<div class="side-by-side">
|
||||
<paper-toggle-button
|
||||
?checked="${this._useSeverity !== false}"
|
||||
@change="${this._toggleSeverity}"
|
||||
>Define Severity?</paper-toggle-button
|
||||
>
|
||||
<div class="severity">
|
||||
<paper-input
|
||||
type="number"
|
||||
label="Green"
|
||||
.value="${this._severity ? this._severity.green : 0}"
|
||||
.configValue=${"green"}
|
||||
@value-changed="${this._severityChanged}"
|
||||
></paper-input>
|
||||
<paper-input
|
||||
type="number"
|
||||
label="Yellow"
|
||||
.value="${this._severity ? this._severity.yellow : 0}"
|
||||
.configValue=${"yellow"}
|
||||
@value-changed="${this._severityChanged}"
|
||||
></paper-input>
|
||||
<paper-input
|
||||
type="number"
|
||||
label="Red"
|
||||
.value="${this._severity ? this._severity.red : 0}"
|
||||
.configValue=${"red"}
|
||||
@value-changed="${this._severityChanged}"
|
||||
></paper-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private renderStyle(): TemplateResult {
|
||||
return html`
|
||||
<style>
|
||||
.severity {
|
||||
display: none;
|
||||
width: 100%;
|
||||
padding-left: 16px;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.severity > * {
|
||||
flex: 1 0 30%;
|
||||
padding-right: 4px;
|
||||
}
|
||||
paper-toggle-button[checked] ~ .severity {
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
}
|
||||
|
||||
private _toggleSeverity(ev: EntitiesEditorEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
|
||||
this._config.severity = target.checked
|
||||
? {
|
||||
green: 0,
|
||||
yellow: 0,
|
||||
red: 0,
|
||||
}
|
||||
: undefined;
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
|
||||
private _severityChanged(ev: EntitiesEditorEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
const severity = {
|
||||
...this._config.severity,
|
||||
[target.configValue!]: Number(target.value),
|
||||
};
|
||||
this._config = {
|
||||
...this._config,
|
||||
severity,
|
||||
};
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
|
||||
private _valueChanged(ev: EntitiesEditorEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
|
||||
if (target.configValue) {
|
||||
if (
|
||||
target.value === "" ||
|
||||
(target.type === "number" && isNaN(Number(target.value)))
|
||||
) {
|
||||
delete this._config[target.configValue!];
|
||||
} else {
|
||||
let value: any = target.value;
|
||||
if (target.type === "number") {
|
||||
value = Number(value);
|
||||
}
|
||||
this._config = { ...this._config, [target.configValue!]: value };
|
||||
}
|
||||
}
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-gauge-card-editor": HuiGaugeCardEditor;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-gauge-card-editor", HuiGaugeCardEditor);
|
||||
@ -1,11 +1,11 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import { struct } from "../../common/structs/struct";
|
||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import "@polymer/paper-toggle-button/paper-toggle-button";
|
||||
|
||||
import { struct } from "../../common/structs/struct";
|
||||
import { processEditorEntities } from "../process-editor-entities";
|
||||
import { EntitiesEditorEvent, EditorTarget } from "../types";
|
||||
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
|
||||
@ -48,8 +48,7 @@ export class HuiGlanceCardEditor extends hassLocalizeLitMixin(LitElement)
|
||||
|
||||
public setConfig(config: Config): void {
|
||||
config = cardConfigStruct(config);
|
||||
|
||||
this._config = { type: "glance", ...config };
|
||||
this._config = config;
|
||||
this._configEntities = processEditorEntities(config.entities);
|
||||
}
|
||||
|
||||
@ -65,8 +64,8 @@ export class HuiGlanceCardEditor extends hassLocalizeLitMixin(LitElement)
|
||||
return this._config!.theme || "Backend-selected";
|
||||
}
|
||||
|
||||
get _columns(): string {
|
||||
return this._config!.columns ? String(this._config!.columns) : "";
|
||||
get _columns(): number {
|
||||
return this._config!.columns || NaN;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
@ -79,7 +78,7 @@ export class HuiGlanceCardEditor extends hassLocalizeLitMixin(LitElement)
|
||||
<div class="card-config">
|
||||
<paper-input
|
||||
label="Title"
|
||||
value="${this._title}"
|
||||
.value="${this._title}"
|
||||
.configValue="${"title"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
@ -93,7 +92,7 @@ export class HuiGlanceCardEditor extends hassLocalizeLitMixin(LitElement)
|
||||
<paper-input
|
||||
label="Columns"
|
||||
type="number"
|
||||
value="${this._columns}"
|
||||
.value="${this._columns}"
|
||||
.configValue="${"columns"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
@ -127,22 +126,29 @@ export class HuiGlanceCardEditor extends hassLocalizeLitMixin(LitElement)
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
|
||||
if (this[`_${target.configValue}`] === target.value) {
|
||||
if (target.configValue && this[`_${target.configValue}`] === target.value) {
|
||||
return;
|
||||
}
|
||||
if (ev.detail && ev.detail.entities) {
|
||||
this._config.entities = ev.detail.entities;
|
||||
this._configEntities = processEditorEntities(this._config.entities);
|
||||
} else if (target.configValue) {
|
||||
let value: any = target.value;
|
||||
if (target.type === "number") {
|
||||
value = Number(value);
|
||||
if (
|
||||
target.value === "" ||
|
||||
(target.type === "number" && isNaN(Number(target.value)))
|
||||
) {
|
||||
delete this._config[target.configValue!];
|
||||
} else {
|
||||
let value: any = target.value;
|
||||
if (target.type === "number") {
|
||||
value = Number(value);
|
||||
}
|
||||
this._config = {
|
||||
...this._config,
|
||||
[target.configValue!]:
|
||||
target.checked !== undefined ? target.checked : value,
|
||||
};
|
||||
}
|
||||
this._config = {
|
||||
...this._config,
|
||||
[target.configValue!]:
|
||||
target.checked !== undefined ? target.checked : value,
|
||||
};
|
||||
}
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
|
||||
@ -0,0 +1,111 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
|
||||
import { struct } from "../../common/structs/struct";
|
||||
import { EntitiesEditorEvent, EditorTarget } from "../types";
|
||||
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceCardEditor } from "../../types";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { Config } from "../../cards/hui-iframe-card";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
|
||||
const cardConfigStruct = struct({
|
||||
type: "string",
|
||||
title: "string?",
|
||||
url: "string?",
|
||||
aspect_ratio: "string?",
|
||||
});
|
||||
|
||||
export class HuiIframeCardEditor extends hassLocalizeLitMixin(LitElement)
|
||||
implements LovelaceCardEditor {
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
|
||||
public setConfig(config: Config): void {
|
||||
config = cardConfigStruct(config);
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return { hass: {}, _config: {} };
|
||||
}
|
||||
|
||||
get _title(): string {
|
||||
return this._config!.title || "";
|
||||
}
|
||||
|
||||
get _url(): string {
|
||||
return this._config!.url || "";
|
||||
}
|
||||
|
||||
get _aspect_ratio(): string {
|
||||
return this._config!.aspect_ratio || "";
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
${configElementStyle}
|
||||
<div class="card-config">
|
||||
<div class="side-by-side">
|
||||
<paper-input
|
||||
label="Title"
|
||||
.value="${this._title}"
|
||||
.configValue="${"title"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
<paper-input
|
||||
label="Aspect Ratio"
|
||||
type="number"
|
||||
.value="${Number(this._aspect_ratio.replace("%", ""))}"
|
||||
.configValue="${"aspect_ratio"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
</div>
|
||||
<paper-input
|
||||
label="Url"
|
||||
.value="${this._url}"
|
||||
.configValue="${"url"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: EntitiesEditorEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
let value = target.value;
|
||||
|
||||
if (target.configValue! === "aspect_ratio" && target.value) {
|
||||
value += "%";
|
||||
}
|
||||
|
||||
if (this[`_${target.configValue}`] === value) {
|
||||
return;
|
||||
}
|
||||
if (target.configValue) {
|
||||
if (target.value === "") {
|
||||
delete this._config[target.configValue!];
|
||||
} else {
|
||||
this._config = { ...this._config, [target.configValue!]: value };
|
||||
}
|
||||
}
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-iframe-card-editor": HuiIframeCardEditor;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-iframe-card-editor", HuiIframeCardEditor);
|
||||
@ -0,0 +1,113 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
|
||||
import { struct } from "../../common/structs/struct";
|
||||
import { EntitiesEditorEvent, EditorTarget } from "../types";
|
||||
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceCardEditor } from "../../types";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { Config } from "../../cards/hui-light-card";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
|
||||
import "../../components/hui-theme-select-editor";
|
||||
import "../../components/hui-entity-editor";
|
||||
|
||||
const cardConfigStruct = struct({
|
||||
type: "string",
|
||||
name: "string?",
|
||||
entity: "string?",
|
||||
theme: "string?",
|
||||
});
|
||||
|
||||
export class HuiLightCardEditor extends hassLocalizeLitMixin(LitElement)
|
||||
implements LovelaceCardEditor {
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
|
||||
public setConfig(config: Config): void {
|
||||
config = cardConfigStruct(config);
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return { hass: {}, _config: {}, _configEntities: {} };
|
||||
}
|
||||
|
||||
get _name(): string {
|
||||
return this._config!.name || "";
|
||||
}
|
||||
|
||||
get _theme(): string {
|
||||
return this._config!.theme || "default";
|
||||
}
|
||||
|
||||
get _entity(): string {
|
||||
return this._config!.entity || "";
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
${configElementStyle}
|
||||
<div class="card-config">
|
||||
<paper-input
|
||||
label="Name"
|
||||
.value="${this._name}"
|
||||
.configValue="${"name"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
<div class="side-by-side">
|
||||
<ha-entity-picker
|
||||
.hass="${this.hass}"
|
||||
.value="${this._entity}"
|
||||
.configValue=${"entity"}
|
||||
domain-filter="light"
|
||||
@change="${this._valueChanged}"
|
||||
allow-custom-entity
|
||||
></ha-entity-picker>
|
||||
<hui-theme-select-editor
|
||||
.hass="${this.hass}"
|
||||
.value="${this._theme}"
|
||||
.configValue="${"theme"}"
|
||||
@theme-changed="${this._valueChanged}"
|
||||
></hui-theme-select-editor>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: EntitiesEditorEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
|
||||
if (this[`_${target.configValue}`] === target.value) {
|
||||
return;
|
||||
}
|
||||
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-light-card-editor": HuiLightCardEditor;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-light-card-editor", HuiLightCardEditor);
|
||||
@ -0,0 +1,143 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
|
||||
import { struct } from "../../common/structs/struct";
|
||||
import { EntitiesEditorEvent, EditorTarget } from "../types";
|
||||
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceCardEditor } from "../../types";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { Config } from "../../cards/hui-alarm-panel-card";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
import { processEditorEntities } from "../process-editor-entities";
|
||||
import { EntityConfig } from "../../entity-rows/types";
|
||||
|
||||
import "../../components/hui-entity-editor";
|
||||
|
||||
const entitiesConfigStruct = struct.union([
|
||||
{
|
||||
entity: "entity-id",
|
||||
name: "string?",
|
||||
icon: "icon?",
|
||||
},
|
||||
"entity-id",
|
||||
]);
|
||||
|
||||
const cardConfigStruct = struct({
|
||||
type: "string",
|
||||
title: "string?",
|
||||
aspect_ratio: "string?",
|
||||
default_zoom: "number?",
|
||||
entities: [entitiesConfigStruct],
|
||||
});
|
||||
|
||||
export class HuiMapCardEditor extends hassLocalizeLitMixin(LitElement)
|
||||
implements LovelaceCardEditor {
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
private _configEntities?: EntityConfig[];
|
||||
|
||||
public setConfig(config: Config): void {
|
||||
config = cardConfigStruct(config);
|
||||
this._config = config;
|
||||
this._configEntities = processEditorEntities(config.entities);
|
||||
}
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return { hass: {}, _config: {}, _configEntities: {} };
|
||||
}
|
||||
|
||||
get _title(): string {
|
||||
return this._config!.title || "";
|
||||
}
|
||||
|
||||
get _aspect_ratio(): string {
|
||||
return this._config!.aspect_ratio || "";
|
||||
}
|
||||
|
||||
get _default_zoom(): number {
|
||||
return this._config!.default_zoom || NaN;
|
||||
}
|
||||
|
||||
get _entities(): string[] {
|
||||
return this._config!.entities || [];
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
${configElementStyle}
|
||||
<div class="card-config">
|
||||
<paper-input
|
||||
label="Title"
|
||||
.value="${this._title}"
|
||||
.configValue="${"title"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
<div class="side-by-side">
|
||||
<paper-input
|
||||
label="Aspect Ratio"
|
||||
.value="${this._aspect_ratio}"
|
||||
.configValue="${"aspect_ratio"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
<paper-input
|
||||
label="Default Zoom"
|
||||
type="number"
|
||||
.value="${this._default_zoom}"
|
||||
.configValue="${"default_zoom"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
</div>
|
||||
<hui-entity-editor
|
||||
.hass="${this.hass}"
|
||||
.entities="${this._configEntities}"
|
||||
@entities-changed="${this._valueChanged}"
|
||||
></hui-entity-editor>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: EntitiesEditorEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
if (target.configValue && this[`_${target.configValue}`] === target.value) {
|
||||
return;
|
||||
}
|
||||
if (ev.detail && ev.detail.entities) {
|
||||
this._config.entities = ev.detail.entities;
|
||||
this._configEntities = processEditorEntities(this._config.entities);
|
||||
} else if (target.configValue) {
|
||||
if (
|
||||
target.value === "" ||
|
||||
(target.type === "number" && isNaN(Number(target.value)))
|
||||
) {
|
||||
delete this._config[target.configValue!];
|
||||
} else {
|
||||
let value: any = target.value;
|
||||
if (target.type === "number") {
|
||||
value = Number(value);
|
||||
}
|
||||
this._config = {
|
||||
...this._config,
|
||||
[target.configValue!]: value,
|
||||
};
|
||||
}
|
||||
}
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-map-card-editor": HuiMapCardEditor;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-map-card-editor", HuiMapCardEditor);
|
||||
@ -0,0 +1,99 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import "@polymer/paper-input/paper-textarea";
|
||||
|
||||
import { struct } from "../../common/structs/struct";
|
||||
import { EntitiesEditorEvent, EditorTarget } from "../types";
|
||||
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceCardEditor } from "../../types";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { Config } from "../../cards/hui-glance-card";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
|
||||
const cardConfigStruct = struct({
|
||||
type: "string",
|
||||
title: "string?",
|
||||
content: "string",
|
||||
});
|
||||
|
||||
export class HuiMarkdownCardEditor extends hassLocalizeLitMixin(LitElement)
|
||||
implements LovelaceCardEditor {
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
|
||||
public setConfig(config: Config): void {
|
||||
config = cardConfigStruct(config);
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return { hass: {}, _config: {} };
|
||||
}
|
||||
|
||||
get _title(): string {
|
||||
return this._config!.title || "";
|
||||
}
|
||||
|
||||
get _content(): string {
|
||||
return this._config!.content || "";
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
${configElementStyle}
|
||||
<div class="card-config">
|
||||
<paper-input
|
||||
label="Title"
|
||||
.value="${this._title}"
|
||||
.configValue="${"title"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
<paper-textarea
|
||||
label="Content"
|
||||
.value="${this._content}"
|
||||
.configValue="${"content"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
autocapitalize="none"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
></paper-textarea>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: EntitiesEditorEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
|
||||
if (this[`_${target.configValue}`] === target.value) {
|
||||
return;
|
||||
}
|
||||
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-markdown-card-editor": HuiMarkdownCardEditor;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-markdown-card-editor", HuiMarkdownCardEditor);
|
||||
@ -0,0 +1,87 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
|
||||
import { struct } from "../../common/structs/struct";
|
||||
import { EntitiesEditorEvent, EditorTarget } from "../types";
|
||||
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceCardEditor } from "../../types";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { Config } from "../../cards/hui-media-control-card";
|
||||
|
||||
import "../../../../components/entity/ha-entity-picker";
|
||||
|
||||
const cardConfigStruct = struct({
|
||||
type: "string",
|
||||
entity: "string?",
|
||||
});
|
||||
|
||||
export class HuiMediaControlCardEditor extends hassLocalizeLitMixin(LitElement)
|
||||
implements LovelaceCardEditor {
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
|
||||
public setConfig(config: Config): void {
|
||||
config = cardConfigStruct(config);
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return { hass: {}, _config: {} };
|
||||
}
|
||||
|
||||
get _entity(): string {
|
||||
return this._config!.entity || "";
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
<div class="card-config">
|
||||
<ha-entity-picker
|
||||
.hass="${this.hass}"
|
||||
.value="${this._entity}"
|
||||
.configValue=${"entity"}
|
||||
domain-filter="media_player"
|
||||
@change="${this._valueChanged}"
|
||||
allow-custom-entity
|
||||
></ha-entity-picker>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: EntitiesEditorEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
if (this[`_${target.configValue}`] === target.value) {
|
||||
return;
|
||||
}
|
||||
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-media-control-card-editor": HuiMediaControlCardEditor;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define(
|
||||
"hui-media-control-card-editor",
|
||||
HuiMediaControlCardEditor
|
||||
);
|
||||
@ -0,0 +1,124 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
|
||||
import { struct } from "../../common/structs/struct";
|
||||
import {
|
||||
EntitiesEditorEvent,
|
||||
EditorTarget,
|
||||
actionConfigStruct,
|
||||
} from "../types";
|
||||
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceCardEditor } from "../../types";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { Config } from "../../cards/hui-picture-card";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
import { ActionConfig } from "../../../../data/lovelace";
|
||||
|
||||
import "../../components/hui-action-editor";
|
||||
|
||||
const cardConfigStruct = struct({
|
||||
type: "string",
|
||||
image: "string?",
|
||||
tap_action: actionConfigStruct,
|
||||
hold_action: actionConfigStruct,
|
||||
});
|
||||
|
||||
export class HuiPictureCardEditor extends hassLocalizeLitMixin(LitElement)
|
||||
implements LovelaceCardEditor {
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
|
||||
public setConfig(config: Config): void {
|
||||
config = cardConfigStruct(config);
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return { hass: {}, _config: {} };
|
||||
}
|
||||
|
||||
get _image(): string {
|
||||
return this._config!.image || "";
|
||||
}
|
||||
|
||||
get _tap_action(): ActionConfig {
|
||||
return this._config!.tap_action || { action: "none" };
|
||||
}
|
||||
|
||||
get _hold_action(): ActionConfig {
|
||||
return this._config!.hold_action || { action: "none" };
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const actions = ["navigate", "call-service", "none"];
|
||||
|
||||
return html`
|
||||
${configElementStyle}
|
||||
<div class="card-config">
|
||||
<paper-input
|
||||
label="Image Url"
|
||||
.value="${this._image}"
|
||||
.configValue="${"image"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
<div class="side-by-side">
|
||||
<hui-action-editor
|
||||
label="Tap Action"
|
||||
.hass="${this.hass}"
|
||||
.config="${this._tap_action}"
|
||||
.actions="${actions}"
|
||||
.configValue="${"tap_action"}"
|
||||
@action-changed="${this._valueChanged}"
|
||||
></hui-action-editor>
|
||||
<hui-action-editor
|
||||
label="Hold Action"
|
||||
.hass="${this.hass}"
|
||||
.config="${this._hold_action}"
|
||||
.actions="${actions}"
|
||||
.configValue="${"hold_action"}"
|
||||
@action-changed="${this._valueChanged}"
|
||||
></hui-action-editor>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: EntitiesEditorEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
|
||||
if (
|
||||
this[`_${target.configValue}`] === target.value ||
|
||||
this[`_${target.configValue}`] === target.config
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (target.configValue) {
|
||||
if (target.value === "") {
|
||||
delete this._config[target.configValue!];
|
||||
} else {
|
||||
this._config = {
|
||||
...this._config,
|
||||
[target.configValue!]: target.value ? target.value : target.config,
|
||||
};
|
||||
}
|
||||
}
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-picture-card-editor": HuiPictureCardEditor;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-picture-card-editor", HuiPictureCardEditor);
|
||||
@ -0,0 +1,101 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
|
||||
import { struct } from "../../common/structs/struct";
|
||||
import { EntitiesEditorEvent, EditorTarget } from "../types";
|
||||
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceCardEditor } from "../../types";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { Config } from "../../cards/hui-alarm-panel-card";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
|
||||
import "../../../../components/entity/ha-entity-picker";
|
||||
import "../../../../components/ha-icon";
|
||||
|
||||
const cardConfigStruct = struct({
|
||||
type: "string",
|
||||
entity: "string",
|
||||
name: "string?",
|
||||
});
|
||||
|
||||
export class HuiPlantStatusCardEditor extends hassLocalizeLitMixin(LitElement)
|
||||
implements LovelaceCardEditor {
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
|
||||
public setConfig(config: Config): void {
|
||||
config = cardConfigStruct(config);
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return { hass: {}, _config: {} };
|
||||
}
|
||||
|
||||
get _entity(): string {
|
||||
return this._config!.entity || "";
|
||||
}
|
||||
|
||||
get _name(): string {
|
||||
return this._config!.name || "";
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
${configElementStyle}
|
||||
<div class="card-config">
|
||||
<div class="side-by-side">
|
||||
<paper-input
|
||||
label="Name"
|
||||
.value="${this._name}"
|
||||
.configValue="${"name"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
<ha-entity-picker
|
||||
.hass="${this.hass}"
|
||||
.value="${this._entity}"
|
||||
.configValue=${"entity"}
|
||||
domain-filter="plant"
|
||||
@change="${this._valueChanged}"
|
||||
allow-custom-entity
|
||||
></ha-entity-picker>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: EntitiesEditorEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
if (this[`_${target.configValue}`] === target.value) {
|
||||
return;
|
||||
}
|
||||
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-plant-status-card-editor": HuiPlantStatusCardEditor;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-plant-status-card-editor", HuiPlantStatusCardEditor);
|
||||
@ -0,0 +1,197 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
|
||||
import { struct } from "../../common/structs/struct";
|
||||
import { EntitiesEditorEvent, EditorTarget } from "../types";
|
||||
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceCardEditor } from "../../types";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { Config } from "../../cards/hui-sensor-card";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
|
||||
import "../../components/hui-theme-select-editor";
|
||||
import "../../../../components/entity/ha-entity-picker";
|
||||
|
||||
const cardConfigStruct = struct({
|
||||
type: "string",
|
||||
entity: "string?",
|
||||
name: "string?",
|
||||
icon: "string?",
|
||||
graph: "string?",
|
||||
unit: "string?",
|
||||
detail: "number?",
|
||||
theme: "string?",
|
||||
hours_to_show: "number?",
|
||||
});
|
||||
|
||||
export class HuiSensorCardEditor extends hassLocalizeLitMixin(LitElement)
|
||||
implements LovelaceCardEditor {
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
|
||||
public setConfig(config: Config): void {
|
||||
config = cardConfigStruct(config);
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return { hass: {}, _config: {} };
|
||||
}
|
||||
|
||||
get _entity(): string {
|
||||
return this._config!.entity || "";
|
||||
}
|
||||
|
||||
get _name(): string {
|
||||
return this._config!.name || "";
|
||||
}
|
||||
|
||||
get _icon(): string {
|
||||
return this._config!.icon || "";
|
||||
}
|
||||
|
||||
get _graph(): string {
|
||||
return this._config!.graph || "none";
|
||||
}
|
||||
|
||||
get _unit(): string {
|
||||
return this._config!.unit || "";
|
||||
}
|
||||
|
||||
get _detail(): number | string {
|
||||
return this._config!.number || "1";
|
||||
}
|
||||
|
||||
get _theme(): string {
|
||||
return this._config!.theme || "default";
|
||||
}
|
||||
|
||||
get _hours_to_show(): number | string {
|
||||
return this._config!.hours_to_show || "24";
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const graphs = ["line", "none"];
|
||||
|
||||
return html`
|
||||
${configElementStyle}
|
||||
<div class="card-config">
|
||||
<div class="side-by-side">
|
||||
<paper-input
|
||||
label="Name"
|
||||
.value="${this._name}"
|
||||
.configValue="${"name"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
<ha-entity-picker
|
||||
.hass="${this.hass}"
|
||||
.value="${this._entity}"
|
||||
.configValue=${"entity"}
|
||||
domain-filter="sensor"
|
||||
@change="${this._valueChanged}"
|
||||
allow-custom-entity
|
||||
></ha-entity-picker>
|
||||
</div>
|
||||
<div class="side-by-side">
|
||||
<paper-input
|
||||
label="Icon"
|
||||
.value="${this._icon}"
|
||||
.configValue="${"icon"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
<paper-dropdown-menu
|
||||
label="Graph Type"
|
||||
.configValue="${"graph"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
>
|
||||
<paper-listbox
|
||||
slot="dropdown-content"
|
||||
.selected="${graphs.indexOf(this._graph)}"
|
||||
>
|
||||
${
|
||||
graphs.map((graph) => {
|
||||
return html`
|
||||
<paper-item>${graph}</paper-item>
|
||||
`;
|
||||
})
|
||||
}
|
||||
</paper-listbox>
|
||||
</paper-dropdown-menu>
|
||||
</div>
|
||||
<div class="side-by-side">
|
||||
<paper-input
|
||||
label="Units"
|
||||
.value="${this._unit}"
|
||||
.configValue="${"unit"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
<paper-input
|
||||
label="Graph Detail"
|
||||
type="number"
|
||||
.value="${this._detail}"
|
||||
.configValue="${"detail"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
</div>
|
||||
<div class="side-by-side">
|
||||
<hui-theme-select-editor
|
||||
.hass="${this.hass}"
|
||||
.value="${this._theme}"
|
||||
.configValue="${"theme"}"
|
||||
@theme-changed="${this._valueChanged}"
|
||||
></hui-theme-select-editor>
|
||||
<paper-input
|
||||
label="Hours To Show"
|
||||
type="number"
|
||||
.value="${this._hours_to_show}"
|
||||
.configValue="${"hours_to_show"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: EntitiesEditorEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
|
||||
if (this[`_${target.configValue}`] === target.value) {
|
||||
return;
|
||||
}
|
||||
if (target.configValue) {
|
||||
if (
|
||||
target.value === "" ||
|
||||
(target.type === "number" && isNaN(Number(target.value)))
|
||||
) {
|
||||
delete this._config[target.configValue!];
|
||||
} else {
|
||||
let value: any = target.value;
|
||||
if (target.type === "number") {
|
||||
value = Number(value);
|
||||
}
|
||||
this._config = { ...this._config, [target.configValue!]: value };
|
||||
}
|
||||
}
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-sensor-card-editor": HuiSensorCardEditor;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-sensor-card-editor", HuiSensorCardEditor);
|
||||
@ -0,0 +1,82 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
|
||||
import { struct } from "../../common/structs/struct";
|
||||
import { EntitiesEditorEvent, EditorTarget } from "../types";
|
||||
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceCardEditor } from "../../types";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { Config } from "../../cards/hui-shopping-list-card";
|
||||
|
||||
const cardConfigStruct = struct({
|
||||
type: "string",
|
||||
title: "string?",
|
||||
});
|
||||
|
||||
export class HuiShoppingListEditor extends hassLocalizeLitMixin(LitElement)
|
||||
implements LovelaceCardEditor {
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
|
||||
public setConfig(config: Config): void {
|
||||
config = cardConfigStruct(config);
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return { hass: {}, _config: {} };
|
||||
}
|
||||
|
||||
get _title(): string {
|
||||
return this._config!.title || "";
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
<div class="card-config">
|
||||
<paper-input
|
||||
label="Title"
|
||||
.value="${this._title}"
|
||||
.configValue="${"title"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: EntitiesEditorEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
|
||||
if (this[`_${target.configValue}`] === target.value) {
|
||||
return;
|
||||
}
|
||||
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-shopping-list-card-editor": HuiShoppingListEditor;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-shopping-list-card-editor", HuiShoppingListEditor);
|
||||
@ -0,0 +1,110 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
|
||||
import { struct } from "../../common/structs/struct";
|
||||
import { EntitiesEditorEvent, EditorTarget } from "../types";
|
||||
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceCardEditor } from "../../types";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { Config } from "../../cards/hui-thermostat-card";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
|
||||
import "../../components/hui-theme-select-editor";
|
||||
import "../../../../components/entity/ha-entity-picker";
|
||||
|
||||
const cardConfigStruct = struct({
|
||||
type: "string",
|
||||
entity: "string",
|
||||
name: "string?",
|
||||
theme: "string?",
|
||||
});
|
||||
|
||||
export class HuiThermostatCardEditor extends hassLocalizeLitMixin(LitElement)
|
||||
implements LovelaceCardEditor {
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
|
||||
public setConfig(config: Config): void {
|
||||
config = cardConfigStruct(config);
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return { hass: {}, _config: {} };
|
||||
}
|
||||
|
||||
get _entity(): string {
|
||||
return this._config!.entity || "";
|
||||
}
|
||||
|
||||
get _name(): string {
|
||||
return this._config!.name || "";
|
||||
}
|
||||
|
||||
get _theme(): string {
|
||||
return this._config!.theme || "default";
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
${configElementStyle}
|
||||
<div class="card-config">
|
||||
<paper-input
|
||||
label="Name"
|
||||
.value="${this._name}"
|
||||
.configValue="${"name"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
<div class="side-by-side">
|
||||
<ha-entity-picker
|
||||
.hass="${this.hass}"
|
||||
.value="${this._entity}"
|
||||
.configValue=${"entity"}
|
||||
domain-filter="climate"
|
||||
@change="${this._valueChanged}"
|
||||
allow-custom-entity
|
||||
></ha-entity-picker>
|
||||
<hui-theme-select-editor
|
||||
.hass="${this.hass}"
|
||||
.value="${this._theme}"
|
||||
.configValue="${"theme"}"
|
||||
@theme-changed="${this._valueChanged}"
|
||||
></hui-theme-select-editor>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: EntitiesEditorEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
|
||||
if (this[`_${target.configValue}`] === target.value) {
|
||||
return;
|
||||
}
|
||||
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-thermostat-card-editor": HuiThermostatCardEditor;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-thermostat-card-editor", HuiThermostatCardEditor);
|
||||
@ -0,0 +1,103 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
|
||||
import { struct } from "../../common/structs/struct";
|
||||
import { EntitiesEditorEvent, EditorTarget } from "../types";
|
||||
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceCardEditor } from "../../types";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { Config } from "../../cards/hui-weather-forecast-card";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
|
||||
import "../../../../components/entity/ha-entity-picker";
|
||||
|
||||
const cardConfigStruct = struct({
|
||||
type: "string",
|
||||
entity: "string?",
|
||||
name: "string?",
|
||||
});
|
||||
|
||||
export class HuiWeatherForecastCardEditor
|
||||
extends hassLocalizeLitMixin(LitElement)
|
||||
implements LovelaceCardEditor {
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
|
||||
public setConfig(config: Config): void {
|
||||
config = cardConfigStruct(config);
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return { hass: {}, _config: {} };
|
||||
}
|
||||
|
||||
get _entity(): string {
|
||||
return this._config!.entity || "";
|
||||
}
|
||||
|
||||
get _name(): string {
|
||||
return this._config!.name || "";
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
${configElementStyle}
|
||||
<div class="card-config">
|
||||
<div class="side-by-side">
|
||||
<paper-input
|
||||
label="Name"
|
||||
.value="${this._name}"
|
||||
.configValue="${"name"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
<ha-entity-picker
|
||||
.hass="${this.hass}"
|
||||
.value="${this._entity}"
|
||||
.configValue=${"entity"}
|
||||
domain-filter="weather"
|
||||
@change="${this._valueChanged}"
|
||||
allow-custom-entity
|
||||
></ha-entity-picker>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: EntitiesEditorEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
if (this[`_${target.configValue}`] === target.value) {
|
||||
return;
|
||||
}
|
||||
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-weather-forecast-card-editor": HuiWeatherForecastCardEditor;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define(
|
||||
"hui-weather-forecast-card-editor",
|
||||
HuiWeatherForecastCardEditor
|
||||
);
|
||||
@ -121,6 +121,44 @@ export const swapCard = (
|
||||
};
|
||||
};
|
||||
|
||||
export const moveCard = (
|
||||
config: LovelaceConfig,
|
||||
fromPath: [number, number],
|
||||
toPath: [number]
|
||||
): LovelaceConfig => {
|
||||
if (fromPath[0] === toPath[0]) {
|
||||
throw new Error("You can not move a card to the view it is in.");
|
||||
}
|
||||
const fromView = config.views[fromPath[0]];
|
||||
const card = fromView.cards![fromPath[1]];
|
||||
|
||||
const newView1 = {
|
||||
...fromView,
|
||||
cards: (fromView.cards || []).filter(
|
||||
(_origConf, ind) => ind !== fromPath[1]
|
||||
),
|
||||
};
|
||||
|
||||
const toView = config.views[toPath[0]];
|
||||
const cards = toView.cards ? [...toView.cards, card] : [card];
|
||||
|
||||
const newView2 = {
|
||||
...toView,
|
||||
cards,
|
||||
};
|
||||
|
||||
return {
|
||||
...config,
|
||||
views: config.views.map((origView, index) =>
|
||||
index === toPath[0]
|
||||
? newView2
|
||||
: index === fromPath[0]
|
||||
? newView1
|
||||
: origView
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
export const addView = (
|
||||
config: LovelaceConfig,
|
||||
viewConfig: LovelaceViewConfig
|
||||
|
||||
@ -14,7 +14,7 @@ import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
||||
import { SaveDialogParams } from "./show-save-config-dialog";
|
||||
|
||||
export class HuiSaveConfig extends hassLocalizeLitMixin(LitElement) {
|
||||
protected hass?: HomeAssistant;
|
||||
public hass?: HomeAssistant;
|
||||
private _params?: SaveDialogParams;
|
||||
private _saving: boolean;
|
||||
|
||||
|
||||
@ -0,0 +1,152 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import "@polymer/paper-spinner/paper-spinner";
|
||||
import "@polymer/paper-dialog/paper-dialog";
|
||||
// This is not a duplicate import, one is for types, one is for element.
|
||||
// tslint:disable-next-line
|
||||
import { PaperDialogElement } from "@polymer/paper-dialog/paper-dialog";
|
||||
import "@polymer/paper-button/paper-button";
|
||||
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
||||
import "./hui-lovelace-editor";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceConfig } from "../../../../data/lovelace";
|
||||
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
|
||||
import { Lovelace } from "../../types";
|
||||
|
||||
export class HuiDialogEditLovelace extends hassLocalizeLitMixin(LitElement) {
|
||||
public hass?: HomeAssistant;
|
||||
private _lovelace?: Lovelace;
|
||||
private _config?: LovelaceConfig;
|
||||
private _saving: boolean;
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return {
|
||||
hass: {},
|
||||
_lovelace: {},
|
||||
};
|
||||
}
|
||||
|
||||
protected constructor() {
|
||||
super();
|
||||
this._saving = false;
|
||||
}
|
||||
|
||||
public async showDialog(lovelace: Lovelace): Promise<void> {
|
||||
this._lovelace = lovelace;
|
||||
if (this._dialog == null) {
|
||||
await this.updateComplete;
|
||||
}
|
||||
|
||||
const { views, ...lovelaceConfig } = this._lovelace!.config;
|
||||
this._config = lovelaceConfig as LovelaceConfig;
|
||||
|
||||
this._dialog.open();
|
||||
}
|
||||
|
||||
private get _dialog(): PaperDialogElement {
|
||||
return this.shadowRoot!.querySelector("paper-dialog")!;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
${this.renderStyle()}
|
||||
<paper-dialog with-backdrop>
|
||||
<h2>Edit Lovelace</h2>
|
||||
<paper-dialog-scrollable>
|
||||
<hui-lovelace-editor
|
||||
.hass="${this.hass}"
|
||||
.config="${this._config}"
|
||||
@lovelace-config-changed="${this._ConfigChanged}"
|
||||
></hui-lovelace-editor
|
||||
></paper-dialog-scrollable>
|
||||
<div class="paper-dialog-buttons">
|
||||
<paper-button @click="${this._closeDialog}"
|
||||
>${this.localize("ui.common.cancel")}</paper-button
|
||||
>
|
||||
<paper-button
|
||||
?disabled="${!this._config || this._saving}"
|
||||
@click="${this._save}"
|
||||
>
|
||||
<paper-spinner
|
||||
?active="${this._saving}"
|
||||
alt="Saving"
|
||||
></paper-spinner>
|
||||
${this.localize("ui.common.save")}</paper-button
|
||||
>
|
||||
</div>
|
||||
</paper-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _closeDialog(): void {
|
||||
this._config = undefined;
|
||||
this._dialog.close();
|
||||
}
|
||||
|
||||
private async _save(): Promise<void> {
|
||||
if (!this._config) {
|
||||
return;
|
||||
}
|
||||
if (!this._isConfigChanged()) {
|
||||
this._closeDialog();
|
||||
return;
|
||||
}
|
||||
|
||||
this._saving = true;
|
||||
const lovelace = this._lovelace!;
|
||||
|
||||
const config: LovelaceConfig = {
|
||||
...lovelace.config,
|
||||
...this._config,
|
||||
};
|
||||
|
||||
try {
|
||||
await lovelace.saveConfig(config);
|
||||
this._closeDialog();
|
||||
} catch (err) {
|
||||
alert(`Saving failed: ${err.message}`);
|
||||
} finally {
|
||||
this._saving = false;
|
||||
}
|
||||
}
|
||||
|
||||
private _ConfigChanged(ev: CustomEvent): void {
|
||||
if (ev.detail && ev.detail.config) {
|
||||
this._config = ev.detail.config;
|
||||
}
|
||||
}
|
||||
|
||||
private _isConfigChanged(): boolean {
|
||||
const { views, ...lovelaceConfig } = this._lovelace!.config;
|
||||
return JSON.stringify(this._config) !== JSON.stringify(lovelaceConfig);
|
||||
}
|
||||
|
||||
private renderStyle(): TemplateResult {
|
||||
return html`
|
||||
<style>
|
||||
paper-dialog {
|
||||
width: 650px;
|
||||
}
|
||||
paper-button paper-spinner {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
paper-spinner {
|
||||
display: none;
|
||||
}
|
||||
paper-spinner[active] {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-dialog-edit-lovelace": HuiDialogEditLovelace;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-dialog-edit-lovelace", HuiDialogEditLovelace);
|
||||
@ -0,0 +1,80 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
|
||||
import { EditorTarget } from "../types";
|
||||
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { configElementStyle } from "../config-elements/config-elements-style";
|
||||
|
||||
import { LovelaceConfig } from "../../../../data/lovelace";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
"lovelace-config-changed": {
|
||||
config: LovelaceConfig;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class HuiLovelaceEditor extends hassLocalizeLitMixin(LitElement) {
|
||||
static get properties(): PropertyDeclarations {
|
||||
return { hass: {}, config: {} };
|
||||
}
|
||||
|
||||
public hass?: HomeAssistant;
|
||||
public config?: LovelaceConfig;
|
||||
|
||||
get _title(): string {
|
||||
if (!this.config) {
|
||||
return "";
|
||||
}
|
||||
return this.config.title || "";
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
${configElementStyle}
|
||||
<div class="card-config">
|
||||
<paper-input
|
||||
label="Title"
|
||||
.value="${this._title}"
|
||||
.configValue="${"title"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: Event): void {
|
||||
if (!this.config) {
|
||||
return;
|
||||
}
|
||||
|
||||
const target = ev.currentTarget! as EditorTarget;
|
||||
|
||||
if (this[`_${target.configValue}`] === target.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
let newConfig;
|
||||
|
||||
if (target.configValue) {
|
||||
newConfig = {
|
||||
...this.config,
|
||||
[target.configValue]: target.value,
|
||||
};
|
||||
}
|
||||
|
||||
fireEvent(this, "lovelace-config-changed", { config: newConfig });
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-lovelace-editor": HuiLovelaceEditor;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-lovelace-editor", HuiLovelaceEditor);
|
||||
@ -0,0 +1,32 @@
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { Lovelace } from "../../types";
|
||||
|
||||
declare global {
|
||||
// for fire event
|
||||
interface HASSDomEvents {
|
||||
"show-edit-lovelace": Lovelace;
|
||||
}
|
||||
}
|
||||
|
||||
let registeredDialog = false;
|
||||
const dialogShowEvent = "show-edit-lovelace";
|
||||
const dialogTag = "hui-dialog-edit-lovelace";
|
||||
|
||||
const registerEditLovelaceDialog = (element: HTMLElement) =>
|
||||
fireEvent(element, "register-dialog", {
|
||||
dialogShowEvent,
|
||||
dialogTag,
|
||||
dialogImport: () =>
|
||||
import(/* webpackChunkName: "hui-dialog-edit-lovelace" */ "./hui-dialog-edit-lovelace"),
|
||||
});
|
||||
|
||||
export const showEditLovelaceDialog = (
|
||||
element: HTMLElement,
|
||||
lovelace: Lovelace
|
||||
) => {
|
||||
if (!registeredDialog) {
|
||||
registeredDialog = true;
|
||||
registerEditLovelaceDialog(element);
|
||||
}
|
||||
fireEvent(element, dialogShowEvent, lovelace);
|
||||
};
|
||||
@ -26,7 +26,8 @@ export const showSaveDialog = (
|
||||
fireEvent(element, "register-dialog", {
|
||||
dialogShowEvent,
|
||||
dialogTag,
|
||||
dialogImport: () => import("./hui-dialog-save-config"),
|
||||
dialogImport: () =>
|
||||
import(/* webpackChunkName: "hui-dialog-save-config" */ "./hui-dialog-save-config"),
|
||||
});
|
||||
}
|
||||
fireEvent(element, dialogShowEvent, saveDialogParams);
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
import { LovelaceCardConfig, LovelaceViewConfig } from "../../../data/lovelace";
|
||||
import {
|
||||
LovelaceCardConfig,
|
||||
LovelaceViewConfig,
|
||||
ActionConfig,
|
||||
} from "../../../data/lovelace";
|
||||
import { EntityConfig } from "../entity-rows/types";
|
||||
import { InputType } from "zlib";
|
||||
import { struct } from "../common/structs/struct";
|
||||
|
||||
export interface YamlChangedEvent extends Event {
|
||||
detail: {
|
||||
@ -37,8 +42,16 @@ export interface EditorTarget extends EventTarget {
|
||||
checked?: boolean;
|
||||
configValue?: string;
|
||||
type?: InputType;
|
||||
config: ActionConfig;
|
||||
}
|
||||
|
||||
export interface CardPickTarget extends EventTarget {
|
||||
type: string;
|
||||
}
|
||||
|
||||
export const actionConfigStruct = struct({
|
||||
action: "string",
|
||||
navigation_path: "string?",
|
||||
service: "string?",
|
||||
service_data: "object?",
|
||||
});
|
||||
|
||||
@ -30,7 +30,7 @@ import { deleteView, addView, replaceView } from "../config-util";
|
||||
export class HuiEditView extends hassLocalizeLitMixin(LitElement) {
|
||||
public lovelace?: Lovelace;
|
||||
public viewIndex?: number;
|
||||
protected hass?: HomeAssistant;
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: LovelaceViewConfig;
|
||||
private _badges?: EntityConfig[];
|
||||
private _cards?: LovelaceCardConfig[];
|
||||
|
||||
@ -69,19 +69,19 @@ export class HuiViewEditor extends hassLocalizeLitMixin(LitElement) {
|
||||
<div class="card-config">
|
||||
<paper-input
|
||||
label="Title"
|
||||
value="${this._title}"
|
||||
.value="${this._title}"
|
||||
.configValue="${"title"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
<paper-input
|
||||
label="Icon"
|
||||
value="${this._icon}"
|
||||
.value="${this._icon}"
|
||||
.configValue="${"icon"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
<paper-input
|
||||
label="URL Path"
|
||||
value="${this._path}"
|
||||
.value="${this._path}"
|
||||
.configValue="${"path"}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input>
|
||||
|
||||
@ -26,7 +26,8 @@ const registerEditViewDialog = (element: HTMLElement) =>
|
||||
fireEvent(element, "register-dialog", {
|
||||
dialogShowEvent,
|
||||
dialogTag,
|
||||
dialogImport: () => import("./hui-dialog-edit-view"),
|
||||
dialogImport: () =>
|
||||
import(/* webpackChunkName: "hui-dialog-edit-view" */ "./hui-dialog-edit-view"),
|
||||
});
|
||||
|
||||
export const showEditViewDialog = (
|
||||
|
||||
@ -31,12 +31,22 @@ class HuiClimateEntityRow extends LitElement implements EntityRow {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const stateObj = this.hass.states[this._config.entity];
|
||||
|
||||
if (!stateObj) {
|
||||
return html`
|
||||
<hui-error-entity-row
|
||||
.entity="${this._config.entity}"
|
||||
></hui-error-entity-row>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
${this.renderStyle()}
|
||||
<hui-generic-entity-row .hass="${this.hass}" .config="${this._config}">
|
||||
<ha-climate-state
|
||||
.hass="${this.hass}"
|
||||
.stateObj="${this.hass.states[this._config.entity]}"
|
||||
.stateObj="${stateObj}"
|
||||
></ha-climate-state>
|
||||
</hui-generic-entity-row>
|
||||
`;
|
||||
|
||||
@ -29,7 +29,7 @@ export type EntityRowConfig =
|
||||
| WeblinkConfig
|
||||
| CallServiceConfig;
|
||||
|
||||
export interface EntityRow {
|
||||
export interface EntityRow extends HTMLElement {
|
||||
hass?: HomeAssistant;
|
||||
setConfig(config: EntityRowConfig);
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { LitElement, html } from "@polymer/lit-element";
|
||||
import { classMap } from "lit-html/directives/classMap";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import yaml from "js-yaml";
|
||||
|
||||
@ -7,20 +8,37 @@ import "@polymer/app-layout/app-header/app-header";
|
||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||
import "@polymer/paper-button/paper-button";
|
||||
import "@polymer/paper-icon-button/paper-icon-button";
|
||||
import "@polymer/paper-spinner/paper-spinner";
|
||||
|
||||
import { struct } from "./common/structs/struct";
|
||||
import { Lovelace } from "./types";
|
||||
import { hassLocalizeLitMixin } from "../../mixins/lit-localize-mixin";
|
||||
|
||||
import "../../components/ha-icon";
|
||||
|
||||
const TAB_INSERT = " ";
|
||||
|
||||
const lovelaceStruct = struct.partial({
|
||||
title: "string?",
|
||||
views: ["object"],
|
||||
});
|
||||
|
||||
class LovelaceFullConfigEditor extends hassLocalizeLitMixin(LitElement) {
|
||||
public lovelace?: Lovelace;
|
||||
public closeEditor?: () => void;
|
||||
private _haStyle?: DocumentFragment;
|
||||
private _saving?: boolean;
|
||||
private _changed?: boolean;
|
||||
private _hashAdded?: boolean;
|
||||
private _hash?: boolean;
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
lovelace: {},
|
||||
_saving: {},
|
||||
_changed: {},
|
||||
_hashAdded: {},
|
||||
_hash: {},
|
||||
};
|
||||
}
|
||||
|
||||
@ -32,10 +50,26 @@ class LovelaceFullConfigEditor extends hassLocalizeLitMixin(LitElement) {
|
||||
<app-toolbar>
|
||||
<paper-icon-button
|
||||
icon="hass:close"
|
||||
@click="${this.closeEditor}"
|
||||
@click="${this._closeEditor}"
|
||||
></paper-icon-button>
|
||||
<div main-title>Edit Config</div>
|
||||
${
|
||||
this._hash
|
||||
? html`
|
||||
<span class="comments">Comments will be not be saved!</span>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
<paper-button @click="${this._handleSave}">Save</paper-button>
|
||||
<ha-icon
|
||||
class="save-button
|
||||
${
|
||||
classMap({
|
||||
saved: this._saving! === false || this._changed === true,
|
||||
})
|
||||
}"
|
||||
icon="${this._changed ? "hass:circle-medium" : "hass:check"}"
|
||||
></ha-icon>
|
||||
</app-toolbar>
|
||||
</app-header>
|
||||
<div class="content">
|
||||
@ -44,6 +78,7 @@ class LovelaceFullConfigEditor extends hassLocalizeLitMixin(LitElement) {
|
||||
autocorrect="off"
|
||||
autocapitalize="off"
|
||||
spellcheck="false"
|
||||
@input="${this._yamlChanged}"
|
||||
></textarea>
|
||||
</div>
|
||||
</app-header-layout>
|
||||
@ -54,6 +89,11 @@ class LovelaceFullConfigEditor extends hassLocalizeLitMixin(LitElement) {
|
||||
const textArea = this.textArea;
|
||||
textArea.value = yaml.safeDump(this.lovelace!.config);
|
||||
textArea.addEventListener("keydown", (e) => {
|
||||
if (e.keyCode === 51) {
|
||||
this._hashAdded = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.keyCode !== 9) {
|
||||
return;
|
||||
}
|
||||
@ -90,10 +130,17 @@ class LovelaceFullConfigEditor extends hassLocalizeLitMixin(LitElement) {
|
||||
app-header-layout {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
paper-button {
|
||||
font-size: 16px;
|
||||
}
|
||||
app-toolbar {
|
||||
background-color: var(--dark-background-color, #455a64);
|
||||
color: var(--dark-text-color);
|
||||
}
|
||||
|
||||
.comments {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.content {
|
||||
height: calc(100vh - 68px);
|
||||
@ -110,20 +157,80 @@ class LovelaceFullConfigEditor extends hassLocalizeLitMixin(LitElement) {
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.save-button {
|
||||
opacity: 0;
|
||||
margin-left: -16px;
|
||||
margin-top: -4px;
|
||||
transition: opacity 1.5s;
|
||||
}
|
||||
|
||||
.saved {
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
}
|
||||
|
||||
private _handleSave() {
|
||||
private _closeEditor() {
|
||||
if (this._changed) {
|
||||
if (
|
||||
!confirm("You have unsafed changes, are you sure you want to exit?")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
window.onbeforeunload = null;
|
||||
this.closeEditor!();
|
||||
}
|
||||
|
||||
private async _handleSave() {
|
||||
this._saving = true;
|
||||
|
||||
if (this._hashAdded) {
|
||||
if (
|
||||
!confirm(
|
||||
"Your config might contain comments, these will not be saved. Do you want to continue?"
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let value;
|
||||
try {
|
||||
value = yaml.safeLoad(this.textArea.value);
|
||||
} catch (err) {
|
||||
alert(`Unable to parse YAML: ${err}`);
|
||||
this._saving = false;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
value = lovelaceStruct(value);
|
||||
} catch (err) {
|
||||
alert(`Your config is not valid: ${err}`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await this.lovelace!.saveConfig(value);
|
||||
} catch (err) {
|
||||
alert(`Unable to save YAML: ${err}`);
|
||||
}
|
||||
window.onbeforeunload = null;
|
||||
this._saving = false;
|
||||
this._changed = false;
|
||||
this._hashAdded = false;
|
||||
}
|
||||
|
||||
this.lovelace!.saveConfig(value);
|
||||
private _yamlChanged() {
|
||||
this._hash = this._hashAdded || this.textArea.value.includes("#");
|
||||
if (this._changed) {
|
||||
return;
|
||||
}
|
||||
window.onbeforeunload = () => {
|
||||
return true;
|
||||
};
|
||||
this._changed = true;
|
||||
}
|
||||
|
||||
private get textArea(): HTMLTextAreaElement {
|
||||
@ -131,4 +238,10 @@ class LovelaceFullConfigEditor extends hassLocalizeLitMixin(LitElement) {
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-editor": LovelaceFullConfigEditor;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-editor", LovelaceFullConfigEditor);
|
||||
|
||||
@ -1,454 +0,0 @@
|
||||
import "@polymer/app-layout/app-header-layout/app-header-layout";
|
||||
import "@polymer/app-layout/app-header/app-header";
|
||||
import "@polymer/app-layout/app-scroll-effects/effects/waterfall";
|
||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||
import "@polymer/app-route/app-route";
|
||||
import "@polymer/paper-icon-button/paper-icon-button";
|
||||
import "@polymer/paper-button/paper-button";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import "@polymer/paper-menu-button/paper-menu-button";
|
||||
import "@polymer/paper-tabs/paper-tab";
|
||||
import "@polymer/paper-tabs/paper-tabs";
|
||||
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
|
||||
import scrollToTarget from "../../common/dom/scroll-to-target";
|
||||
|
||||
import EventsMixin from "../../mixins/events-mixin";
|
||||
import localizeMixin from "../../mixins/localize-mixin";
|
||||
import NavigateMixin from "../../mixins/navigate-mixin";
|
||||
|
||||
import "../../layouts/ha-app-layout";
|
||||
import "../../components/ha-start-voice-button";
|
||||
import "../../components/ha-icon";
|
||||
import { loadModule, loadCSS, loadJS } from "../../common/dom/load_resource";
|
||||
import { subscribeNotifications } from "../../data/ws-notifications";
|
||||
import { computeNotifications } from "./common/compute-notifications";
|
||||
import "./components/notifications/hui-notification-drawer";
|
||||
import "./components/notifications/hui-notifications-button";
|
||||
import "./hui-unused-entities";
|
||||
import "./hui-view";
|
||||
import debounce from "../../common/util/debounce";
|
||||
import createCardElement from "./common/create-card-element";
|
||||
import { showEditViewDialog } from "./editor/view-editor/show-edit-view-dialog";
|
||||
|
||||
// CSS and JS should only be imported once. Modules and HTML are safe.
|
||||
const CSS_CACHE = {};
|
||||
const JS_CACHE = {};
|
||||
|
||||
class HUIRoot extends NavigateMixin(
|
||||
EventsMixin(localizeMixin(PolymerElement))
|
||||
) {
|
||||
static get template() {
|
||||
return html`
|
||||
<style include='ha-style'>
|
||||
:host {
|
||||
-ms-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
ha-app-layout {
|
||||
min-height: 100%;
|
||||
}
|
||||
paper-tabs {
|
||||
margin-left: 12px;
|
||||
--paper-tabs-selection-bar-color: var(--text-primary-color, #FFF);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
paper-tab.iron-selected .edit-view-icon{
|
||||
display: inline-flex;
|
||||
}
|
||||
.edit-view-icon {
|
||||
padding-left: 8px;
|
||||
display: none;
|
||||
}
|
||||
#add-view {
|
||||
position: absolute;
|
||||
height: 44px;
|
||||
}
|
||||
#add-view ha-icon {
|
||||
background-color: var(--accent-color);
|
||||
border-radius: 5px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
app-toolbar a {
|
||||
color: var(--text-primary-color, white);
|
||||
}
|
||||
paper-button.warning:not([disabled]) {
|
||||
color: var(--google-red-500);
|
||||
}
|
||||
#view {
|
||||
min-height: calc(100vh - 112px);
|
||||
/**
|
||||
* Since we only set min-height, if child nodes need percentage
|
||||
* heights they must use absolute positioning so we need relative
|
||||
* positioning here.
|
||||
*
|
||||
* https://www.w3.org/TR/CSS2/visudet.html#the-height-property
|
||||
*/
|
||||
position: relative;
|
||||
}
|
||||
#view.tabs-hidden {
|
||||
min-height: calc(100vh - 64px);
|
||||
}
|
||||
paper-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
<app-route route="[[route]]" pattern="/:view" data="{{routeData}}"></app-route>
|
||||
<hui-notification-drawer
|
||||
hass="[[hass]]"
|
||||
notifications="[[_notifications]]"
|
||||
open="{{notificationsOpen}}"
|
||||
narrow="[[narrow]]"
|
||||
></hui-notification-drawer>
|
||||
<ha-app-layout id="layout">
|
||||
<app-header slot="header" effects="waterfall" fixed condenses>
|
||||
<template is='dom-if' if="[[!_editMode]]">
|
||||
<app-toolbar>
|
||||
<ha-menu-button narrow='[[narrow]]' show-menu='[[showMenu]]'></ha-menu-button>
|
||||
<div main-title>[[_computeTitle(config)]]</div>
|
||||
<hui-notifications-button
|
||||
hass="[[hass]]"
|
||||
notifications-open="{{notificationsOpen}}"
|
||||
notifications="[[_notifications]]"
|
||||
></hui-notifications-button>
|
||||
<ha-start-voice-button hass="[[hass]]"></ha-start-voice-button>
|
||||
<paper-menu-button
|
||||
no-animations
|
||||
horizontal-align="right"
|
||||
horizontal-offset="-5"
|
||||
>
|
||||
<paper-icon-button icon="hass:dots-vertical" slot="dropdown-trigger"></paper-icon-button>
|
||||
<paper-listbox on-iron-select="_deselect" slot="dropdown-content">
|
||||
<template is='dom-if' if="[[_yamlMode]]">
|
||||
<paper-item on-click="_handleRefresh">Refresh</paper-item>
|
||||
</template>
|
||||
<paper-item on-click="_handleUnusedEntities">Unused entities</paper-item>
|
||||
<paper-item on-click="_editModeEnable">[[localize("ui.panel.lovelace.editor.configure_ui")]]</paper-item>
|
||||
<template is='dom-if' if="[[_storageMode]]">
|
||||
<paper-item on-click="_handleFullEditor">Raw config editor</paper-item>
|
||||
</template>
|
||||
<paper-item on-click="_handleHelp">Help</paper-item>
|
||||
</paper-listbox>
|
||||
</paper-menu-button>
|
||||
</app-toolbar>
|
||||
</template>
|
||||
<template is='dom-if' if="[[_editMode]]">
|
||||
<app-toolbar>
|
||||
<paper-icon-button
|
||||
icon='hass:close'
|
||||
on-click='_editModeDisable'
|
||||
></paper-icon-button>
|
||||
<div main-title>[[localize("ui.panel.lovelace.editor.header")]]</div>
|
||||
</app-toolbar>
|
||||
</template>
|
||||
|
||||
<div sticky hidden$="[[_computeTabsHidden(config.views, _editMode)]]">
|
||||
<paper-tabs scrollable selected="[[_curView]]" on-iron-activate="_handleViewSelected">
|
||||
<template is="dom-repeat" items="[[config.views]]">
|
||||
<paper-tab>
|
||||
<template is="dom-if" if="[[item.icon]]">
|
||||
<ha-icon title$="[[item.title]]" icon="[[item.icon]]"></ha-icon>
|
||||
</template>
|
||||
<template is="dom-if" if="[[!item.icon]]">
|
||||
[[_computeTabTitle(item.title)]]
|
||||
</template>
|
||||
<template is='dom-if' if="[[_editMode]]">
|
||||
<ha-icon class="edit-view-icon" on-click="_editView" icon="hass:pencil"></ha-icon>
|
||||
</template>
|
||||
</paper-tab>
|
||||
</template>
|
||||
<template is='dom-if' if="[[_editMode]]">
|
||||
<paper-button id="add-view" on-click="_addView">
|
||||
<ha-icon title=[[localize("ui.panel.lovelace.editor.edit_view.add")]] icon="hass:plus"></ha-icon>
|
||||
</paper-button>
|
||||
</template>
|
||||
</paper-tabs>
|
||||
</div>
|
||||
</app-header>
|
||||
<div id='view' on-rebuild-view='_debouncedConfigChanged'></div>
|
||||
</app-header-layout>
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
narrow: Boolean,
|
||||
showMenu: Boolean,
|
||||
hass: { type: Object, observer: "_hassChanged" },
|
||||
config: {
|
||||
type: Object,
|
||||
computed: "_computeConfig(lovelace)",
|
||||
observer: "_configChanged",
|
||||
},
|
||||
lovelace: { type: Object },
|
||||
columns: { type: Number, observer: "_columnsChanged" },
|
||||
_curView: { type: Number, value: 0 },
|
||||
route: { type: Object, observer: "_routeChanged" },
|
||||
notificationsOpen: { type: Boolean, value: false },
|
||||
_persistentNotifications: { type: Array, value: [] },
|
||||
_notifications: {
|
||||
type: Array,
|
||||
computed: "_updateNotifications(hass.states, _persistentNotifications)",
|
||||
},
|
||||
_yamlMode: {
|
||||
type: Boolean,
|
||||
computed: "_computeYamlMode(lovelace)",
|
||||
},
|
||||
_storageMode: {
|
||||
type: Boolean,
|
||||
computed: "_computeStorageMode(lovelace)",
|
||||
},
|
||||
_editMode: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
computed: "_computeEditMode(lovelace)",
|
||||
observer: "_editModeChanged",
|
||||
},
|
||||
routeData: Object,
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._debouncedConfigChanged = debounce(
|
||||
() => this._selectView(this._curView),
|
||||
100
|
||||
);
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._unsubNotifications = subscribeNotifications(
|
||||
this.hass.connection,
|
||||
(notifications) => {
|
||||
this._persistentNotifications = notifications;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
if (typeof this._unsubNotifications === "function") {
|
||||
this._unsubNotifications();
|
||||
}
|
||||
}
|
||||
|
||||
_updateNotifications(states, persistent) {
|
||||
if (!states) return persistent;
|
||||
|
||||
const configurator = computeNotifications(states);
|
||||
return persistent.concat(configurator);
|
||||
}
|
||||
|
||||
_routeChanged(route) {
|
||||
const views = this.config && this.config.views;
|
||||
if (route.path === "" && route.prefix === "/lovelace" && views) {
|
||||
this.navigate(`/lovelace/${views[0].path || 0}`, true);
|
||||
} else if (this.routeData.view) {
|
||||
const view = this.routeData.view;
|
||||
let index = 0;
|
||||
for (let i = 0; i < views.length; i++) {
|
||||
if (views[i].path === view || i === parseInt(view)) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index !== this._curView) this._selectView(index);
|
||||
}
|
||||
}
|
||||
|
||||
_computeViewPath(path, index) {
|
||||
return path || index;
|
||||
}
|
||||
|
||||
_computeTitle(config) {
|
||||
return config.title || "Home Assistant";
|
||||
}
|
||||
|
||||
_computeTabsHidden(views, editMode) {
|
||||
return views.length < 2 && !editMode;
|
||||
}
|
||||
|
||||
_computeTabTitle(title) {
|
||||
return title || "Unnamed view";
|
||||
}
|
||||
|
||||
_handleRefresh() {
|
||||
this.fire("config-refresh");
|
||||
}
|
||||
|
||||
_handleUnusedEntities() {
|
||||
this._selectView("unused");
|
||||
}
|
||||
|
||||
_deselect(ev) {
|
||||
ev.target.selected = null;
|
||||
}
|
||||
|
||||
_handleHelp() {
|
||||
window.open("https://www.home-assistant.io/lovelace/", "_blank");
|
||||
}
|
||||
|
||||
_handleFullEditor() {
|
||||
this.lovelace.enableFullEditMode();
|
||||
}
|
||||
|
||||
_editModeEnable() {
|
||||
if (this._yamlMode) {
|
||||
window.alert("The edit UI is not available when in YAML mode.");
|
||||
return;
|
||||
}
|
||||
this.lovelace.setEditMode(true);
|
||||
if (this.config.views.length < 2) {
|
||||
this.$.view.classList.remove("tabs-hidden");
|
||||
this.fire("iron-resize");
|
||||
}
|
||||
}
|
||||
|
||||
_editModeDisable() {
|
||||
this.lovelace.setEditMode(false);
|
||||
if (this.config.views.length < 2) {
|
||||
this.$.view.classList.add("tabs-hidden");
|
||||
this.fire("iron-resize");
|
||||
}
|
||||
}
|
||||
|
||||
_editModeChanged() {
|
||||
this._selectView(this._curView);
|
||||
}
|
||||
|
||||
_editView() {
|
||||
showEditViewDialog(this, {
|
||||
lovelace: this.lovelace,
|
||||
viewIndex: this._curView,
|
||||
});
|
||||
}
|
||||
|
||||
_addView() {
|
||||
showEditViewDialog(this, {
|
||||
lovelace: this.lovelace,
|
||||
});
|
||||
}
|
||||
|
||||
_handleViewSelected(ev) {
|
||||
const index = ev.detail.selected;
|
||||
this._navigateView(index);
|
||||
}
|
||||
|
||||
_navigateView(viewIndex) {
|
||||
if (viewIndex !== this._curView) {
|
||||
const path = this.config.views[viewIndex].path || viewIndex;
|
||||
this.navigate(`/lovelace/${path}`);
|
||||
}
|
||||
scrollToTarget(this, this.$.layout.header.scrollTarget);
|
||||
}
|
||||
|
||||
_selectView(viewIndex) {
|
||||
this._curView = viewIndex;
|
||||
|
||||
// Recreate a new element to clear the applied themes.
|
||||
const root = this.$.view;
|
||||
if (root.lastChild) {
|
||||
root.removeChild(root.lastChild);
|
||||
}
|
||||
|
||||
let view;
|
||||
let background = this.config.background || "";
|
||||
|
||||
if (viewIndex === "unused") {
|
||||
view = document.createElement("hui-unused-entities");
|
||||
view.setConfig(this.config);
|
||||
} else {
|
||||
const viewConfig = this.config.views[this._curView];
|
||||
if (!viewConfig) {
|
||||
this._editModeEnable();
|
||||
return;
|
||||
}
|
||||
if (viewConfig.panel) {
|
||||
view = createCardElement(viewConfig.cards[0]);
|
||||
view.isPanel = true;
|
||||
} else {
|
||||
view = document.createElement("hui-view");
|
||||
view.lovelace = this.lovelace;
|
||||
view.config = viewConfig;
|
||||
view.columns = this.columns;
|
||||
view.index = viewIndex;
|
||||
}
|
||||
if (viewConfig.background) background = viewConfig.background;
|
||||
}
|
||||
|
||||
this.$.view.style.background = background;
|
||||
|
||||
view.hass = this.hass;
|
||||
root.appendChild(view);
|
||||
}
|
||||
|
||||
_hassChanged(hass) {
|
||||
if (!this.$.view.lastChild) return;
|
||||
this.$.view.lastChild.hass = hass;
|
||||
}
|
||||
|
||||
_configChanged(config) {
|
||||
this._loadResources(config.resources || []);
|
||||
// On config change, recreate the view from scratch.
|
||||
this._selectView(this._curView);
|
||||
this.$.view.classList.toggle("tabs-hidden", config.views.length < 2);
|
||||
}
|
||||
|
||||
_columnsChanged(columns) {
|
||||
if (!this.$.view.lastChild) return;
|
||||
this.$.view.lastChild.columns = columns;
|
||||
}
|
||||
|
||||
_loadResources(resources) {
|
||||
resources.forEach((resource) => {
|
||||
switch (resource.type) {
|
||||
case "css":
|
||||
if (resource.url in CSS_CACHE) break;
|
||||
CSS_CACHE[resource.url] = loadCSS(resource.url);
|
||||
break;
|
||||
|
||||
case "js":
|
||||
if (resource.url in JS_CACHE) break;
|
||||
JS_CACHE[resource.url] = loadJS(resource.url);
|
||||
break;
|
||||
|
||||
case "module":
|
||||
loadModule(resource.url);
|
||||
break;
|
||||
|
||||
case "html":
|
||||
import(/* webpackChunkName: "import-href-polyfill" */ "../../resources/html-import/import-href").then(
|
||||
({ importHref }) => importHref(resource.url)
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
// eslint-disable-next-line
|
||||
console.warn("Unknown resource type specified: ${resource.type}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_computeConfig(lovelace) {
|
||||
return lovelace ? lovelace.config : null;
|
||||
}
|
||||
|
||||
_computeYamlMode(lovelace) {
|
||||
return lovelace ? lovelace.mode === "yaml" : false;
|
||||
}
|
||||
|
||||
_computeStorageMode(lovelace) {
|
||||
return lovelace ? lovelace.mode === "storage" : false;
|
||||
}
|
||||
|
||||
_computeEditMode(lovelace) {
|
||||
return lovelace ? lovelace.editMode : false;
|
||||
}
|
||||
}
|
||||
customElements.define("hui-root", HUIRoot);
|
||||
665
src/panels/lovelace/hui-root.ts
Normal file
665
src/panels/lovelace/hui-root.ts
Normal file
@ -0,0 +1,665 @@
|
||||
import {
|
||||
html,
|
||||
LitElement,
|
||||
PropertyDeclarations,
|
||||
PropertyValues,
|
||||
} from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import { classMap } from "lit-html/directives/classMap";
|
||||
import "@polymer/app-layout/app-header-layout/app-header-layout";
|
||||
import "@polymer/app-layout/app-header/app-header";
|
||||
import "@polymer/app-layout/app-scroll-effects/effects/waterfall";
|
||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||
import "@polymer/app-route/app-route";
|
||||
import "@polymer/paper-icon-button/paper-icon-button";
|
||||
import "@polymer/paper-button/paper-button";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import "@polymer/paper-menu-button/paper-menu-button";
|
||||
import "@polymer/paper-tabs/paper-tab";
|
||||
import "@polymer/paper-tabs/paper-tabs";
|
||||
import { HassEntities } from "home-assistant-js-websocket";
|
||||
|
||||
import scrollToTarget from "../../common/dom/scroll-to-target";
|
||||
|
||||
import "../../layouts/ha-app-layout";
|
||||
import "../../components/ha-start-voice-button";
|
||||
import "../../components/ha-icon";
|
||||
import { loadModule, loadCSS, loadJS } from "../../common/dom/load_resource";
|
||||
import { subscribeNotifications } from "../../data/ws-notifications";
|
||||
import debounce from "../../common/util/debounce";
|
||||
import { hassLocalizeLitMixin } from "../../mixins/lit-localize-mixin";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import { LovelaceConfig } from "../../data/lovelace";
|
||||
import { navigate } from "../../common/navigate";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { computeNotifications } from "./common/compute-notifications";
|
||||
import "./components/notifications/hui-notification-drawer";
|
||||
import "./components/notifications/hui-notifications-button";
|
||||
import "./hui-view";
|
||||
// Not a duplicate import, this one is for type
|
||||
// tslint:disable-next-line
|
||||
import { HUIView } from "./hui-view";
|
||||
import { createCardElement } from "./common/create-card-element";
|
||||
import { showEditViewDialog } from "./editor/view-editor/show-edit-view-dialog";
|
||||
import { showEditLovelaceDialog } from "./editor/lovelace-editor/show-edit-lovelace-dialog";
|
||||
import { Lovelace } from "./types";
|
||||
import { afterNextRender } from "../../common/util/render-status";
|
||||
|
||||
// CSS and JS should only be imported once. Modules and HTML are safe.
|
||||
const CSS_CACHE = {};
|
||||
const JS_CACHE = {};
|
||||
|
||||
declare global {
|
||||
// tslint:disable-next-line
|
||||
interface HASSDomEvents {
|
||||
"rebuild-view": {};
|
||||
}
|
||||
}
|
||||
|
||||
let loadedUnusedEntities = false;
|
||||
|
||||
class HUIRoot extends hassLocalizeLitMixin(LitElement) {
|
||||
public narrow?: boolean;
|
||||
public showMenu?: boolean;
|
||||
public hass?: HomeAssistant;
|
||||
public lovelace?: Lovelace;
|
||||
public columns?: number;
|
||||
public route?: { path: string; prefix: string };
|
||||
private _routeData?: { view: string };
|
||||
private _curView?: number | "hass-unused-entities";
|
||||
private _notificationsOpen: boolean;
|
||||
private _persistentNotifications?: Notification[];
|
||||
private _haStyle?: DocumentFragment;
|
||||
private _viewCache?: { [viewId: string]: HUIView };
|
||||
|
||||
private _debouncedConfigChanged: () => void;
|
||||
private _unsubNotifications?: () => void;
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return {
|
||||
narrow: {},
|
||||
showMenu: {},
|
||||
hass: {},
|
||||
lovelace: {},
|
||||
columns: {},
|
||||
route: {},
|
||||
_routeData: {},
|
||||
_curView: {},
|
||||
_notificationsOpen: {},
|
||||
_persistentNotifications: {},
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._notificationsOpen = false;
|
||||
// The view can trigger a re-render when it knows that certain
|
||||
// web components have been loaded.
|
||||
this._debouncedConfigChanged = debounce(
|
||||
() => this._selectView(this._curView, true),
|
||||
100
|
||||
);
|
||||
}
|
||||
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
this._unsubNotifications = subscribeNotifications(
|
||||
this.hass!.connection,
|
||||
(notifications) => {
|
||||
this._persistentNotifications = notifications;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
if (this._unsubNotifications) {
|
||||
this._unsubNotifications();
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
${this.renderStyle()}
|
||||
<app-route .route="${this.route}" pattern="/:view" data="${
|
||||
this._routeData
|
||||
}" @data-changed="${this._routeDataChanged}"></app-route>
|
||||
<hui-notification-drawer
|
||||
.hass="${this.hass}"
|
||||
.notifications="${this._notifications}"
|
||||
.open="${this._notificationsOpen}"
|
||||
@open-changed="${this._handleNotificationsOpenChanged}"
|
||||
.narrow="${this.narrow}"
|
||||
></hui-notification-drawer>
|
||||
<ha-app-layout id="layout">
|
||||
<app-header slot="header" effects="waterfall" class="${classMap({
|
||||
"edit-mode": this._editMode,
|
||||
})}" fixed condenses>
|
||||
${
|
||||
this._editMode
|
||||
? html`
|
||||
<app-toolbar class="edit-mode">
|
||||
<paper-icon-button
|
||||
icon="hass:close"
|
||||
@click="${this._editModeDisable}"
|
||||
></paper-icon-button>
|
||||
<div main-title>
|
||||
${
|
||||
this.config.title ||
|
||||
this.localize("ui.panel.lovelace.editor.header")
|
||||
}
|
||||
<paper-icon-button
|
||||
icon="hass:pencil"
|
||||
class="edit-icon"
|
||||
@click="${this._editLovelace}"
|
||||
></paper-icon-button>
|
||||
</div>
|
||||
<paper-icon-button
|
||||
icon="hass:help-circle"
|
||||
title="Help"
|
||||
@click="${this._handleHelp}"
|
||||
></paper-icon-button>
|
||||
<paper-menu-button
|
||||
no-animations
|
||||
horizontal-align="right"
|
||||
horizontal-offset="-5"
|
||||
>
|
||||
<paper-icon-button
|
||||
icon="hass:dots-vertical"
|
||||
slot="dropdown-trigger"
|
||||
></paper-icon-button>
|
||||
<paper-listbox
|
||||
@iron-select="${this._deselect}"
|
||||
slot="dropdown-content"
|
||||
>
|
||||
<paper-item @click="${this.lovelace!.enableFullEditMode}"
|
||||
>Raw config editor</paper-item
|
||||
>
|
||||
</paper-listbox>
|
||||
</paper-menu-button>
|
||||
</app-toolbar>
|
||||
`
|
||||
: html`
|
||||
<app-toolbar>
|
||||
<ha-menu-button
|
||||
.narrow="${this.narrow}"
|
||||
.showMenu="${this.showMenu}"
|
||||
></ha-menu-button>
|
||||
<div main-title>${this.config.title || "Home Assistant"}</div>
|
||||
<hui-notifications-button
|
||||
.hass="${this.hass}"
|
||||
.open="${this._notificationsOpen}"
|
||||
@open-changed="${this._handleNotificationsOpenChanged}"
|
||||
.notifications="${this._notifications}"
|
||||
></hui-notifications-button>
|
||||
<ha-start-voice-button
|
||||
.hass="${this.hass}"
|
||||
></ha-start-voice-button>
|
||||
<paper-menu-button
|
||||
no-animations
|
||||
horizontal-align="right"
|
||||
horizontal-offset="-5"
|
||||
>
|
||||
<paper-icon-button
|
||||
icon="hass:dots-vertical"
|
||||
slot="dropdown-trigger"
|
||||
></paper-icon-button>
|
||||
<paper-listbox
|
||||
@iron-select="${this._deselect}"
|
||||
slot="dropdown-content"
|
||||
>
|
||||
${
|
||||
this._yamlMode
|
||||
? html`
|
||||
<paper-item @click="${this._handleRefresh}"
|
||||
>Refresh</paper-item
|
||||
>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
<paper-item @click="${this._handleUnusedEntities}"
|
||||
>Unused entities</paper-item
|
||||
>
|
||||
<paper-item @click="${this._editModeEnable}"
|
||||
>${
|
||||
this.localize("ui.panel.lovelace.editor.configure_ui")
|
||||
}</paper-item
|
||||
>
|
||||
<paper-item @click="${this._handleHelp}">Help</paper-item>
|
||||
</paper-listbox>
|
||||
</paper-menu-button>
|
||||
</app-toolbar>
|
||||
`
|
||||
}
|
||||
|
||||
${
|
||||
this.lovelace!.config.views.length > 1 || this._editMode
|
||||
? html`
|
||||
<div sticky>
|
||||
<paper-tabs
|
||||
scrollable
|
||||
.selected="${this._curView}"
|
||||
@iron-activate="${this._handleViewSelected}"
|
||||
>
|
||||
${
|
||||
this.lovelace!.config.views.map(
|
||||
(view) => html`
|
||||
<paper-tab>
|
||||
${
|
||||
view.icon
|
||||
? html`
|
||||
<ha-icon
|
||||
title="${view.title}"
|
||||
.icon="${view.icon}"
|
||||
></ha-icon>
|
||||
`
|
||||
: view.title || "Unnamed view"
|
||||
}
|
||||
${
|
||||
this._editMode
|
||||
? html`
|
||||
<ha-icon
|
||||
class="edit-icon view"
|
||||
@click="${this._editView}"
|
||||
icon="hass:pencil"
|
||||
></ha-icon>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
</paper-tab>
|
||||
`
|
||||
)
|
||||
}
|
||||
${
|
||||
this._editMode
|
||||
? html`
|
||||
<paper-button
|
||||
id="add-view"
|
||||
@click="${this._addView}"
|
||||
>
|
||||
<ha-icon
|
||||
title="${
|
||||
this.localize(
|
||||
"ui.panel.lovelace.editor.edit_view.add"
|
||||
)
|
||||
}"
|
||||
icon="hass:plus"
|
||||
></ha-icon>
|
||||
</paper-button>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
</paper-tabs>
|
||||
</div>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
</app-header>
|
||||
<div id='view' class="${classMap({
|
||||
"tabs-hidden": this.lovelace!.config.views.length < 2,
|
||||
})}" @rebuild-view='${this._debouncedConfigChanged}'></div>
|
||||
</app-header-layout>
|
||||
`;
|
||||
}
|
||||
|
||||
protected renderStyle(): TemplateResult {
|
||||
if (!this._haStyle) {
|
||||
this._haStyle = document.importNode(
|
||||
(document.getElementById("ha-style")!
|
||||
.children[0] as HTMLTemplateElement).content,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
return html`
|
||||
${this._haStyle}
|
||||
<style include="ha-style">
|
||||
:host {
|
||||
-ms-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
--dark-color: #455a64;
|
||||
--text-dark-color: #fff;
|
||||
}
|
||||
|
||||
ha-app-layout {
|
||||
min-height: 100%;
|
||||
}
|
||||
paper-tabs {
|
||||
margin-left: 12px;
|
||||
--paper-tabs-selection-bar-color: var(--text-primary-color, #fff);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.edit-mode {
|
||||
background-color: var(--dark-color, #455a64);
|
||||
color: var(--text-dark-color);
|
||||
}
|
||||
.edit-mode div[main-title] {
|
||||
pointer-events: auto;
|
||||
}
|
||||
paper-tab.iron-selected .edit-icon {
|
||||
display: inline-flex;
|
||||
}
|
||||
.edit-icon {
|
||||
color: var(--accent-color);
|
||||
padding-left: 8px;
|
||||
}
|
||||
.edit-icon.view {
|
||||
display: none;
|
||||
}
|
||||
#add-view {
|
||||
position: absolute;
|
||||
height: 44px;
|
||||
}
|
||||
#add-view ha-icon {
|
||||
background-color: var(--accent-color);
|
||||
border-radius: 5px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
app-toolbar a {
|
||||
color: var(--text-primary-color, white);
|
||||
}
|
||||
paper-button.warning:not([disabled]) {
|
||||
color: var(--google-red-500);
|
||||
}
|
||||
#view {
|
||||
min-height: calc(100vh - 112px);
|
||||
/**
|
||||
* Since we only set min-height, if child nodes need percentage
|
||||
* heights they must use absolute positioning so we need relative
|
||||
* positioning here.
|
||||
*
|
||||
* https://www.w3.org/TR/CSS2/visudet.html#the-height-property
|
||||
*/
|
||||
position: relative;
|
||||
}
|
||||
#view.tabs-hidden {
|
||||
min-height: calc(100vh - 64px);
|
||||
}
|
||||
paper-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
}
|
||||
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
super.updated(changedProperties);
|
||||
|
||||
const view = this._viewRoot;
|
||||
const huiView = view.lastChild as HUIView;
|
||||
|
||||
if (changedProperties.has("columns") && huiView) {
|
||||
huiView.columns = this.columns;
|
||||
}
|
||||
|
||||
if (changedProperties.has("hass") && huiView) {
|
||||
huiView.hass = this.hass;
|
||||
}
|
||||
|
||||
let newSelectView;
|
||||
let force = false;
|
||||
|
||||
if (changedProperties.has("route")) {
|
||||
const views = this.config && this.config.views;
|
||||
if (
|
||||
this.route!.path === "" &&
|
||||
this.route!.prefix === "/lovelace" &&
|
||||
views
|
||||
) {
|
||||
navigate(this, `/lovelace/${views[0].path || 0}`, true);
|
||||
} else if (this._routeData!.view === "hass-unused-entities") {
|
||||
newSelectView = "hass-unused-entities";
|
||||
} else if (this._routeData!.view) {
|
||||
const selectedView = this._routeData!.view;
|
||||
const selectedViewInt = parseInt(selectedView, 10);
|
||||
let index = 0;
|
||||
for (let i = 0; i < views.length; i++) {
|
||||
if (views[i].path === selectedView || i === selectedViewInt) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
newSelectView = index;
|
||||
}
|
||||
}
|
||||
|
||||
if (changedProperties.has("lovelace")) {
|
||||
const oldLovelace = changedProperties.get("lovelace") as
|
||||
| Lovelace
|
||||
| undefined;
|
||||
|
||||
if (!oldLovelace || oldLovelace.config !== this.lovelace!.config) {
|
||||
this._loadResources(this.lovelace!.config.resources || []);
|
||||
// On config change, recreate the current view from scratch.
|
||||
force = true;
|
||||
}
|
||||
|
||||
if (!oldLovelace || oldLovelace.editMode !== this.lovelace!.editMode) {
|
||||
// On edit mode change, recreate the current view from scratch
|
||||
force = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (newSelectView !== undefined || force) {
|
||||
if (force && newSelectView === undefined) {
|
||||
newSelectView = this._curView;
|
||||
}
|
||||
this._selectView(newSelectView, force);
|
||||
}
|
||||
}
|
||||
|
||||
private get _notifications() {
|
||||
return this._updateNotifications(
|
||||
this.hass!.states,
|
||||
this._persistentNotifications! || []
|
||||
);
|
||||
}
|
||||
|
||||
private get config(): LovelaceConfig {
|
||||
return this.lovelace!.config;
|
||||
}
|
||||
|
||||
private get _yamlMode(): boolean {
|
||||
return this.lovelace!.mode === "yaml";
|
||||
}
|
||||
|
||||
private get _editMode() {
|
||||
return this.lovelace!.editMode;
|
||||
}
|
||||
|
||||
private get _layout(): any {
|
||||
return this.shadowRoot!.getElementById("layout");
|
||||
}
|
||||
|
||||
private get _viewRoot(): HTMLDivElement {
|
||||
return this.shadowRoot!.getElementById("view") as HTMLDivElement;
|
||||
}
|
||||
|
||||
private _routeDataChanged(ev): void {
|
||||
this._routeData = ev.detail.value;
|
||||
}
|
||||
|
||||
private _handleNotificationsOpenChanged(ev): void {
|
||||
this._notificationsOpen = ev.detail.value;
|
||||
}
|
||||
|
||||
private _updateNotifications(
|
||||
states: HassEntities,
|
||||
persistent: Array<unknown>
|
||||
): Array<unknown> {
|
||||
const configurator = computeNotifications(states);
|
||||
return persistent.concat(configurator);
|
||||
}
|
||||
|
||||
private _handleRefresh(): void {
|
||||
fireEvent(this, "config-refresh");
|
||||
}
|
||||
|
||||
private _handleUnusedEntities(): void {
|
||||
navigate(this, `/lovelace/hass-unused-entities`);
|
||||
}
|
||||
|
||||
private _deselect(ev): void {
|
||||
ev.target.selected = null;
|
||||
}
|
||||
|
||||
private _handleHelp(): void {
|
||||
window.open("https://www.home-assistant.io/lovelace/", "_blank");
|
||||
}
|
||||
|
||||
private _editModeEnable(): void {
|
||||
if (this._yamlMode) {
|
||||
window.alert("The edit UI is not available when in YAML mode.");
|
||||
return;
|
||||
}
|
||||
this.lovelace!.setEditMode(true);
|
||||
if (this.config.views.length < 2) {
|
||||
fireEvent(this, "iron-resize");
|
||||
}
|
||||
}
|
||||
|
||||
private _editModeDisable(): void {
|
||||
this.lovelace!.setEditMode(false);
|
||||
if (this.config.views.length < 2) {
|
||||
fireEvent(this, "iron-resize");
|
||||
}
|
||||
}
|
||||
|
||||
private _editLovelace() {
|
||||
showEditLovelaceDialog(this, this.lovelace!);
|
||||
}
|
||||
|
||||
private _editView() {
|
||||
showEditViewDialog(this, {
|
||||
lovelace: this.lovelace!,
|
||||
viewIndex: this._curView as number,
|
||||
});
|
||||
}
|
||||
|
||||
private _addView() {
|
||||
showEditViewDialog(this, {
|
||||
lovelace: this.lovelace!,
|
||||
});
|
||||
}
|
||||
|
||||
private _handleViewSelected(ev) {
|
||||
const viewIndex = ev.detail.selected as number;
|
||||
|
||||
if (viewIndex !== this._curView) {
|
||||
const path = this.config.views[viewIndex].path || viewIndex;
|
||||
navigate(this, `/lovelace/${path}`);
|
||||
}
|
||||
scrollToTarget(this, this._layout.header.scrollTarget);
|
||||
}
|
||||
|
||||
private async _selectView(
|
||||
viewIndex: HUIRoot["_curView"],
|
||||
force: boolean
|
||||
): Promise<void> {
|
||||
if (!force && this._curView === viewIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
viewIndex = viewIndex === undefined ? 0 : viewIndex;
|
||||
|
||||
this._curView = viewIndex;
|
||||
|
||||
if (force) {
|
||||
this._viewCache = {};
|
||||
}
|
||||
|
||||
// Recreate a new element to clear the applied themes.
|
||||
const root = this._viewRoot;
|
||||
|
||||
if (root.lastChild) {
|
||||
root.removeChild(root.lastChild);
|
||||
}
|
||||
|
||||
if (viewIndex === "hass-unused-entities") {
|
||||
if (!loadedUnusedEntities) {
|
||||
loadedUnusedEntities = true;
|
||||
await import(/* webpackChunkName: "hui-unused-entities" */ "./hui-unused-entities");
|
||||
}
|
||||
const unusedEntities = document.createElement("hui-unused-entities");
|
||||
unusedEntities.setConfig(this.config);
|
||||
unusedEntities.hass = this.hass!;
|
||||
root.style.background = this.config.background || "";
|
||||
root.appendChild(unusedEntities);
|
||||
return;
|
||||
}
|
||||
|
||||
let view;
|
||||
const viewConfig = this.config.views[viewIndex];
|
||||
|
||||
if (!viewConfig) {
|
||||
this._editModeEnable();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!force && this._viewCache![viewIndex]) {
|
||||
view = this._viewCache![viewIndex];
|
||||
} else {
|
||||
await new Promise((resolve) => afterNextRender(resolve));
|
||||
|
||||
if (viewConfig.panel && viewConfig.cards && viewConfig.cards.length > 0) {
|
||||
view = createCardElement(viewConfig.cards[0]);
|
||||
view.isPanel = true;
|
||||
} else {
|
||||
view = document.createElement("hui-view");
|
||||
view.lovelace = this.lovelace;
|
||||
view.columns = this.columns;
|
||||
view.index = viewIndex;
|
||||
}
|
||||
this._viewCache![viewIndex] = view;
|
||||
}
|
||||
|
||||
view.hass = this.hass;
|
||||
root.style.background =
|
||||
viewConfig.background || this.config.background || "";
|
||||
root.appendChild(view);
|
||||
}
|
||||
|
||||
private _loadResources(resources) {
|
||||
resources.forEach((resource) => {
|
||||
switch (resource.type) {
|
||||
case "css":
|
||||
if (resource.url in CSS_CACHE) {
|
||||
break;
|
||||
}
|
||||
CSS_CACHE[resource.url] = loadCSS(resource.url);
|
||||
break;
|
||||
|
||||
case "js":
|
||||
if (resource.url in JS_CACHE) {
|
||||
break;
|
||||
}
|
||||
JS_CACHE[resource.url] = loadJS(resource.url);
|
||||
break;
|
||||
|
||||
case "module":
|
||||
loadModule(resource.url);
|
||||
break;
|
||||
|
||||
case "html":
|
||||
import(/* webpackChunkName: "import-href-polyfill" */ "../../resources/html-import/import-href").then(
|
||||
({ importHref }) => importHref(resource.url)
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
// tslint:disable-next-line
|
||||
console.warn(`Unknown resource type specified: ${resource.type}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-root": HUIRoot;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-root", HUIRoot);
|
||||
@ -2,15 +2,16 @@ import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
|
||||
import "./cards/hui-entities-card";
|
||||
|
||||
import computeUnusedEntities from "./common/compute-unused-entities";
|
||||
import createCardElement from "./common/create-card-element";
|
||||
import { computeUnusedEntities } from "./common/compute-unused-entities";
|
||||
import { createCardElement } from "./common/create-card-element";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import { LovelaceCard } from "./types";
|
||||
import { LovelaceConfig } from "../../data/lovelace";
|
||||
|
||||
export class HuiUnusedEntities extends LitElement {
|
||||
private _hass?: HomeAssistant;
|
||||
private _config?: object;
|
||||
private _config?: LovelaceConfig;
|
||||
private _element?: LovelaceCard;
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
@ -29,10 +30,7 @@ export class HuiUnusedEntities extends LitElement {
|
||||
this._element.hass = this._hass;
|
||||
}
|
||||
|
||||
public setConfig(config: object): void {
|
||||
if (!config) {
|
||||
throw new Error("Card config incorrect");
|
||||
}
|
||||
public setConfig(config: LovelaceConfig): void {
|
||||
this._config = config;
|
||||
this._createElement();
|
||||
}
|
||||
@ -62,7 +60,7 @@ export class HuiUnusedEntities extends LitElement {
|
||||
|
||||
private _createElement(): void {
|
||||
if (this._hass) {
|
||||
const entities = computeUnusedEntities(this._hass, this._config).map(
|
||||
const entities = computeUnusedEntities(this._hass, this._config!).map(
|
||||
(entity) => ({
|
||||
entity,
|
||||
secondary_info: "entity-id",
|
||||
|
||||
3
src/panels/lovelace/hui-view-editable.ts
Normal file
3
src/panels/lovelace/hui-view-editable.ts
Normal file
@ -0,0 +1,3 @@
|
||||
// hui-view dependencies for when in edit mode.
|
||||
import "@polymer/paper-fab/paper-fab";
|
||||
import "./components/hui-card-options";
|
||||
@ -1,251 +0,0 @@
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
|
||||
import "@polymer/paper-fab/paper-fab";
|
||||
import "../../components/entity/ha-state-label-badge";
|
||||
import "./components/hui-card-options";
|
||||
|
||||
import applyThemesOnElement from "../../common/dom/apply_themes_on_element";
|
||||
|
||||
import EventsMixin from "../../mixins/events-mixin";
|
||||
import localizeMixin from "../../mixins/localize-mixin";
|
||||
import createCardElement from "./common/create-card-element";
|
||||
import { computeCardSize } from "./common/compute-card-size";
|
||||
import { showEditCardDialog } from "./editor/card-editor/show-edit-card-dialog";
|
||||
|
||||
class HUIView extends localizeMixin(EventsMixin(PolymerElement)) {
|
||||
static get template() {
|
||||
return html`
|
||||
<style>
|
||||
:host {
|
||||
display: block;
|
||||
padding: 4px 4px 0;
|
||||
transform: translateZ(0);
|
||||
position: relative;
|
||||
min-height: calc(100vh - 155px);
|
||||
}
|
||||
|
||||
#badges {
|
||||
margin: 8px 16px;
|
||||
font-size: 85%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#columns {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
max-width: 500px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.column > * {
|
||||
display: block;
|
||||
margin: 4px 4px 8px;
|
||||
}
|
||||
|
||||
paper-fab {
|
||||
position: sticky;
|
||||
float: right;
|
||||
bottom: 16px;
|
||||
right: 16px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
paper-fab[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div id="badges"></div>
|
||||
<div id="columns"></div>
|
||||
<paper-fab
|
||||
hidden$="[[!lovelace.editMode]]"
|
||||
elevated="2"
|
||||
icon="hass:plus"
|
||||
title=[[localize("ui.panel.lovelace.editor.edit_card.add")]]
|
||||
on-click="_addCard"
|
||||
></paper-fab>
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
hass: {
|
||||
type: Object,
|
||||
observer: "_hassChanged",
|
||||
},
|
||||
lovelace: Object,
|
||||
config: Object,
|
||||
columns: Number,
|
||||
editMode: Boolean,
|
||||
index: Number,
|
||||
};
|
||||
}
|
||||
|
||||
static get observers() {
|
||||
return [
|
||||
// Put all properties in 1 observer so we only call configChanged once
|
||||
"_createBadges(config)",
|
||||
"_createCards(config, columns, editMode)",
|
||||
];
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._cards = [];
|
||||
this._badges = [];
|
||||
}
|
||||
|
||||
_addCard() {
|
||||
showEditCardDialog(this, {
|
||||
lovelace: this.lovelace,
|
||||
path: [this.index],
|
||||
});
|
||||
}
|
||||
|
||||
_createBadges(config) {
|
||||
const root = this.$.badges;
|
||||
while (root.lastChild) {
|
||||
root.removeChild(root.lastChild);
|
||||
}
|
||||
|
||||
if (!config || !config.badges || !Array.isArray(config.badges)) {
|
||||
root.style.display = "none";
|
||||
this._badges = [];
|
||||
return;
|
||||
}
|
||||
|
||||
const elements = [];
|
||||
for (const entityId of config.badges) {
|
||||
if (!(entityId in this.hass.states)) continue;
|
||||
|
||||
const element = document.createElement("ha-state-label-badge");
|
||||
element.setProperties({
|
||||
hass: this.hass,
|
||||
state: this.hass.states[entityId],
|
||||
});
|
||||
elements.push({ element, entityId });
|
||||
root.appendChild(element);
|
||||
}
|
||||
this._badges = elements;
|
||||
root.style.display = elements.length > 0 ? "block" : "none";
|
||||
}
|
||||
|
||||
_createCards(config) {
|
||||
const root = this.$.columns;
|
||||
|
||||
while (root.lastChild) {
|
||||
root.removeChild(root.lastChild);
|
||||
}
|
||||
|
||||
if (!config || !config.cards || !Array.isArray(config.cards)) {
|
||||
this._cards = [];
|
||||
return;
|
||||
}
|
||||
|
||||
const elements = [];
|
||||
const elementsToAppend = [];
|
||||
config.cards.forEach((cardConfig, cardIndex) => {
|
||||
const element = createCardElement(cardConfig);
|
||||
element.hass = this.hass;
|
||||
elements.push(element);
|
||||
|
||||
if (!this.lovelace.editMode) {
|
||||
elementsToAppend.push(element);
|
||||
return;
|
||||
}
|
||||
|
||||
const wrapper = document.createElement("hui-card-options");
|
||||
wrapper.hass = this.hass;
|
||||
wrapper.lovelace = this.lovelace;
|
||||
wrapper.path = [this.index, cardIndex];
|
||||
wrapper.appendChild(element);
|
||||
elementsToAppend.push(wrapper);
|
||||
});
|
||||
|
||||
let columns = [];
|
||||
const columnEntityCount = [];
|
||||
for (let i = 0; i < this.columns; i++) {
|
||||
columns.push([]);
|
||||
columnEntityCount.push(0);
|
||||
}
|
||||
|
||||
// Find column with < 5 entities, else column with lowest count
|
||||
function getColumnIndex(size) {
|
||||
let minIndex = 0;
|
||||
for (let i = 0; i < columnEntityCount.length; i++) {
|
||||
if (columnEntityCount[i] < 5) {
|
||||
minIndex = i;
|
||||
break;
|
||||
}
|
||||
if (columnEntityCount[i] < columnEntityCount[minIndex]) {
|
||||
minIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
columnEntityCount[minIndex] += size;
|
||||
|
||||
return minIndex;
|
||||
}
|
||||
|
||||
elements.forEach((el, index) => {
|
||||
const cardSize = computeCardSize(el);
|
||||
// Element to append might be the wrapped card when we're editing.
|
||||
columns[getColumnIndex(cardSize)].push(elementsToAppend[index]);
|
||||
});
|
||||
|
||||
// Remove empty columns
|
||||
columns = columns.filter((val) => val.length > 0);
|
||||
|
||||
columns.forEach((column) => {
|
||||
const columnEl = document.createElement("div");
|
||||
columnEl.classList.add("column");
|
||||
column.forEach((el) => columnEl.appendChild(el));
|
||||
root.appendChild(columnEl);
|
||||
});
|
||||
|
||||
this._cards = elements;
|
||||
|
||||
if ("theme" in config) {
|
||||
applyThemesOnElement(root, this.hass.themes, config.theme);
|
||||
}
|
||||
}
|
||||
|
||||
_hassChanged(hass) {
|
||||
this._badges.forEach((badge) => {
|
||||
const { element, entityId } = badge;
|
||||
element.setProperties({
|
||||
hass,
|
||||
state: hass.states[entityId],
|
||||
});
|
||||
});
|
||||
this._cards.forEach((element) => {
|
||||
element.hass = hass;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-view", HUIView);
|
||||
298
src/panels/lovelace/hui-view.ts
Normal file
298
src/panels/lovelace/hui-view.ts
Normal file
@ -0,0 +1,298 @@
|
||||
import {
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
PropertyDeclarations,
|
||||
} from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
|
||||
import "../../components/entity/ha-state-label-badge";
|
||||
// This one is for types
|
||||
// tslint:disable-next-line
|
||||
import { HaStateLabelBadge } from "../../components/entity/ha-state-label-badge";
|
||||
|
||||
import applyThemesOnElement from "../../common/dom/apply_themes_on_element";
|
||||
|
||||
import { hassLocalizeLitMixin } from "../../mixins/lit-localize-mixin";
|
||||
import { LovelaceViewConfig } from "../../data/lovelace";
|
||||
import { HomeAssistant } from "../../types";
|
||||
|
||||
import { Lovelace, LovelaceCard } from "./types";
|
||||
import { createCardElement } from "./common/create-card-element";
|
||||
import { computeCardSize } from "./common/compute-card-size";
|
||||
import { showEditCardDialog } from "./editor/card-editor/show-edit-card-dialog";
|
||||
|
||||
let editCodeLoaded = false;
|
||||
|
||||
// Find column with < 5 entities, else column with lowest count
|
||||
const getColumnIndex = (columnEntityCount: number[], size: number) => {
|
||||
let minIndex = 0;
|
||||
for (let i = 0; i < columnEntityCount.length; i++) {
|
||||
if (columnEntityCount[i] < 5) {
|
||||
minIndex = i;
|
||||
break;
|
||||
}
|
||||
if (columnEntityCount[i] < columnEntityCount[minIndex]) {
|
||||
minIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
columnEntityCount[minIndex] += size;
|
||||
|
||||
return minIndex;
|
||||
};
|
||||
|
||||
export class HUIView extends hassLocalizeLitMixin(LitElement) {
|
||||
public hass?: HomeAssistant;
|
||||
public lovelace?: Lovelace;
|
||||
public columns?: number;
|
||||
public index?: number;
|
||||
private _cards: LovelaceCard[];
|
||||
private _badges: Array<{ element: HaStateLabelBadge; entityId: string }>;
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return {
|
||||
hass: {},
|
||||
lovelace: {},
|
||||
columns: {},
|
||||
index: {},
|
||||
_cards: {},
|
||||
_badges: {},
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._cards = [];
|
||||
this._badges = [];
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
${this.renderStyles()}
|
||||
<div id="badges"></div>
|
||||
<div id="columns"></div>
|
||||
${
|
||||
this.lovelace!.editMode
|
||||
? html`
|
||||
<paper-fab
|
||||
elevated="2"
|
||||
icon="hass:plus"
|
||||
title="${
|
||||
this.localize("ui.panel.lovelace.editor.edit_card.add")
|
||||
}"
|
||||
@click="${this._addCard}"
|
||||
></paper-fab>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
protected renderStyles(): TemplateResult {
|
||||
return html`
|
||||
<style>
|
||||
:host {
|
||||
display: block;
|
||||
padding: 4px 4px 0;
|
||||
transform: translateZ(0);
|
||||
position: relative;
|
||||
min-height: calc(100vh - 155px);
|
||||
}
|
||||
|
||||
#badges {
|
||||
margin: 8px 16px;
|
||||
font-size: 85%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#columns {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
max-width: 500px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.column > * {
|
||||
display: block;
|
||||
margin: 4px 4px 8px;
|
||||
}
|
||||
|
||||
paper-fab {
|
||||
position: sticky;
|
||||
float: right;
|
||||
bottom: 16px;
|
||||
right: 16px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
}
|
||||
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
super.updated(changedProperties);
|
||||
|
||||
const lovelace = this.lovelace!;
|
||||
|
||||
if (lovelace.editMode && !editCodeLoaded) {
|
||||
editCodeLoaded = true;
|
||||
import(/* webpackChunkName: "hui-view-editable" */ "./hui-view-editable");
|
||||
}
|
||||
|
||||
let editModeChanged = false;
|
||||
let configChanged = false;
|
||||
|
||||
if (changedProperties.has("lovelace")) {
|
||||
const oldLovelace = changedProperties.get("lovelace") as Lovelace;
|
||||
editModeChanged =
|
||||
!oldLovelace || lovelace.editMode !== oldLovelace.editMode;
|
||||
configChanged = !oldLovelace || lovelace.config !== oldLovelace.config;
|
||||
}
|
||||
|
||||
if (configChanged) {
|
||||
this._createBadges(lovelace.config.views[this.index!]);
|
||||
} else if (changedProperties.has("hass")) {
|
||||
this._badges.forEach((badge) => {
|
||||
const { element, entityId } = badge;
|
||||
element.hass = this.hass!;
|
||||
element.state = this.hass!.states[entityId];
|
||||
});
|
||||
}
|
||||
|
||||
if (configChanged || editModeChanged || changedProperties.has("columns")) {
|
||||
this._createCards(lovelace.config.views[this.index!]);
|
||||
} else if (changedProperties.has("hass")) {
|
||||
this._cards.forEach((element) => {
|
||||
element.hass = this.hass;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _addCard(): void {
|
||||
showEditCardDialog(this, {
|
||||
lovelace: this.lovelace!,
|
||||
path: [this.index!],
|
||||
});
|
||||
}
|
||||
|
||||
private _createBadges(config: LovelaceViewConfig): void {
|
||||
const root = this.shadowRoot!.getElementById("badges")!;
|
||||
|
||||
while (root.lastChild) {
|
||||
root.removeChild(root.lastChild);
|
||||
}
|
||||
|
||||
if (!config || !config.badges || !Array.isArray(config.badges)) {
|
||||
root.style.display = "none";
|
||||
this._badges = [];
|
||||
return;
|
||||
}
|
||||
|
||||
const elements: HUIView["_badges"] = [];
|
||||
for (const entityId of config.badges) {
|
||||
const element = document.createElement("ha-state-label-badge");
|
||||
element.hass = this.hass;
|
||||
element.state = this.hass!.states[entityId];
|
||||
elements.push({ element, entityId });
|
||||
root.appendChild(element);
|
||||
}
|
||||
this._badges = elements;
|
||||
root.style.display = elements.length > 0 ? "block" : "none";
|
||||
}
|
||||
|
||||
private _createCards(config: LovelaceViewConfig): void {
|
||||
const root = this.shadowRoot!.getElementById("columns")!;
|
||||
|
||||
while (root.lastChild) {
|
||||
root.removeChild(root.lastChild);
|
||||
}
|
||||
|
||||
if (!config || !config.cards || !Array.isArray(config.cards)) {
|
||||
this._cards = [];
|
||||
return;
|
||||
}
|
||||
|
||||
const elements: LovelaceCard[] = [];
|
||||
const elementsToAppend: HTMLElement[] = [];
|
||||
config.cards.forEach((cardConfig, cardIndex) => {
|
||||
const element = createCardElement(cardConfig) as LovelaceCard;
|
||||
element.hass = this.hass;
|
||||
elements.push(element);
|
||||
|
||||
if (!this.lovelace!.editMode) {
|
||||
elementsToAppend.push(element);
|
||||
return;
|
||||
}
|
||||
|
||||
const wrapper = document.createElement("hui-card-options");
|
||||
wrapper.hass = this.hass;
|
||||
wrapper.lovelace = this.lovelace;
|
||||
wrapper.path = [this.index!, cardIndex];
|
||||
wrapper.appendChild(element);
|
||||
elementsToAppend.push(wrapper);
|
||||
});
|
||||
|
||||
let columns: HTMLElement[][] = [];
|
||||
const columnEntityCount: number[] = [];
|
||||
for (let i = 0; i < this.columns!; i++) {
|
||||
columns.push([]);
|
||||
columnEntityCount.push(0);
|
||||
}
|
||||
|
||||
elements.forEach((el, index) => {
|
||||
const cardSize = computeCardSize(el);
|
||||
// Element to append might be the wrapped card when we're editing.
|
||||
columns[getColumnIndex(columnEntityCount, cardSize)].push(
|
||||
elementsToAppend[index]
|
||||
);
|
||||
});
|
||||
|
||||
// Remove empty columns
|
||||
columns = columns.filter((val) => val.length > 0);
|
||||
|
||||
columns.forEach((column) => {
|
||||
const columnEl = document.createElement("div");
|
||||
columnEl.classList.add("column");
|
||||
column.forEach((el) => columnEl.appendChild(el));
|
||||
root.appendChild(columnEl);
|
||||
});
|
||||
|
||||
this._cards = elements;
|
||||
|
||||
if ("theme" in config) {
|
||||
applyThemesOnElement(root, this.hass!.themes, config.theme);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-view": HUIView;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-view", HUIView);
|
||||
@ -167,7 +167,8 @@ class HaPanelMailbox extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
||||
this.fire("register-dialog", {
|
||||
dialogShowEvent: "show-audio-message-dialog",
|
||||
dialogTag: "ha-dialog-show-audio-message",
|
||||
dialogImport: () => import("./ha-dialog-show-audio-message"),
|
||||
dialogImport: () =>
|
||||
import(/* webpackChunkName: "ha-dialog-show-audio-message" */ "./ha-dialog-show-audio-message"),
|
||||
});
|
||||
}
|
||||
this.hassChanged = this.hassChanged.bind(this);
|
||||
|
||||
@ -90,7 +90,8 @@ class HaMfaModulesCard extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
||||
this.fire("register-dialog", {
|
||||
dialogShowEvent: "show-mfa-module-setup-flow",
|
||||
dialogTag: "ha-mfa-module-setup-flow",
|
||||
dialogImport: () => import("./ha-mfa-module-setup-flow"),
|
||||
dialogImport: () =>
|
||||
import(/* webpackChunkName: "ha-mfa-module-setup-flow" */ "./ha-mfa-module-setup-flow"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,6 +35,7 @@
|
||||
"updater": "Updater",
|
||||
"vacuum": "Vacuum",
|
||||
"weblink": "Weblink",
|
||||
"zha": "ZHA",
|
||||
"zwave": "Z-Wave"
|
||||
},
|
||||
"panel": {
|
||||
@ -472,6 +473,19 @@
|
||||
}
|
||||
},
|
||||
"dialogs": {
|
||||
"more_info_control": {
|
||||
"script": {
|
||||
"last_action": "Last Action"
|
||||
},
|
||||
"sun": {
|
||||
"elevation": "Elevation",
|
||||
"rising": "Rising",
|
||||
"setting": "Setting"
|
||||
},
|
||||
"updater": {
|
||||
"title": "Update Instructions"
|
||||
}
|
||||
},
|
||||
"more_info_settings": {
|
||||
"save": "Save",
|
||||
"name": "Name",
|
||||
@ -751,6 +765,10 @@
|
||||
"delete_user": "Delete user"
|
||||
}
|
||||
},
|
||||
"zha": {
|
||||
"caption": "ZHA",
|
||||
"description": "Zigbee Home Automation network management"
|
||||
},
|
||||
"zwave": {
|
||||
"caption": "Z-Wave",
|
||||
"description": "Manage your Z-Wave network"
|
||||
|
||||
@ -14,8 +14,8 @@ export default () => {
|
||||
!__DEV__
|
||||
) {
|
||||
// Notify users here of a new frontend being available.
|
||||
import("./show-new-frontend-toast").then((mod) =>
|
||||
mod.default(installingWorker)
|
||||
import(/* webpackChunkName: "show-new-frontend-toast" */ "./show-new-frontend-toast").then(
|
||||
(mod) => mod.default(installingWorker)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
import * as assert from "assert";
|
||||
|
||||
import { swapCard } from "../../../../src/panels/lovelace/editor/config-util";
|
||||
import {
|
||||
swapCard,
|
||||
moveCard,
|
||||
} from "../../../../src/panels/lovelace/editor/config-util";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace";
|
||||
|
||||
describe("swapCard", () => {
|
||||
@ -52,3 +55,77 @@ describe("swapCard", () => {
|
||||
assert.deepEqual(expected, result);
|
||||
});
|
||||
});
|
||||
|
||||
describe("moveCard", () => {
|
||||
it("move a card to an empty view", () => {
|
||||
const config: LovelaceConfig = {
|
||||
views: [
|
||||
{},
|
||||
{
|
||||
cards: [{ type: "card1" }, { type: "card2" }],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = moveCard(config, [1, 0], [0]);
|
||||
const expected = {
|
||||
views: [
|
||||
{
|
||||
cards: [{ type: "card1" }],
|
||||
},
|
||||
{
|
||||
cards: [{ type: "card2" }],
|
||||
},
|
||||
],
|
||||
};
|
||||
assert.deepEqual(expected, result);
|
||||
});
|
||||
|
||||
it("move a card to different view", () => {
|
||||
const config: LovelaceConfig = {
|
||||
views: [
|
||||
{
|
||||
cards: [{ type: "v1-c1" }, { type: "v1-c2" }],
|
||||
},
|
||||
{
|
||||
cards: [{ type: "v2-c1" }, { type: "v2-c2" }],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = moveCard(config, [1, 0], [0]);
|
||||
const expected = {
|
||||
views: [
|
||||
{
|
||||
cards: [{ type: "v1-c1" }, { type: "v1-c2" }, { type: "v2-c1" }],
|
||||
},
|
||||
{
|
||||
cards: [{ type: "v2-c2" }],
|
||||
},
|
||||
],
|
||||
};
|
||||
assert.deepEqual(expected, result);
|
||||
});
|
||||
|
||||
it("move a card to the same view", () => {
|
||||
const config: LovelaceConfig = {
|
||||
views: [
|
||||
{
|
||||
cards: [{ type: "v1-c1" }, { type: "v1-c2" }],
|
||||
},
|
||||
{
|
||||
cards: [{ type: "v2-c1" }, { type: "v2-c2" }],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = () => {
|
||||
moveCard(config, [1, 0], [1]);
|
||||
};
|
||||
assert.throws(
|
||||
result,
|
||||
Error,
|
||||
"You can not move a card to the view it is in."
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -53,8 +53,8 @@
|
||||
"on": "Регистриран"
|
||||
},
|
||||
"motion": {
|
||||
"off": "Чисто",
|
||||
"on": "Регистрирано"
|
||||
"off": "Без движение",
|
||||
"on": "Движение"
|
||||
},
|
||||
"occupancy": {
|
||||
"off": "Чисто",
|
||||
@ -144,7 +144,8 @@
|
||||
"performance": "Производителност",
|
||||
"high_demand": "Високо натоварване",
|
||||
"heat_pump": "Термопомпа",
|
||||
"gas": "Газ"
|
||||
"gas": "Газ",
|
||||
"manual": "Ръчен режим"
|
||||
},
|
||||
"configurator": {
|
||||
"configure": "Настройване",
|
||||
@ -185,8 +186,8 @@
|
||||
"on": "Включен"
|
||||
},
|
||||
"light": {
|
||||
"off": "Изключена",
|
||||
"on": "Включен"
|
||||
"off": "Изключено",
|
||||
"on": "Включено"
|
||||
},
|
||||
"lock": {
|
||||
"locked": "Заключен",
|
||||
@ -422,6 +423,10 @@
|
||||
"event": "Събитие",
|
||||
"enter": "Влизане",
|
||||
"leave": "Излизане"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhook",
|
||||
"webhook_id": "Webhook идентификатор"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -712,6 +717,47 @@
|
||||
"required_fields": "Попълнете всички задължителни полета"
|
||||
}
|
||||
}
|
||||
},
|
||||
"lovelace": {
|
||||
"cards": {
|
||||
"shopping-list": {
|
||||
"checked_items": "Отметнати артикули",
|
||||
"clear_items": "Изтрий отметнатите артикули",
|
||||
"add_item": "Добави артикул"
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"edit_card": {
|
||||
"header": "Конфигуриране на Карта",
|
||||
"save": "Запази",
|
||||
"toggle_editor": "Превключете редактора",
|
||||
"pick_card": "Изберете картата, която искате да добавите.",
|
||||
"add": "Добавяне на карта",
|
||||
"edit": "Редактиране",
|
||||
"delete": "Изтриване"
|
||||
},
|
||||
"migrate": {
|
||||
"header": "Несъвместима конфигурация",
|
||||
"para_no_id": "Този елемент няма идентификатор. Моля, добавете идентификатор към този елемент в \"ui-lovelace.yaml\".",
|
||||
"para_migrate": "Home Assistant може автоматично да добави идентификатори към всичките ви карти и изгледи, като натиска бутона \"Мигриране на конфигурация\".",
|
||||
"migrate": "Мигриране на конфигурация"
|
||||
},
|
||||
"header": "Редактиране на потребителския интерфейс",
|
||||
"configure_ui": "Конфигуриране на потребителския интерфейс",
|
||||
"edit_view": {
|
||||
"header": "Конфигурация на изглед",
|
||||
"add": "Добавяне на изглед",
|
||||
"edit": "Редактиране на изгледа",
|
||||
"delete": "Изтриване на изгледа"
|
||||
},
|
||||
"save_config": {
|
||||
"header": "Поемете контрол над потребителския интерфейс на Lovelace",
|
||||
"para": "По подразбиране Home Assistant поддържа потребителския интерфейс, като го актуализира, когато нови обекти или компоненти на Lovelace станат достъпни. Ако поемете контрол, ние вече няма да правим автоматично промени вместо вас.",
|
||||
"para_sure": "Наистина ли искате да поемете управлението на потребителския интерфейс?",
|
||||
"cancel": "Няма значение",
|
||||
"save": "Поемете контрола"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sidebar": {
|
||||
@ -720,7 +766,8 @@
|
||||
},
|
||||
"common": {
|
||||
"loading": "Зареждане",
|
||||
"cancel": "Отмени"
|
||||
"cancel": "Отмени",
|
||||
"save": "Запази"
|
||||
},
|
||||
"duration": {
|
||||
"day": "{count}{count, plural,\n one {ден}\n other {дни}\n}",
|
||||
@ -780,7 +827,9 @@
|
||||
"clear_code": "Изчистване",
|
||||
"disarm": "Деактивирaне",
|
||||
"arm_home": "Под охрана - вкъщи",
|
||||
"arm_away": "Под охрана"
|
||||
"arm_away": "Под охрана",
|
||||
"arm_night": "Под охрана - нощ",
|
||||
"armed_custom_bypass": "Потребителски байпас"
|
||||
},
|
||||
"automation": {
|
||||
"last_triggered": "Последно задействане",
|
||||
|
||||
@ -133,8 +133,8 @@
|
||||
"climate": {
|
||||
"off": "Apagat",
|
||||
"on": "Encès",
|
||||
"heat": "Calent",
|
||||
"cool": "Fred",
|
||||
"heat": "Escalfar",
|
||||
"cool": "Refredar",
|
||||
"idle": "Inactiu",
|
||||
"auto": "Automàtic",
|
||||
"dry": "Assecar",
|
||||
@ -423,6 +423,10 @@
|
||||
"event": "Esdeveniment:",
|
||||
"enter": "Entrar",
|
||||
"leave": "Sortir"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhook",
|
||||
"webhook_id": "ID de Webhook"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -820,7 +824,7 @@
|
||||
},
|
||||
"alarm_control_panel": {
|
||||
"code": "Codi",
|
||||
"clear_code": "Esborrar",
|
||||
"clear_code": "Supr.",
|
||||
"disarm": "Desactivar",
|
||||
"arm_home": "Activar, a casa",
|
||||
"arm_away": "Activar, fora",
|
||||
|
||||
@ -423,6 +423,10 @@
|
||||
"event": "Událost:",
|
||||
"enter": "Vstup",
|
||||
"leave": "Opuštění"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhook",
|
||||
"webhook_id": "Webhook ID"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -726,13 +730,32 @@
|
||||
"edit_card": {
|
||||
"header": "Konfigurace karty",
|
||||
"save": "Uložit",
|
||||
"toggle_editor": "Přepnout Editor"
|
||||
"toggle_editor": "Přepnout Editor",
|
||||
"pick_card": "Vyberte kartu, kterou chcete přidat.",
|
||||
"add": "Přidat kartu",
|
||||
"edit": "Upravit",
|
||||
"delete": "Odstranit"
|
||||
},
|
||||
"migrate": {
|
||||
"header": "Konfigurace není kompatibilní",
|
||||
"para_no_id": "Tento prvek nemá ID. Přidejte k tomuto prvku ID v 'ui-lovelace.yaml'.",
|
||||
"para_migrate": "Home Assistant může automaticky přidávat ID ke všem kartám a pohledům stisknutím tlačítka Migrate config.",
|
||||
"migrate": "Migrovat konfiguraci"
|
||||
},
|
||||
"header": "Upravit UI",
|
||||
"configure_ui": "Konfigurovat UI",
|
||||
"edit_view": {
|
||||
"header": "Zobrazit konfiguraci",
|
||||
"add": "Přidat pohled",
|
||||
"edit": "Upravit pohled",
|
||||
"delete": "Odstranit pohled"
|
||||
},
|
||||
"save_config": {
|
||||
"header": "Převzít kontrolu nad vaší Lovelace UI",
|
||||
"para": "Ve výchozím nastavení Home Assistant bude usprávovat vaše uživatelské rozhraní, aktualizovat při přidání nové entity nebo Lovelace komponenty. Pokud převezmete kontrolu nebudeme již provádět změny automaticky za vás.",
|
||||
"para_sure": "Opravdu chcete převzít kontrolu nad uživalským rohraním ?",
|
||||
"cancel": "Zahodit změnu",
|
||||
"save": "Převzít kontrolu"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -743,7 +766,8 @@
|
||||
},
|
||||
"common": {
|
||||
"loading": "Načítání",
|
||||
"cancel": "Zrušit"
|
||||
"cancel": "Zrušit",
|
||||
"save": "Uložit"
|
||||
},
|
||||
"duration": {
|
||||
"day": "{count} {count, plural,\none {den}\nother {dny}\n}",
|
||||
|
||||
@ -423,6 +423,10 @@
|
||||
"event": "Begivenhed",
|
||||
"enter": "Ankom",
|
||||
"leave": "Forlade"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhook",
|
||||
"webhook_id": "Webhook-ID"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -605,12 +609,17 @@
|
||||
"logout": "Log af",
|
||||
"change_password": {
|
||||
"header": "Skift adgangskode",
|
||||
"current_password": "Nuværende adgangskode",
|
||||
"new_password": "Ny adgangskode",
|
||||
"confirm_new_password": "Bekræft ny adgangskode",
|
||||
"error_required": "Påkrævet",
|
||||
"submit": "Gem og afslut"
|
||||
},
|
||||
"mfa": {
|
||||
"header": "Multifaktor godkendelsesmoduler",
|
||||
"disable": "Deaktiver",
|
||||
"enable": "Aktiver"
|
||||
"enable": "Aktiver",
|
||||
"confirm_disable": "Er du sikker på du vil deaktivere {name}?"
|
||||
},
|
||||
"mfa_setup": {
|
||||
"title_aborted": "Afbrudt",
|
||||
@ -708,6 +717,47 @@
|
||||
"required_fields": "Udfyld alle obligatoriske felter"
|
||||
}
|
||||
}
|
||||
},
|
||||
"lovelace": {
|
||||
"cards": {
|
||||
"shopping-list": {
|
||||
"checked_items": "Markerede elementer",
|
||||
"clear_items": "Ryd markerede elementer",
|
||||
"add_item": "Tilføj element"
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"edit_card": {
|
||||
"header": "Kortkonfiguration",
|
||||
"save": "Gem",
|
||||
"toggle_editor": "Skifte Editor",
|
||||
"pick_card": "Vælg det kort, du vil tilføje.",
|
||||
"add": "Tilføj kort",
|
||||
"edit": "Rediger",
|
||||
"delete": "Slet"
|
||||
},
|
||||
"migrate": {
|
||||
"header": "Konfiguration ikke færdiggjort",
|
||||
"para_no_id": "Dette element har ikke et id. Tilføj venligst et id til dette element i \"ui-lovelace.yaml\".",
|
||||
"para_migrate": "Hjem assistent kan tilføje id'er til alle dine kort og oversigter automatisk for dig ved at trykke på knappen 'Overfør configuration'.",
|
||||
"migrate": "Migrer opsætning"
|
||||
},
|
||||
"header": "Rediger UI",
|
||||
"configure_ui": "Konfigurer UI",
|
||||
"edit_view": {
|
||||
"header": "Vis konfiguration",
|
||||
"add": "Tilføje visning",
|
||||
"edit": "Redigere visning",
|
||||
"delete": "Slette visning"
|
||||
},
|
||||
"save_config": {
|
||||
"header": "Tage kontrol over din 2Lovelace\" UI",
|
||||
"para": "Som standard vil hjem assistenten fastholde din brugergrænseflade, opdaterer den når nye enheder eller \"Lovelace\" komponenter bliver tilgængelige. Hvis du tager kontrol så vil vi ikke længere foretage ændringer automatisk for dig.",
|
||||
"para_sure": "Er du sikker på du ønsker at tage kontrol over din brugergrænseflade?",
|
||||
"cancel": "Glem det",
|
||||
"save": "tag kontrol"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sidebar": {
|
||||
@ -716,7 +766,8 @@
|
||||
},
|
||||
"common": {
|
||||
"loading": "Indlæser",
|
||||
"cancel": "Annuller"
|
||||
"cancel": "Annuller",
|
||||
"save": "Gem"
|
||||
},
|
||||
"duration": {
|
||||
"day": "{count} {count, plural,\none {dag}\nother {dage}\n}",
|
||||
|
||||
@ -365,7 +365,7 @@
|
||||
"alias": "Name",
|
||||
"triggers": {
|
||||
"header": "Auslöser",
|
||||
"introduction": "Auslöser starten automatisierte Abläufe. Es ist mögliche, mehrere Auslöser für dieselbe Abfolge zu definieren. Wenn ein Auslöser aktiviert wird, prüft Home Assistant die Bedingungen, sofern vorhanden, und führt die Aktion aus.\n\n[Erfahre mehr über Auslöser.](https:\/\/home-assistant.io\/docs\/automation\/trigger\/)",
|
||||
"introduction": "Auslöser starten automatisierte Abläufe. Es ist möglich, mehrere Auslöser für dieselbe Abfolge zu definieren. Wenn ein Auslöser aktiviert wird, prüft Home Assistant die Bedingungen, sofern vorhanden, und führt die Aktion aus.\n\n[Erfahre mehr über Auslöser.](https:\/\/home-assistant.io\/docs\/automation\/trigger\/)",
|
||||
"add": "Auslöser hinzufügen",
|
||||
"duplicate": "Duplizieren",
|
||||
"delete": "Löschen",
|
||||
@ -423,12 +423,16 @@
|
||||
"event": "Ereignis:",
|
||||
"enter": "Betreten",
|
||||
"leave": "Verlassen"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhook",
|
||||
"webhook_id": "Webhook ID"
|
||||
}
|
||||
}
|
||||
},
|
||||
"conditions": {
|
||||
"header": "Bedingungen",
|
||||
"introduction": "Bedingungen sind ein optionaler Bestandteil bei automatisierten Abläufen und können das Ausführen einer Aktion verhindern. Bedingungen sind Auslösern ähnlich, aber dennoch verschieden. Ein Auslöser beobachtet im System ablaufende Ereignisse, wohingegen Bedingungen nur den aktuellen Systemzustand betrachten. Ein Auslöser kann beobachten, dass ein Schalter aktiviert wird. Eine Bedingung kann lediglich sehen, ob ein Schalter aktuell an oder aus ist.\n\n[Erfahre mehr über Bedingungen.](https:\/\/home-assistant.io\/docs\/scripts\/conditions\/)",
|
||||
"introduction": "Bedingungen sind ein optionaler Bestandteil bei automatisierten Abläufen und können das Ausführen einer Aktion verhindern. Bedingungen sind Auslösern ähnlich, aber dennoch verschieden. Ein Auslöser beobachtet im System ablaufende Ereignisse, wohingegen Bedingungen nur den aktuellen Systemzustand betrachten. Ein Auslöser kann beobachten, ob ein Schalter aktiviert wird. Eine Bedingung kann lediglich sehen, ob ein Schalter aktuell an oder aus ist.\n\n[Erfahre mehr über Bedingungen.](https:\/\/home-assistant.io\/docs\/scripts\/conditions\/)",
|
||||
"add": "Bedingung hinzufügen",
|
||||
"duplicate": "Duplizieren",
|
||||
"delete": "Löschen",
|
||||
@ -717,9 +721,9 @@
|
||||
"lovelace": {
|
||||
"cards": {
|
||||
"shopping-list": {
|
||||
"checked_items": "Markierte Items",
|
||||
"checked_items": "Markierte Artikel",
|
||||
"clear_items": "Markierte Elemente löschen",
|
||||
"add_item": "Item hinzufügen"
|
||||
"add_item": "Artikel hinzufügen"
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
@ -735,6 +739,7 @@
|
||||
"migrate": {
|
||||
"header": "Konfiguration inkompatibel",
|
||||
"para_no_id": "Dieses Element hat keine ID. Bitte füge diesem Element eine ID in 'ui-lovelace.yaml' hinzu.",
|
||||
"para_migrate": "Home Assistant kann für alle Ihre Karten und Ansichten die IDs automatisch generieren, wenn Sie den \"Konfiguration migrieren\"-Button klicken.",
|
||||
"migrate": "Konfiguration migrieren"
|
||||
},
|
||||
"header": "Benutzeroberfläche bearbeiten",
|
||||
@ -746,6 +751,9 @@
|
||||
"delete": "Ansicht löschen"
|
||||
},
|
||||
"save_config": {
|
||||
"header": "Lovelace Userinterface selbst verwalten",
|
||||
"para": "Standardmäßig verwaltet Home Assistant Ihre Benutzeroberfläche und aktualisiert sie, sobald neue Entitäten oder Lovelace-Komponenten verfügbar sind. Wenn Sie die Verwaltung selbst übernehmen, nehmen wir für Sie keine Änderungen mehr vor.",
|
||||
"para_sure": "Bist du dir sicher, dass du die Benutzeroberfläche selbst verwalten möchtest?",
|
||||
"cancel": "Abbrechen",
|
||||
"save": "Kontrolle übernehmen"
|
||||
}
|
||||
|
||||
@ -222,8 +222,67 @@
|
||||
"config": {
|
||||
"zwave": {
|
||||
"caption": "Z-Wave"
|
||||
},
|
||||
"automation": {
|
||||
"editor": {
|
||||
"triggers": {
|
||||
"type": {
|
||||
"state": {
|
||||
"for": "Για"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"lovelace": {
|
||||
"editor": {
|
||||
"edit_card": {
|
||||
"save": "Αποθήκευση",
|
||||
"edit": "Επεξεργασία",
|
||||
"delete": "Διαγραφή"
|
||||
},
|
||||
"header": "Επεξεργασία UI",
|
||||
"configure_ui": "Ρύθμιση UI",
|
||||
"edit_view": {
|
||||
"header": "Ρυθμίσεις",
|
||||
"add": "Προσθήκη προβολής",
|
||||
"edit": "Επεξεργασία προβολής",
|
||||
"delete": "Διαγραφή προβολής"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"card": {
|
||||
"cover": {
|
||||
"position": "Θέση",
|
||||
"tilt_position": "Θέση ανάκλισης"
|
||||
},
|
||||
"fan": {
|
||||
"speed": "Ταχύτητα",
|
||||
"oscillate": "Περιστροφή",
|
||||
"direction": "Κατεύθυνση"
|
||||
},
|
||||
"light": {
|
||||
"brightness": "Φωτεινότητα",
|
||||
"color_temperature": "Θερμοκρασία χρώματος",
|
||||
"white_value": "Τιμή λευκού",
|
||||
"effect": "Εφέ"
|
||||
},
|
||||
"media_player": {
|
||||
"text_to_speak": "Κείμενο για εκφώνηση"
|
||||
},
|
||||
"climate": {
|
||||
"currently": "Αυτή τη στιγμή",
|
||||
"target_temperature": "Επιθυμητή θερμοκρασία",
|
||||
"target_humidity": "Επιθυμητή υγρασία",
|
||||
"operation": "Λειτουργία",
|
||||
"fan_mode": "Λειτουργία ανεμιστήρα",
|
||||
"away_mode": "Λειτουργία εκτός σπιτιού"
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"save": "Αποθήκευση"
|
||||
}
|
||||
},
|
||||
"domain": {
|
||||
|
||||
@ -423,6 +423,10 @@
|
||||
"event": "Event:",
|
||||
"enter": "Enter",
|
||||
"leave": "Leave"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhook",
|
||||
"webhook_id": "Webhook ID"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -423,6 +423,10 @@
|
||||
"event": "Evento:",
|
||||
"enter": "Entrar",
|
||||
"leave": "Salir"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhook",
|
||||
"webhook_id": "ID de webhook"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -726,13 +730,32 @@
|
||||
"edit_card": {
|
||||
"header": "Configuración de la tarjeta",
|
||||
"save": "Guardar",
|
||||
"toggle_editor": "Alternar editor"
|
||||
"toggle_editor": "Alternar editor",
|
||||
"pick_card": "Escoge la tarjeta que desea agregar.",
|
||||
"add": "Añadir tarjeta",
|
||||
"edit": "Editar",
|
||||
"delete": "Eliminar"
|
||||
},
|
||||
"migrate": {
|
||||
"header": "Configuración incompatible",
|
||||
"para_no_id": "Este elemento no tiene un ID. Por favor agregue uno este elemento en 'ui-lovelace.yaml'.",
|
||||
"para_migrate": "Home Assistant puede agregar ID a todas sus tarjetas y vistas automáticamente por usted presionando el botón 'Migrar configuración'.",
|
||||
"migrate": "Migrar configuración"
|
||||
},
|
||||
"header": "Editar la interfaz de usuario",
|
||||
"configure_ui": "Configurar la interfaz de usuario",
|
||||
"edit_view": {
|
||||
"header": "Ver configuración",
|
||||
"add": "Añadir vista",
|
||||
"edit": "Editar vista",
|
||||
"delete": "Borrar vista"
|
||||
},
|
||||
"save_config": {
|
||||
"header": "Tomar el control de la interfaz de usuario de Lovelace",
|
||||
"para": "Por defecto, Home Assistant mantendrá su interfaz de usuario y la actualizará cuando haya nuevas entidades o componentes de Lovelace disponibles. Si usted toma el control, ya no haremos cambios automáticamente para usted.",
|
||||
"para_sure": "¿Está seguro de que desea tomar el control de su interfaz de usuario?",
|
||||
"cancel": "No importa",
|
||||
"save": "Tomar el control"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -743,7 +766,8 @@
|
||||
},
|
||||
"common": {
|
||||
"loading": "Cargando",
|
||||
"cancel": "Cancelar"
|
||||
"cancel": "Cancelar",
|
||||
"save": "Guardar"
|
||||
},
|
||||
"duration": {
|
||||
"day": "{count} {count, plural,\none {día}\nother {días}\n}",
|
||||
|
||||
@ -719,13 +719,25 @@
|
||||
"edit_card": {
|
||||
"header": "Kortti-asetukset",
|
||||
"save": "Tallenna",
|
||||
"toggle_editor": "Vaihda editori"
|
||||
"toggle_editor": "Vaihda editori",
|
||||
"pick_card": "Valitse kortti jonka haluat lisätä",
|
||||
"add": "Lisää kortti",
|
||||
"edit": "Muokkaa",
|
||||
"delete": "Poista"
|
||||
},
|
||||
"migrate": {
|
||||
"header": "Epäkelvot asetukset",
|
||||
"para_no_id": "Elementillä ei ole ID. Lisää ID elementille 'ui-lovelace.yaml'-tiedostossa.",
|
||||
"para_migrate": "Home Assistant voi lisätä ID:t kaikkiin kortteihisi ja näkymiin automaattisesti painamalla 'Tuo vanhat asetukset'-nappia.",
|
||||
"migrate": "Tuo vanhat asetukset"
|
||||
},
|
||||
"header": "Muokkaa käyttöliittymää",
|
||||
"configure_ui": "Määrittele käyttöliittymä",
|
||||
"edit_view": {
|
||||
"header": "Näytä asetukset",
|
||||
"add": "Lisää näkymä",
|
||||
"edit": "Muokkaa näkymää",
|
||||
"delete": "Poista näkymä"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -736,7 +748,8 @@
|
||||
},
|
||||
"common": {
|
||||
"loading": "Ladataan",
|
||||
"cancel": "Peruuta"
|
||||
"cancel": "Peruuta",
|
||||
"save": "Tallenna"
|
||||
},
|
||||
"duration": {
|
||||
"day": "{count} {count, plural,\n one {päivä}\n other {päivää}\n}",
|
||||
@ -755,7 +768,7 @@
|
||||
"not_available": "Kuvaa ei saatavilla"
|
||||
},
|
||||
"persistent_notification": {
|
||||
"dismiss": "Peruuta"
|
||||
"dismiss": "Hylkää"
|
||||
},
|
||||
"scene": {
|
||||
"activate": "Aktivoi"
|
||||
|
||||
@ -70,7 +70,7 @@
|
||||
},
|
||||
"vibration": {
|
||||
"off": "RAS",
|
||||
"on": "Détecté"
|
||||
"on": "Détectée"
|
||||
},
|
||||
"opening": {
|
||||
"off": "Fermé",
|
||||
@ -275,8 +275,8 @@
|
||||
"alarm_control_panel": {
|
||||
"armed": "Activé",
|
||||
"disarmed": "Désactivé",
|
||||
"armed_home": "Activée (présent)",
|
||||
"armed_away": "Armée (absent)",
|
||||
"armed_home": "Armé",
|
||||
"armed_away": "Armé",
|
||||
"armed_night": "Activé",
|
||||
"pending": "En cours",
|
||||
"arming": "Activer",
|
||||
@ -301,7 +301,7 @@
|
||||
"period": "Période"
|
||||
},
|
||||
"logbook": {
|
||||
"showing_entries": "Afficher les entrées pour"
|
||||
"showing_entries": "Afficher les entrées pour le"
|
||||
},
|
||||
"mailbox": {
|
||||
"empty": "Vous n'avez aucun message",
|
||||
@ -423,6 +423,10 @@
|
||||
"event": "Événement :",
|
||||
"enter": "Entre",
|
||||
"leave": "Quitter"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhook",
|
||||
"webhook_id": "ID Webhook"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -432,7 +436,7 @@
|
||||
"add": "Ajouter une condition",
|
||||
"duplicate": "Dupliquer",
|
||||
"delete": "Effacement",
|
||||
"delete_confirm": "Voulez-vous effacer ?",
|
||||
"delete_confirm": "Voulez-vous vraiment effacer ?",
|
||||
"unsupported_condition": "Condition non supportée : {condition}",
|
||||
"type_select": "Type de condition",
|
||||
"type": {
|
||||
@ -476,7 +480,7 @@
|
||||
"introduction": "Les actions sont ce que Home Assistant fera quand une automatisation est déclenchée.\n\n[En apprendre plus sur les actions.](https:\/\/home-assistant.io\/docs\/automation\/action\/)",
|
||||
"add": "Ajouter une action",
|
||||
"duplicate": "Dupliquer",
|
||||
"delete": "Effacer",
|
||||
"delete": "Supprimer",
|
||||
"delete_confirm": "Voulez-vous vraiment effacer ?",
|
||||
"unsupported_action": "Action non supportée : {action}",
|
||||
"type_select": "Type d'action",
|
||||
@ -529,7 +533,7 @@
|
||||
}
|
||||
},
|
||||
"cloud": {
|
||||
"caption": "Home Assistant Cloud",
|
||||
"caption": "Nuage Home Assistant",
|
||||
"description_login": "Connecté en tant que {email}",
|
||||
"description_not_login": "Pas connecté"
|
||||
},
|
||||
@ -718,19 +722,40 @@
|
||||
"cards": {
|
||||
"shopping-list": {
|
||||
"checked_items": "Éléments cochés",
|
||||
"clear_items": "Effacer éléments cochés",
|
||||
"add_item": "Ajouter un élément"
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"edit_card": {
|
||||
"header": "Configuration de la carte",
|
||||
"save": "Enregistrer",
|
||||
"toggle_editor": "Activer\/désactiver l’éditeur"
|
||||
"toggle_editor": "Activer\/désactiver l’éditeur",
|
||||
"pick_card": "Choisissez l'automatisation à ajouter",
|
||||
"add": "Ajouter une action",
|
||||
"edit": "Modifier",
|
||||
"delete": "Supprimer"
|
||||
},
|
||||
"migrate": {
|
||||
"header": "Configuration incompatible",
|
||||
"para_no_id": "Cet élément n'a pas d'identifiant. Veuillez ajouter un identifiant à cet élément dans 'ui-lovelace.yaml'.",
|
||||
"para_migrate": "Home Assistant peut ajouter automatiquement des identifiants à toutes vos cartes et vues en appuyant sur le bouton «Migrer la configuration».",
|
||||
"migrate": "Migrer la configuration"
|
||||
},
|
||||
"header": "Modifier l'interface utilisateur",
|
||||
"configure_ui": "Configurer l'interface utilisateur",
|
||||
"edit_view": {
|
||||
"header": "Voir la configuration",
|
||||
"add": "Ajouter la vue\n",
|
||||
"edit": "Modifier la vue",
|
||||
"delete": "Supprimer la vue"
|
||||
},
|
||||
"save_config": {
|
||||
"header": "Prenez le controle de Lovelace UI",
|
||||
"para": "Par défaut, Home Assistant maintient votre interface utilisateur et la met à jour lorsque de nouvelles entités ou de nouveaux composants Lovelace sont disponibles. Si vous prenez le contrôle, nous ne ferons plus les changements automatiquement pour vous.",
|
||||
"para_sure": "Êtes-vous sûr de vouloir prendre le controle de l'interface utilisateur?",
|
||||
"cancel": "Oublie ce que j'ai dit, c'est pas grave.",
|
||||
"save": "Prenez le contrôle"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -741,7 +766,8 @@
|
||||
},
|
||||
"common": {
|
||||
"loading": "Chargement",
|
||||
"cancel": "Annuler"
|
||||
"cancel": "Annuler",
|
||||
"save": "Enregistrer"
|
||||
},
|
||||
"duration": {
|
||||
"day": "{count} {count, plural,\none {jour}\nother {jours}\n}",
|
||||
@ -801,7 +827,9 @@
|
||||
"clear_code": "Effacer",
|
||||
"disarm": "Désarmer",
|
||||
"arm_home": "Armer (domicile)",
|
||||
"arm_away": "Armer (absent)"
|
||||
"arm_away": "Armer (absent)",
|
||||
"arm_night": "Armer nuit",
|
||||
"armed_custom_bypass": "Bypass personnalisé"
|
||||
},
|
||||
"automation": {
|
||||
"last_triggered": "Dernier déclenchement",
|
||||
|
||||
@ -423,6 +423,10 @@
|
||||
"event": "אירוע:",
|
||||
"enter": "כניסה",
|
||||
"leave": "יציאה"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhook",
|
||||
"webhook_id": "Webhook ID"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -557,7 +561,7 @@
|
||||
"profile": {
|
||||
"push_notifications": {
|
||||
"header": "הודעות דחיפה",
|
||||
"description": "שלח התראות למכשיר זה",
|
||||
"description": "שלח התראות למכשיר זה.",
|
||||
"error_load_platform": "הגדר את notify.html5.",
|
||||
"error_use_https": "נדרש SSL מופעל עבור הממשק.",
|
||||
"push_notifications": "הודעות דחיפה",
|
||||
|
||||
@ -251,11 +251,13 @@
|
||||
},
|
||||
"vacuum": {
|
||||
"cleaning": "Čišćenje",
|
||||
"docked": "Usidreni",
|
||||
"error": "Greška",
|
||||
"idle": "Besposlen",
|
||||
"off": "Ugašeno",
|
||||
"on": "Upaljeno",
|
||||
"paused": "Pauzirano"
|
||||
"paused": "Pauzirano",
|
||||
"returning": "Povratak na dok"
|
||||
}
|
||||
},
|
||||
"state_badge": {
|
||||
@ -345,12 +347,24 @@
|
||||
"unsaved_confirm": "Imate nespremljene izmjene. Jeste li sigurni da želite napustiti?",
|
||||
"alias": "Ime",
|
||||
"triggers": {
|
||||
"header": "Okidači",
|
||||
"introduction": "Okidači su ono što pokreće obradu pravila o automatizaciji. Moguće je odrediti više okidača za isto pravilo. Kada pokrenete okidač, Home Assistant provjerit će uvjete, ako ih ima i pozvati akciju. \n\n [Saznajte više o pokretačima.] (Https:\/\/home-assistant.io\/docs\/automation\/trigger\/)",
|
||||
"add": "Dodaj okidač",
|
||||
"duplicate": "Udvostruči",
|
||||
"delete": "Obriši",
|
||||
"delete_confirm": "Jeste li sigurni dai želite izbrisati?",
|
||||
"unsupported_platform": "Nepodržana platforma: {platform}",
|
||||
"type_select": "Tip okidača",
|
||||
"type": {
|
||||
"event": {
|
||||
"label": "Događaj:",
|
||||
"event_type": "Vrsta događaja",
|
||||
"event_data": "Podaci o događaju"
|
||||
},
|
||||
"state": {
|
||||
"label": "Stanje",
|
||||
"from": "Od",
|
||||
"to": "Do",
|
||||
"for": "Za"
|
||||
},
|
||||
"homeassistant": {
|
||||
@ -361,7 +375,8 @@
|
||||
},
|
||||
"mqtt": {
|
||||
"label": "MQTT",
|
||||
"topic": "Tema"
|
||||
"topic": "Tema",
|
||||
"payload": "Opterećenje (opcionalno)"
|
||||
},
|
||||
"numeric_state": {
|
||||
"label": "Numeričko stanje",
|
||||
@ -391,6 +406,10 @@
|
||||
"event": "Event:",
|
||||
"enter": "Unesite",
|
||||
"leave": "Napustiti"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhook",
|
||||
"webhook_id": "Webhook ID"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -474,8 +493,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"script": {
|
||||
"description": "Stvaranje i uređivanje skripti"
|
||||
},
|
||||
"zwave": {
|
||||
"caption": "Z-Wave"
|
||||
"caption": "Z-Wave",
|
||||
"description": "Upravljajte mrežom Z-Wave"
|
||||
},
|
||||
"users": {
|
||||
"caption": "Korisnici",
|
||||
@ -632,7 +655,8 @@
|
||||
"mfa": {
|
||||
"data": {
|
||||
"code": "Kôd autentifikacije s dva faktora"
|
||||
}
|
||||
},
|
||||
"description": "Otvori **{mfa_module_name}** na svojem uređaju kao bi vidio kod dvofaktorske autentikacije i provjerio svoj identitet:"
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
@ -688,13 +712,32 @@
|
||||
"edit_card": {
|
||||
"header": "Konfiguracija Kartice ",
|
||||
"save": "Spremi",
|
||||
"toggle_editor": "Uključi uređivač"
|
||||
"toggle_editor": "Uključi uređivač",
|
||||
"pick_card": "Odaberite karticu koju želite dodati.",
|
||||
"add": "Dodaj karticu",
|
||||
"edit": "Uredi",
|
||||
"delete": "Izbrisati"
|
||||
},
|
||||
"migrate": {
|
||||
"header": "Konfiguracija nije kompatibilna",
|
||||
"para_no_id": "Ovaj element nema ID. Dodajte ID ovom elementu u 'ui-lovelace.yaml'.",
|
||||
"para_migrate": "Home Assistant može dodati ID-ove na sve vaše kartice i pogleda automatski za vas pritiskom na gumb 'Migriraj konfiguriranje'.",
|
||||
"migrate": "Migriraj konfiguraciju"
|
||||
},
|
||||
"header": "Uredi UI",
|
||||
"configure_ui": "Konfiguriraj UI",
|
||||
"edit_view": {
|
||||
"header": "Pogledaj konfiguraciju",
|
||||
"add": "Dodaj prikaz",
|
||||
"edit": "Uredi prikaz",
|
||||
"delete": "Izbriši prikaz"
|
||||
},
|
||||
"save_config": {
|
||||
"header": "Preuzmite kontrolu nad Lovelace UI",
|
||||
"para": "Home Assistant će prema zadanim postavkama održavati vaše korisničko sučelje, ažurirati ga kada novi entiteti ili Lovelace komponente postanu dostupni. Ako preuzmete kontrolu, više nećemo automatski vršiti izmjene za vas.",
|
||||
"para_sure": "Jeste li sigurni da želite preuzeti kontrolu nad svojim korisničkim sučeljem?",
|
||||
"cancel": "Nema veze",
|
||||
"save": "Preuzmi kontrolu"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -705,7 +748,8 @@
|
||||
},
|
||||
"common": {
|
||||
"loading": "Učitavam",
|
||||
"cancel": "Otkazati"
|
||||
"cancel": "Otkazati",
|
||||
"save": "Spremi"
|
||||
},
|
||||
"duration": {
|
||||
"day": "{count} {count, plural,\n one {dan}\n other {dani}\n}",
|
||||
@ -736,21 +780,38 @@
|
||||
"wind_speed": "Brzina vjetra"
|
||||
},
|
||||
"cardinal_direction": {
|
||||
"e": "I",
|
||||
"ene": "ISI",
|
||||
"ese": "ESE",
|
||||
"n": "N",
|
||||
"ne": "NE",
|
||||
"nne": "NNE",
|
||||
"nw": "NW"
|
||||
}
|
||||
"nw": "NW",
|
||||
"nnw": "SSZ",
|
||||
"s": "J",
|
||||
"se": "JI",
|
||||
"sse": "JJI",
|
||||
"ssw": "JJZ",
|
||||
"sw": "JZ",
|
||||
"w": "Z",
|
||||
"wnw": "ZSZ",
|
||||
"wsw": "ZJZ"
|
||||
},
|
||||
"forecast": "Prognoza"
|
||||
},
|
||||
"alarm_control_panel": {
|
||||
"code": "Kod",
|
||||
"clear_code": "Vedro",
|
||||
"disarm": "Deaktiviraj",
|
||||
"arm_home": "Aktiviran doma",
|
||||
"arm_away": "Aktiviran odsutno",
|
||||
"arm_night": "Aktiviran nočni",
|
||||
"armed_custom_bypass": "Prilagođena obilaznica"
|
||||
},
|
||||
"automation": {
|
||||
"last_triggered": "Zadnje aktivirano",
|
||||
"trigger": "Okidač"
|
||||
},
|
||||
"fan": {
|
||||
"speed": "Brzina",
|
||||
"oscillate": "Oscilirati",
|
||||
@ -784,6 +845,8 @@
|
||||
},
|
||||
"vacuum": {
|
||||
"actions": {
|
||||
"resume_cleaning": "Nastavi čišćenje",
|
||||
"return_to_base": "Povratak na dok",
|
||||
"start_cleaning": "Započnite čišćenje",
|
||||
"turn_on": "Uključiti",
|
||||
"turn_off": "Isključiti"
|
||||
@ -803,8 +866,14 @@
|
||||
"entity": "Entitet"
|
||||
}
|
||||
},
|
||||
"service-picker": {
|
||||
"service": "Usluga"
|
||||
},
|
||||
"relative_time": {
|
||||
"past": "{vrijeme} prije"
|
||||
"past": "{vrijeme} prije",
|
||||
"duration": {
|
||||
"week": "{count} {count, plural,\n jedan {week}\n ostali {weeks}\n}"
|
||||
}
|
||||
},
|
||||
"history_charts": {
|
||||
"loading_history": "Učitavanje povijesti stanja ...",
|
||||
@ -814,7 +883,8 @@
|
||||
"dialogs": {
|
||||
"more_info_settings": {
|
||||
"save": "Spremi",
|
||||
"name": "Ime"
|
||||
"name": "Ime",
|
||||
"entity_id": "ID entiteta"
|
||||
}
|
||||
},
|
||||
"auth_store": {
|
||||
@ -863,7 +933,8 @@
|
||||
"switch": "Prekidač",
|
||||
"updater": "Ažuriranje",
|
||||
"weblink": "WebLink",
|
||||
"zwave": "Z-Wave"
|
||||
"zwave": "Z-Wave",
|
||||
"vacuum": "Vakuum"
|
||||
},
|
||||
"attribute": {
|
||||
"weather": {
|
||||
|
||||
@ -292,7 +292,7 @@
|
||||
"ui": {
|
||||
"panel": {
|
||||
"shopping-list": {
|
||||
"clear_completed": "Kijelöltek törlése",
|
||||
"clear_completed": "Bejelöltek törlése",
|
||||
"add_item": "Tétel hozzáadása",
|
||||
"microphone_tip": "Koppints a jobb felső sarokban található mikrofonra, és mondd ki: \"Add candy to my shopping list\""
|
||||
},
|
||||
@ -423,6 +423,10 @@
|
||||
"event": "Esemény:",
|
||||
"enter": "Érkezés",
|
||||
"leave": "Távozás"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhook",
|
||||
"webhook_id": "Webhook ID"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -546,8 +550,8 @@
|
||||
"no_device": "Entitások eszközök nélkül",
|
||||
"delete_confirm": "Biztosan törölni szeretnéd ezt az integrációt?",
|
||||
"restart_confirm": "Indítsd újra a Home Assistant-ot az integráció törlésének befejezéséhez",
|
||||
"manuf": "Gyártó: {manufacturer}",
|
||||
"hub": "Csatlakoztatva",
|
||||
"manuf": "{manufacturer} által",
|
||||
"hub": "Kapcsolódva",
|
||||
"firmware": "Firmware: {version}",
|
||||
"device_unavailable": "eszköz nem érhető el",
|
||||
"entity_unavailable": "entitás nem érhető el"
|
||||
@ -620,7 +624,7 @@
|
||||
"mfa_setup": {
|
||||
"title_aborted": "Megszakítva",
|
||||
"title_success": "Siker!",
|
||||
"step_done": "Beállítás kész {step}",
|
||||
"step_done": "{step} beállítás elvégezve",
|
||||
"close": "Bezárás",
|
||||
"submit": "Küldés"
|
||||
}
|
||||
@ -699,7 +703,7 @@
|
||||
}
|
||||
},
|
||||
"page-onboarding": {
|
||||
"intro": "Készen állsz arra, hogy felébreszd az otthonod, visszaszerezed a magánéleted és csatlakozz egy világhálós közösséghez?",
|
||||
"intro": "Készen állsz arra, hogy felébreszd az otthonod, visszaszerezd a magánéleted és csatlakozz egy világhálós közösséghez?",
|
||||
"user": {
|
||||
"intro": "Kezdjük a felhasználói fiók létrehozásával.",
|
||||
"required_field": "Szükséges",
|
||||
@ -717,8 +721,8 @@
|
||||
"lovelace": {
|
||||
"cards": {
|
||||
"shopping-list": {
|
||||
"checked_items": "Kijelölt tételek",
|
||||
"clear_items": "Kijelölt tételek törlése",
|
||||
"checked_items": "Bejelölt tételek",
|
||||
"clear_items": "Bejelölt tételek törlése",
|
||||
"add_item": "Tétel hozzáadása"
|
||||
}
|
||||
},
|
||||
@ -734,8 +738,8 @@
|
||||
},
|
||||
"migrate": {
|
||||
"header": "Inkompatibilis Konfiguráció",
|
||||
"para_no_id": "Ez az elem nem rendelkezik azonosítóval. Kérlek, adj hozzá egyet az 'ui-lovelace.yaml' fájlban!",
|
||||
"para_migrate": "A Home Assistant a 'Konfiguráció áttelepítése' gomb megnyomásával az összes kártyához és nézethez automatikusan létre tud hozni azonosítókat.",
|
||||
"para_no_id": "Ez az elem nem rendelkezik ID-val. Kérlek, adj hozzá egyet az 'ui-lovelace.yaml' fájlban!",
|
||||
"para_migrate": "A Home Assistant a 'Konfiguráció áttelepítése' gomb megnyomásával az összes kártyához és nézethez automatikusan létre tud hozni ID-kat.",
|
||||
"migrate": "Konfiguráció áttelepítése"
|
||||
},
|
||||
"header": "Felhasználói felület szerkesztése",
|
||||
|
||||
@ -274,7 +274,7 @@
|
||||
},
|
||||
"alarm_control_panel": {
|
||||
"armed": "Attivo",
|
||||
"disarmed": "Disattivo",
|
||||
"disarmed": "Disattiva",
|
||||
"armed_home": "Attivo",
|
||||
"armed_away": "Attivo",
|
||||
"armed_night": "Attivo",
|
||||
@ -368,7 +368,7 @@
|
||||
"introduction": "I trigger sono ciò che avvia l'elaborazione di una regola di automazione. È possibile specificare più trigger per la stessa regola. Una volta avviato il trigger, Home Assistant convaliderà le condizioni, se presenti, e chiamerà l'azione. \n\n [Ulteriori informazioni sui trigger.](Https:\/\/home-assistant.io\/docs\/automation\/trigger\/)",
|
||||
"add": "Aggiungi trigger",
|
||||
"duplicate": "Duplica",
|
||||
"delete": "Cancella",
|
||||
"delete": "Elimina",
|
||||
"delete_confirm": "Sicuro di voler eliminare?",
|
||||
"unsupported_platform": "Piattaforma non supportata: {platform}",
|
||||
"type_select": "Tipo di trigger",
|
||||
@ -423,6 +423,10 @@
|
||||
"event": "Evento",
|
||||
"enter": "Ingresso",
|
||||
"leave": "Uscita"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhook",
|
||||
"webhook_id": "ID Webhook"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -431,7 +435,7 @@
|
||||
"introduction": "Le condizioni sono una parte facoltativa di una regola di automazione e possono essere utilizzate per impedire che un'azione si verifichi quando viene attivata. Le condizioni sembrano molto simili ai trigger, ma sono molto diverse. Un trigger analizzerà gli eventi che si verificano nel sistema mentre una condizione analizza solo l'aspetto del sistema in questo momento. Un trigger può osservare che un interruttore è in fase di accensione. Una condizione può vedere solo se un interruttore è attivo o meno. \n\n [Ulteriori informazioni sulle condizioni.](Https:\/\/home-assistant.io\/docs\/scripts\/conditions\/)",
|
||||
"add": "Aggiungi condizione",
|
||||
"duplicate": "Duplica",
|
||||
"delete": "Cancella",
|
||||
"delete": "Elimina",
|
||||
"delete_confirm": "Sicuro di voler eliminare?",
|
||||
"unsupported_condition": "Condizione non supportata: {condition}",
|
||||
"type_select": "Tipo di condizione",
|
||||
@ -476,7 +480,7 @@
|
||||
"introduction": "Le azioni sono ciò che Home Assistant farà quando un trigger attiva un automazione. \n\n [Ulteriori informazioni sulle azioni.](Https:\/\/home-assistant.io\/docs\/automation\/action\/)",
|
||||
"add": "Aggiungi azione",
|
||||
"duplicate": "Duplica",
|
||||
"delete": "Cancella",
|
||||
"delete": "Elimina",
|
||||
"delete_confirm": "Sicuro di voler eliminare?",
|
||||
"unsupported_action": "Azione non supportata: {action}",
|
||||
"type_select": "Tipo di azione",
|
||||
@ -500,7 +504,7 @@
|
||||
"event": {
|
||||
"label": "Scatena Evento",
|
||||
"event": "Evento:",
|
||||
"service_data": "Dato servizio"
|
||||
"service_data": "Dati servizio"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -531,7 +535,7 @@
|
||||
"cloud": {
|
||||
"caption": "Home Assistant Cloud",
|
||||
"description_login": "Connesso come {email}",
|
||||
"description_not_login": "Non loggato"
|
||||
"description_not_login": "Accesso non effettuato"
|
||||
},
|
||||
"integrations": {
|
||||
"caption": "integrazioni",
|
||||
@ -547,7 +551,7 @@
|
||||
"delete_confirm": "Sei sicuro di voler eliminare questa integrazione?",
|
||||
"restart_confirm": "Riavvia Home Assistant per terminare la rimozione di questa integrazione",
|
||||
"manuf": "da {manufacturer}",
|
||||
"hub": "Connesso via",
|
||||
"hub": "Connesso tramite",
|
||||
"firmware": "Firmware: {version}",
|
||||
"device_unavailable": "dispositivo non disponibile",
|
||||
"entity_unavailable": "entità non disponibile"
|
||||
@ -596,7 +600,7 @@
|
||||
"create_failed": "Impossibile creare il token di accesso.",
|
||||
"prompt_name": "Nome?",
|
||||
"prompt_copy_token": "Copia il tuo token di accesso. Non verrà più mostrato.",
|
||||
"empty_state": "Non hai ancora token di accesso di lunga durata.",
|
||||
"empty_state": "Non hai ancora un token di accesso di lunga durata.",
|
||||
"last_used": "Utilizzato l'ultima volta il {date} da {location}",
|
||||
"not_used": "Non è mai stato usato"
|
||||
},
|
||||
@ -726,13 +730,32 @@
|
||||
"edit_card": {
|
||||
"header": "Configurazione della scheda",
|
||||
"save": "Salva",
|
||||
"toggle_editor": "Attiva \/ disattiva l'editor"
|
||||
"toggle_editor": "Attiva \/ disattiva l'editor",
|
||||
"pick_card": "Scegliere la scheda che si desidera aggiungere.",
|
||||
"add": "Aggiungi scheda",
|
||||
"edit": "Modifica",
|
||||
"delete": "Elimina"
|
||||
},
|
||||
"migrate": {
|
||||
"header": "Configurazione incompatibile",
|
||||
"para_no_id": "Questo elemento non ha un ID. Aggiungi un ID a questo elemento in 'ui-lovelace.yaml'.",
|
||||
"para_migrate": "Home Assistant può aggiungere automaticamente gli ID a tutte le tue schede e visualizzazioni automaticamente premendo il pulsante \"Eporta configurazione\".",
|
||||
"migrate": "Esporta configurazione"
|
||||
},
|
||||
"header": "Modifica dell'interfaccia utente",
|
||||
"configure_ui": "Configurare l'interfaccia utente",
|
||||
"edit_view": {
|
||||
"header": "Visualizza configurazione",
|
||||
"add": "Aggiungi vista",
|
||||
"edit": "Modifica vista",
|
||||
"delete": "Cancella vista"
|
||||
},
|
||||
"save_config": {
|
||||
"header": "Prendi il controllo della tua interfaccia utente di Lovelace",
|
||||
"para": "Per impostazione predefinita, Home Assistant manterrà l'interfaccia utente, aggiornandola quando nuove entità o componenti Lovelace diventano disponibili. Se prendi il controllo non effettueremo più automaticamente le modifiche per te.",
|
||||
"para_sure": "Sei sicuro di voler prendere il controllo della tua interfaccia utente?",
|
||||
"cancel": "Rinuncia",
|
||||
"save": "Prendere il controllo"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -743,7 +766,8 @@
|
||||
},
|
||||
"common": {
|
||||
"loading": "Caricamento",
|
||||
"cancel": "Annulla"
|
||||
"cancel": "Annulla",
|
||||
"save": "Salva"
|
||||
},
|
||||
"duration": {
|
||||
"day": "{count} {count, plural,\none {giorno}\nother {giorni}\n}",
|
||||
@ -801,8 +825,8 @@
|
||||
"alarm_control_panel": {
|
||||
"code": "Codice",
|
||||
"clear_code": "Canc",
|
||||
"disarm": "Disattivato",
|
||||
"arm_home": "Attivo in casa",
|
||||
"disarm": "Disattiva",
|
||||
"arm_home": "Attiva In casa",
|
||||
"arm_away": "Attiva Fuori Casa",
|
||||
"arm_night": "Attiva Notte",
|
||||
"armed_custom_bypass": "Attiva con bypass"
|
||||
|
||||
@ -423,6 +423,10 @@
|
||||
"event": "이벤트:",
|
||||
"enter": "입장",
|
||||
"leave": "퇴장"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhook",
|
||||
"webhook_id": "Webhook ID"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -751,7 +755,7 @@
|
||||
"para": "기본적으로 Home Assistant 는 사용자 인터페이스를 유지 관리하고, 사용할 수 있는 새로운 구성요소 또는 Lovelace 구성요소가 있을 때 업데이트를 합니다. 사용자가 직접 관리하는 경우 Home Assistant 는 더 이상 자동으로 변경하지 않습니다.",
|
||||
"para_sure": "사용자 인터페이스를 직접 관리하시겠습니까?",
|
||||
"cancel": "아닙니다",
|
||||
"save": "직접 관리할께요"
|
||||
"save": "직접 관리할게요"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -275,9 +275,9 @@
|
||||
"alarm_control_panel": {
|
||||
"armed": "Aktivéiert",
|
||||
"disarmed": "Desaktivéieren",
|
||||
"armed_home": "Aktivéiert",
|
||||
"armed_away": "Aktivéiert",
|
||||
"armed_night": "Aktivéiert",
|
||||
"armed_home": "Uzbrojony",
|
||||
"armed_away": "Uzbrojony",
|
||||
"armed_night": "Uzbrojony",
|
||||
"pending": "Ustoend",
|
||||
"arming": "Aktivéieren",
|
||||
"disarming": "Desaktivéieren",
|
||||
@ -317,7 +317,7 @@
|
||||
"description": "Konfiguratioun validéieren an de Server kontrolléieren",
|
||||
"section": {
|
||||
"core": {
|
||||
"header": "Konfiguratioun an Server Kontroll",
|
||||
"header": "Konfiguracja i konktrola serwera",
|
||||
"introduction": "D'Ännere vun der Konfiguratioun kann e lästege Prozess sinn. Mir wëssen dat. Dës Sektioun probéiert fir Äert Liewen e bësse méi einfach ze maachen.",
|
||||
"validation": {
|
||||
"heading": "Validatioun vun der Konfiguratioun",
|
||||
@ -419,10 +419,14 @@
|
||||
"zone": {
|
||||
"label": "Zone",
|
||||
"entity": "Entitéit mam Standuert",
|
||||
"zone": "Zon",
|
||||
"zone": "Strefa",
|
||||
"event": "Evenement:",
|
||||
"enter": "Eran",
|
||||
"leave": "Verloossen"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhook",
|
||||
"webhook_id": "Webhook ID"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -441,7 +445,7 @@
|
||||
"state": "Zoustand"
|
||||
},
|
||||
"numeric_state": {
|
||||
"label": "Numereschen Zoustand",
|
||||
"label": "Stan (numeryczny)",
|
||||
"above": "Iwwert",
|
||||
"below": "Ënnert",
|
||||
"value_template": "Wäerte Modell (optional)"
|
||||
@ -465,15 +469,15 @@
|
||||
"before": "Virdrun"
|
||||
},
|
||||
"zone": {
|
||||
"label": "Zon",
|
||||
"label": "Strefa",
|
||||
"entity": "Entitéit mam Standuert",
|
||||
"zone": "Zon"
|
||||
"zone": "Strefa"
|
||||
}
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"header": "Aktiounen",
|
||||
"introduction": "Aktioune déi den HomeAssistant ausféiert wann den Automatisme ausgeléist gouf.\n[Léier méi iwwert Aktioune.](https:\/\/home-assistant.io\/docs\/automation\/action\/)",
|
||||
"introduction": "Aktioune déi den Home Assistant ausféiert wann den Automatisme ausgeléist gouf.\n[Léier méi iwwert Aktioune.](https:\/\/home-assistant.io\/docs\/automation\/action\/)",
|
||||
"add": "Aktioun dobäisetzen",
|
||||
"duplicate": "Replikéiere",
|
||||
"delete": "Läschen",
|
||||
@ -487,7 +491,7 @@
|
||||
},
|
||||
"delay": {
|
||||
"label": "Delai",
|
||||
"delay": "Delai"
|
||||
"delay": "Opóźnienie"
|
||||
},
|
||||
"wait_template": {
|
||||
"label": "Waart",
|
||||
@ -581,7 +585,7 @@
|
||||
"created_at": "Erstallt um {date}",
|
||||
"confirm_delete": "Sécher fir den Erneierungs Token fir {name} ze läsche?",
|
||||
"delete_failed": "Fehler beim läschen vum Erneierungs Token",
|
||||
"last_used": "Läscht benotz um {date} vun {location}",
|
||||
"last_used": "Fir d'Läscht benotzt um {date} vun {location}",
|
||||
"not_used": "Nach nie benotzt ginn",
|
||||
"current_token_tooltip": "Fehler beim läschen vum aktuellen Erneierungs Token"
|
||||
},
|
||||
@ -748,6 +752,7 @@
|
||||
},
|
||||
"save_config": {
|
||||
"header": "Kontroll iwwert Loveloce UI iwwerhuelen",
|
||||
"para": "Standardméisseg verwalt Home Assistant de Benotzer Interface an aktualiséiert en soubal nei Entitéiten oder Lovelace-Komponenten disponibel sinn. Wann dir d'Kontrolle iwwerhuelt, kënne mir keng automatesch Ännerung méi fir iech maachen.",
|
||||
"para_sure": "Sécher fir d'Kontrolle iwwert de Benotzer Interface z'iwwerhuelen?",
|
||||
"cancel": "Vergiess et",
|
||||
"save": "Kontroll iwwerhuelen"
|
||||
@ -870,7 +875,7 @@
|
||||
"actions": {
|
||||
"resume_cleaning": "Fuer mam botzen weider",
|
||||
"return_to_base": "Zeréck zur Statioun kommen",
|
||||
"start_cleaning": "Fänkt mam botzen un",
|
||||
"start_cleaning": "Fänk mam botzen un",
|
||||
"turn_on": "Uschalten",
|
||||
"turn_off": "Ausschalten"
|
||||
}
|
||||
@ -910,8 +915,8 @@
|
||||
}
|
||||
},
|
||||
"notification_toast": {
|
||||
"entity_turned_on": "{entity} gouf ausgeschalt",
|
||||
"entity_turned_off": "{entity} gouf ugeschalt",
|
||||
"entity_turned_on": "{entity} gouf ugeschalt",
|
||||
"entity_turned_off": "{entity} gouf ausgeschalt",
|
||||
"service_called": "Service {service} operuff",
|
||||
"service_call_failed": "Fehler beim opruffen vun {service}",
|
||||
"connection_lost": "Verbindung verluer. Verbindung gëtt nees opgebaut..."
|
||||
|
||||
@ -317,7 +317,7 @@
|
||||
"description": "Veiciet konfigurācijas failu pārbaudi un pārvaldiet serveri",
|
||||
"section": {
|
||||
"core": {
|
||||
"header": "Konfigurācijas un servera pārvaldība",
|
||||
"header": "Konfigurācijas un Servera pārvaldība",
|
||||
"introduction": "Izmaiņas konfigurācijā var būt nogurdinošs process. Mēs zinām. Šai sadaļai vajadzētu padarīt dzīvi mazliet vieglāku.",
|
||||
"validation": {
|
||||
"heading": "Konfigurācijas pārbaude",
|
||||
@ -351,7 +351,7 @@
|
||||
"caption": "Automatizācija",
|
||||
"description": "Veidojiet un rediģējiet automatizācijas",
|
||||
"picker": {
|
||||
"header": "Automatizāciju redaktors",
|
||||
"header": "Automatizāciju Redaktors",
|
||||
"introduction": "Automatizācijas redaktors ļauj jums izveidot un rediģēt automatizācijas. Lūdzu, izlasiet [norādījumus] (https:\/\/home-assistant.io\/docs\/automation\/editor\/), lai pārliecinātos, ka esat pareizi konfigurējis Home Assistant.",
|
||||
"pick_automation": "Izvēlieties automatizāciju kuru rediģēt",
|
||||
"no_automations": "Mēs nevarējām atrast rediģējamas automatizācijas",
|
||||
@ -423,6 +423,10 @@
|
||||
"event": "Notikums:",
|
||||
"enter": "Ieiet",
|
||||
"leave": "Iziet"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhook",
|
||||
"webhook_id": "Webhook ID"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -713,6 +717,47 @@
|
||||
"required_fields": "Aizpildiet visus obligātos laukus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"lovelace": {
|
||||
"cards": {
|
||||
"shopping-list": {
|
||||
"checked_items": "Atzīmētie vienumi",
|
||||
"clear_items": "Notīrīt atzīmētos vienumus",
|
||||
"add_item": "Pievienot vienumu"
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"edit_card": {
|
||||
"header": "Kartes Konfigurācija",
|
||||
"save": "Saglabāt",
|
||||
"toggle_editor": "Pārslēgt Redaktoru",
|
||||
"pick_card": "Izvēlieties karti, kuru vēlaties pievienot.",
|
||||
"add": "Pievienot karti",
|
||||
"edit": "Rediģēt",
|
||||
"delete": "Dzēst"
|
||||
},
|
||||
"migrate": {
|
||||
"header": "Konfigurācija Nesaderīga",
|
||||
"para_no_id": "Šim elementam nav ID. Lūdzu, pievienojiet ID šim elementam 'ui-lovelace.yaml' failā.",
|
||||
"para_migrate": "Home Assistant var automātiski pievienot ID visām kartēm un skatiem, nospiežot pogu \"Pārvietot konfigurāciju\".",
|
||||
"migrate": "Pārvietot konfigurāciju"
|
||||
},
|
||||
"header": "Rediģēt lietotāja interfeisu",
|
||||
"configure_ui": "Konfigurēt lietotāja interfeisu",
|
||||
"edit_view": {
|
||||
"header": "Skatīt konfigurāciju",
|
||||
"add": "Pievienot skatu",
|
||||
"edit": "Rediģēt skatu",
|
||||
"delete": "Dzēst skatu"
|
||||
},
|
||||
"save_config": {
|
||||
"header": "Pārņemt kontroli pār savu Lovelace UI",
|
||||
"para": "Pēc noklusējuma Home Assistant uzturēs jūsu lietotāja interfeisu, atjauninot to, kad būs pieejami jauni objekti vai Lovelace komponenti. Ja jūs uzņematies kontroli, mēs jums vairs neveiksim izmaiņas automātiski jūsu vietā.",
|
||||
"para_sure": "Vai tiešām vēlaties kontrolēt savu lietotāja interfeisu?",
|
||||
"cancel": "Nekas",
|
||||
"save": "Pārņemt kontroli"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sidebar": {
|
||||
@ -721,7 +766,8 @@
|
||||
},
|
||||
"common": {
|
||||
"loading": "Ielāde",
|
||||
"cancel": "Atcelt"
|
||||
"cancel": "Atcelt",
|
||||
"save": "Saglabāt"
|
||||
},
|
||||
"duration": {
|
||||
"day": "{count} {count, plural,\none {diena}\nother {dienas}\n}",
|
||||
@ -778,10 +824,12 @@
|
||||
},
|
||||
"alarm_control_panel": {
|
||||
"code": "Kods",
|
||||
"clear_code": "Dzēst",
|
||||
"clear_code": "Notīrīt",
|
||||
"disarm": "Atslēgt",
|
||||
"arm_home": "Pieslēgt mājas",
|
||||
"arm_away": "Pieslēgt prombūtni"
|
||||
"arm_away": "Pieslēgt prombūtni",
|
||||
"arm_night": "Pieslēgts uz nakti",
|
||||
"armed_custom_bypass": "Pielāgots apvedceļš"
|
||||
},
|
||||
"automation": {
|
||||
"last_triggered": "Pēdējais izsaukums",
|
||||
|
||||
@ -236,7 +236,7 @@
|
||||
"ready": "Gereed"
|
||||
},
|
||||
"query_stage": {
|
||||
"initializing": "Initialiseren ({query_stage})",
|
||||
"initializing": "Voorbereiden ({query_stage})",
|
||||
"dead": "Onbereikbaar ({query_stage})"
|
||||
}
|
||||
},
|
||||
@ -248,7 +248,7 @@
|
||||
"lightning": "Bliksem",
|
||||
"lightning-rainy": "Bliksem, regenachtig",
|
||||
"partlycloudy": "Gedeeltelijk bewolkt",
|
||||
"pouring": "Gieten",
|
||||
"pouring": "Regen",
|
||||
"rainy": "Regenachtig",
|
||||
"snowy": "Sneeuwachtig",
|
||||
"snowy-rainy": "Sneeuw-, regenachtig",
|
||||
@ -276,8 +276,8 @@
|
||||
"armed": "Actief",
|
||||
"disarmed": "Uit",
|
||||
"armed_home": "Actief",
|
||||
"armed_away": "Ingeschakeld",
|
||||
"armed_night": "Ingeschakeld",
|
||||
"armed_away": "Actief",
|
||||
"armed_night": "Actief",
|
||||
"pending": "Wacht",
|
||||
"arming": "Activeren",
|
||||
"disarming": "Uitschakelen",
|
||||
@ -301,7 +301,7 @@
|
||||
"period": "Periode"
|
||||
},
|
||||
"logbook": {
|
||||
"showing_entries": "Toon items voor"
|
||||
"showing_entries": "Toont gegevens van"
|
||||
},
|
||||
"mailbox": {
|
||||
"empty": "Je hebt geen berichten",
|
||||
@ -423,6 +423,10 @@
|
||||
"event": "Gebeurtenis:",
|
||||
"enter": "Betreden",
|
||||
"leave": "Verlaten"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhook",
|
||||
"webhook_id": "Webhook ID"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -441,7 +445,7 @@
|
||||
"state": "Staat"
|
||||
},
|
||||
"numeric_state": {
|
||||
"label": "Numerieke status",
|
||||
"label": "Numerieke staat",
|
||||
"above": "Boven",
|
||||
"below": "Onder",
|
||||
"value_template": "Waardetemplate (optioneel)"
|
||||
@ -726,13 +730,32 @@
|
||||
"edit_card": {
|
||||
"header": "Kaart configuratie",
|
||||
"save": "Opslaan",
|
||||
"toggle_editor": "Toggle Editor"
|
||||
"toggle_editor": "Toggle Editor",
|
||||
"pick_card": "Kies de kaart die je wilt toevoegen.",
|
||||
"add": "Kaart toevoegen",
|
||||
"edit": "Bewerken",
|
||||
"delete": "Verwijder"
|
||||
},
|
||||
"migrate": {
|
||||
"header": "Configuratie incompatibel",
|
||||
"para_no_id": "Dit element heeft geen ID. Voeg een ID toe aan dit element in 'ui-lovelace.yaml'.",
|
||||
"para_migrate": "Home Assistant kan ID's voor al je kaarten en weergaven automatisch voor je toevoegen door op de knop 'Migrate config' te klikken.",
|
||||
"migrate": "Configuratie migreren"
|
||||
},
|
||||
"header": "Bewerk UI",
|
||||
"configure_ui": "Configureer UI",
|
||||
"edit_view": {
|
||||
"header": "Bekijk de configuratie",
|
||||
"add": "Weergave toevoegen",
|
||||
"edit": "Weergave bewerken",
|
||||
"delete": "Weergave verwijderen"
|
||||
},
|
||||
"save_config": {
|
||||
"header": "Neem de controle over uw Lovelace UI",
|
||||
"para": "Normaal gesproken onderhoudt Home Assistant je gebruikersinterface en update die met nieuwe entiteiten of Lovelace-onderdelen wanneer deze beschikbaar zijn. Als je het beheer overneemt, zullen we niet langer automatisch wijzigingen aanbrengen.",
|
||||
"para_sure": "Weet je zeker dat je de controle wilt over je gebruikersinterface?",
|
||||
"cancel": "Laat maar",
|
||||
"save": "Neem over"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -743,7 +766,8 @@
|
||||
},
|
||||
"common": {
|
||||
"loading": "Bezig met laden",
|
||||
"cancel": "Annuleren"
|
||||
"cancel": "Annuleren",
|
||||
"save": "Opslaan"
|
||||
},
|
||||
"duration": {
|
||||
"day": "{count} {count, plural,\none {dag}\nother {dagen}\n}",
|
||||
|
||||
@ -145,7 +145,7 @@
|
||||
"high_demand": "Høg etterspurnad",
|
||||
"heat_pump": "Varmepumpe",
|
||||
"gas": "Gass",
|
||||
"manual": "Håndbok"
|
||||
"manual": "Handbok"
|
||||
},
|
||||
"configurator": {
|
||||
"configure": "Konfigurerer",
|
||||
@ -423,6 +423,10 @@
|
||||
"event": "Hending:",
|
||||
"enter": "Kjem",
|
||||
"leave": "Forlet"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhook",
|
||||
"webhook_id": "Webhook ID"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -545,12 +549,12 @@
|
||||
"no_devices": "Essa integração não possui dispositivos.",
|
||||
"no_device": "Entidades sem dispositivos.",
|
||||
"delete_confirm": "Voc^tem certeza que deseja apagar essa integração?",
|
||||
"restart_confirm": "Reinicie Home Assistant para finalizar essa integração",
|
||||
"manuf": "por {fabricante}",
|
||||
"hub": "Conectado via",
|
||||
"firmware": "Firmware: {versão}",
|
||||
"device_unavailable": "Dispositivo indisponível",
|
||||
"entity_unavailable": "Entidade indisponível"
|
||||
"restart_confirm": "Restart Home Assistant for å fjerne denne integrasjonen",
|
||||
"manuf": "av {manufacturer}",
|
||||
"hub": "Tilkopla via",
|
||||
"firmware": "Firmware: {version}",
|
||||
"device_unavailable": "Eininga utilgjengelig",
|
||||
"entity_unavailable": "Oppføringa utilgjengelig"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -600,11 +604,11 @@
|
||||
"last_used": "Sist brukt den {date} frå {location}",
|
||||
"not_used": "Har aldri vore brukt"
|
||||
},
|
||||
"current_user": "Você está atualmente conectado como {NomeCompleto}.",
|
||||
"current_user": "Du er for augeblinken logga inn som {fullName}.",
|
||||
"is_owner": "Du er ein eigar",
|
||||
"logout": "Sair",
|
||||
"logout": "Logg ut",
|
||||
"change_password": {
|
||||
"header": "Mudar Senha",
|
||||
"header": "Bytt passord",
|
||||
"current_password": "Senha Atual",
|
||||
"new_password": "Nova Senha",
|
||||
"confirm_new_password": "Confirme Nova Senha",
|
||||
@ -713,6 +717,47 @@
|
||||
"required_fields": "Fyll ut dei nødvendige felta"
|
||||
}
|
||||
}
|
||||
},
|
||||
"lovelace": {
|
||||
"cards": {
|
||||
"shopping-list": {
|
||||
"checked_items": "Markerte element",
|
||||
"clear_items": "Fjern dei markerrte elementa",
|
||||
"add_item": "Legg til element"
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"edit_card": {
|
||||
"header": "Kortkonfigurasjon",
|
||||
"save": "Lagre",
|
||||
"toggle_editor": "Bytt redigeringsverktøy",
|
||||
"pick_card": "Vel kortet du vil legge til.",
|
||||
"add": "Legg til kort",
|
||||
"edit": "Redigere",
|
||||
"delete": "Slett"
|
||||
},
|
||||
"migrate": {
|
||||
"header": "Konfigurasjonen er ikkje kompatibel",
|
||||
"para_no_id": "Dette elementet har ikkje ein ID. Ver vennleg og legg til ein ID til dette elementet i \"ui-lovelace.yaml\"-fila di.",
|
||||
"para_migrate": "Home assistant kan legge til ID-ar til alle korta og sidene dine automatisk for deg ved å trykke \"Overfør konfigurasjon\"-knappen.",
|
||||
"migrate": "Overfør konfigurasjon"
|
||||
},
|
||||
"header": "Rediger brukargrensesnitt",
|
||||
"configure_ui": "Konfigurer brukargrensesnitt",
|
||||
"edit_view": {
|
||||
"header": "Vis konfigurasjon",
|
||||
"add": "Legg til side",
|
||||
"edit": "Rediger sida",
|
||||
"delete": "Slett sida"
|
||||
},
|
||||
"save_config": {
|
||||
"header": "Ta kontroll over Lovelace-brukargrensesnittet",
|
||||
"para": "Som standard kjem Home Assistant til å vedlikehalde brukargrensesnittet, og oppdatere det når nye oppføringar eller Lovelace-komponentar vert tilgjengelege. Dersom du tek kontroll, vil vi ikkje lenger kunne lage dette til automatisk for deg.",
|
||||
"para_sure": "Er du sikker på at du vil ta kontroll over brukergrensesnittet ditt?",
|
||||
"cancel": "Gløym det",
|
||||
"save": "Ta kontroll"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sidebar": {
|
||||
@ -721,7 +766,8 @@
|
||||
},
|
||||
"common": {
|
||||
"loading": "Lastar",
|
||||
"cancel": "Avbryt\n"
|
||||
"cancel": "Avbryt\n",
|
||||
"save": "Lagre"
|
||||
},
|
||||
"duration": {
|
||||
"day": "{count} {count, plural,\none {dag}\nother {dagar}\n}",
|
||||
@ -782,8 +828,8 @@
|
||||
"disarm": "Skru av",
|
||||
"arm_home": "Heimemodus",
|
||||
"arm_away": "Bortemodus",
|
||||
"arm_night": "Acionamento noturno",
|
||||
"armed_custom_bypass": "Atalho configurado"
|
||||
"arm_night": "Aktiver natt",
|
||||
"armed_custom_bypass": "Tilpassa bypass"
|
||||
},
|
||||
"automation": {
|
||||
"last_triggered": "Sist utløyst",
|
||||
@ -835,11 +881,11 @@
|
||||
}
|
||||
},
|
||||
"water_heater": {
|
||||
"currently": "Atualmente",
|
||||
"on_off": "Liga \/ desliga",
|
||||
"target_temperature": "Temperatura desejada",
|
||||
"operation": "Operação",
|
||||
"away_mode": "Modo distante"
|
||||
"currently": "For augeblinken",
|
||||
"on_off": "På \/ av",
|
||||
"target_temperature": "Temperaturmål",
|
||||
"operation": "Operasjon",
|
||||
"away_mode": "Bortemodus"
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
|
||||
@ -423,6 +423,10 @@
|
||||
"event": "Hendelse:",
|
||||
"enter": "Ankommer",
|
||||
"leave": "Forlater"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhook",
|
||||
"webhook_id": "Webhook ID"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -823,7 +827,7 @@
|
||||
"clear_code": "Klarer",
|
||||
"disarm": "Deaktiver",
|
||||
"arm_home": "Armer hjemme",
|
||||
"arm_away": "Armer bort",
|
||||
"arm_away": "Armer borte",
|
||||
"arm_night": "Armer natt",
|
||||
"armed_custom_bypass": "Tilpasset bypass"
|
||||
},
|
||||
|
||||
@ -423,6 +423,10 @@
|
||||
"event": "Zdarzenie",
|
||||
"enter": "Wprowadź",
|
||||
"leave": "Opuść"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhook",
|
||||
"webhook_id": "Identyfikator Webhook"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -482,7 +486,7 @@
|
||||
"type_select": "Typ akcji",
|
||||
"type": {
|
||||
"service": {
|
||||
"label": "Wykonanie usługi",
|
||||
"label": "Wywołanie usługi",
|
||||
"service_data": "Dane usługi"
|
||||
},
|
||||
"delay": {
|
||||
@ -490,7 +494,7 @@
|
||||
"delay": "Opóźnienie"
|
||||
},
|
||||
"wait_template": {
|
||||
"label": "Czekaj",
|
||||
"label": "Oczekiwanie",
|
||||
"wait_template": "Szablon czekania",
|
||||
"timeout": "Limit czasu (opcjonalnie)"
|
||||
},
|
||||
@ -498,7 +502,7 @@
|
||||
"label": "Warunek"
|
||||
},
|
||||
"event": {
|
||||
"label": "Uruchom zdarzenie",
|
||||
"label": "Wywołanie zdarzenia",
|
||||
"event": "Zdarzenie:",
|
||||
"service_data": "Dane usługi"
|
||||
}
|
||||
@ -821,11 +825,11 @@
|
||||
"alarm_control_panel": {
|
||||
"code": "Kod",
|
||||
"clear_code": "Wyczyść",
|
||||
"disarm": "Rozbrojony",
|
||||
"arm_home": "uzbrojony (w domu)",
|
||||
"arm_away": "uzbrojony (nieobecny)",
|
||||
"arm_night": "uzbrojony (noc)",
|
||||
"armed_custom_bypass": "uzbrojony (częściowo)"
|
||||
"disarm": "Rozbrojenie",
|
||||
"arm_home": "Uzbrojenie (w domu)",
|
||||
"arm_away": "Uzbrojenie (nieobecny)",
|
||||
"arm_night": "Uzbrojenie (noc)",
|
||||
"armed_custom_bypass": "Uzbrój (częściowo)"
|
||||
},
|
||||
"automation": {
|
||||
"last_triggered": "Ostatnie uruchomienie",
|
||||
|
||||
@ -423,6 +423,10 @@
|
||||
"event": "Evento:",
|
||||
"enter": "Entrar",
|
||||
"leave": "Sair"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhook",
|
||||
"webhook_id": "ID da Webhook"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -726,13 +730,32 @@
|
||||
"edit_card": {
|
||||
"header": "Configuração de cartão",
|
||||
"save": "Salvar",
|
||||
"toggle_editor": "Alternar Editor"
|
||||
"toggle_editor": "Alternar Editor",
|
||||
"pick_card": "Escolha o cartão que você deseja adicionar.",
|
||||
"add": "Adicionar cartão",
|
||||
"edit": "Editar",
|
||||
"delete": "Excluir"
|
||||
},
|
||||
"migrate": {
|
||||
"header": "Configuração Incompatível",
|
||||
"para_no_id": "Este elemento não possui um ID. Por favor adicione um ID a este elemento em 'ui-lovelace.yaml'.",
|
||||
"para_migrate": "O Home Assistant pode adicionar IDs a todos os seus cards e visualizações automaticamente clicando no botão 'Migrar config'.",
|
||||
"migrate": "Migrar configuração"
|
||||
},
|
||||
"header": "Editar “interface” do usuário",
|
||||
"configure_ui": "Configurar “interface” do usuário",
|
||||
"edit_view": {
|
||||
"header": "Configurações",
|
||||
"add": "Editar visualização",
|
||||
"edit": "Editar visualização",
|
||||
"delete": "Excluir visualização"
|
||||
},
|
||||
"save_config": {
|
||||
"header": "Assuma o controle da sua interface do Lovelace",
|
||||
"para": "Por padrão, o Home Assistant manterá sua interface de usuário, atualizando-a quando novas entidades ou componentes do Lovelace estiverem disponíveis. Se você assumir o controle, não faremos mais alterações automaticamente para você.",
|
||||
"para_sure": "Tem certeza de que deseja assumir o controle da sua interface de usuário?",
|
||||
"cancel": "Nunca",
|
||||
"save": "Assuma o controle"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -743,7 +766,8 @@
|
||||
},
|
||||
"common": {
|
||||
"loading": "Carregando",
|
||||
"cancel": "Cancelar"
|
||||
"cancel": "Cancelar",
|
||||
"save": "Salvar"
|
||||
},
|
||||
"duration": {
|
||||
"day": "{count} {count, plural,\none {dia}\nother {dias}\n}",
|
||||
|
||||
@ -273,16 +273,16 @@
|
||||
"unavailable": "Indisp"
|
||||
},
|
||||
"alarm_control_panel": {
|
||||
"armed": "Armad",
|
||||
"armed": "Armado",
|
||||
"disarmed": "Desarm",
|
||||
"armed_home": "Armad",
|
||||
"armed_away": "Armad",
|
||||
"armed_night": "Armad",
|
||||
"armed_home": "Armado",
|
||||
"armed_away": "Armado",
|
||||
"armed_night": "Armado",
|
||||
"pending": "Pend",
|
||||
"arming": "A armar",
|
||||
"disarming": "Desarmar",
|
||||
"triggered": "Disp",
|
||||
"armed_custom_bypass": "Armad"
|
||||
"armed_custom_bypass": "Armado"
|
||||
},
|
||||
"device_tracker": {
|
||||
"home": "Casa",
|
||||
@ -423,6 +423,10 @@
|
||||
"event": "Evento:",
|
||||
"enter": "Entrar",
|
||||
"leave": "Sair"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "",
|
||||
"webhook_id": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -601,7 +605,7 @@
|
||||
"not_used": "Nunca foi utilizado"
|
||||
},
|
||||
"current_user": "Esta actualmente ligado como {fullName}",
|
||||
"is_owner": "Você é o proprietário.",
|
||||
"is_owner": "Você é um proprietário.",
|
||||
"logout": "Sair",
|
||||
"change_password": {
|
||||
"header": "Alterar palavra-passe",
|
||||
@ -726,13 +730,32 @@
|
||||
"edit_card": {
|
||||
"header": "Configuração do cartão",
|
||||
"save": "Guardar",
|
||||
"toggle_editor": "Alterar para editor"
|
||||
"toggle_editor": "Alterar para editor",
|
||||
"pick_card": "Escolha o cartão que deseja adicionar.",
|
||||
"add": "Adicionar Cartão",
|
||||
"edit": "Editar",
|
||||
"delete": "Apagar"
|
||||
},
|
||||
"migrate": {
|
||||
"header": "Configuração Incompatível",
|
||||
"para_no_id": "Este elemento não possui um ID. Por favor adicione um ID a este elemento em 'ui-lovelace.yaml'.",
|
||||
"para_migrate": "O Home Assistant pode adicionar IDs a todos os seus cartões e vistas automaticamente clicando no botão 'Migrar configuração'.",
|
||||
"migrate": "Migrar configuração"
|
||||
},
|
||||
"header": "Editar UI",
|
||||
"configure_ui": "Configurar UI",
|
||||
"edit_view": {
|
||||
"header": "Ver configuração",
|
||||
"add": "Acrescentar vista",
|
||||
"edit": "Editar vista",
|
||||
"delete": "Apagar a vista"
|
||||
},
|
||||
"save_config": {
|
||||
"header": "Assumir controle sobre a interface do Lovelace",
|
||||
"para": "Por omissão o Home Assistant irá manter a sua interface de utilizador, actualizando sempre que uma entidade nova ou componentes Lovelace fiquem disponíveis. Se assumir o controle não será possivel fazer alterações automáticas por si.",
|
||||
"para_sure": "Tem certeza que deseja assumir o controle sobre a interface de utilizador?",
|
||||
"cancel": "Cancelar",
|
||||
"save": "Assumir o controle"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -743,7 +766,8 @@
|
||||
},
|
||||
"common": {
|
||||
"loading": "A carregar",
|
||||
"cancel": "Cancelar"
|
||||
"cancel": "Cancelar",
|
||||
"save": "Guardar"
|
||||
},
|
||||
"duration": {
|
||||
"day": "{count} {count, plural,\n one {dia}\n other {dias}\n}",
|
||||
|
||||
@ -101,8 +101,8 @@
|
||||
"on": "Охлаждение"
|
||||
},
|
||||
"door": {
|
||||
"off": "Закрыто",
|
||||
"on": "Открыто"
|
||||
"off": "Закрыта",
|
||||
"on": "Открыта"
|
||||
},
|
||||
"garage_door": {
|
||||
"off": "Закрыто",
|
||||
@ -182,20 +182,20 @@
|
||||
"problem": "Проблема"
|
||||
},
|
||||
"input_boolean": {
|
||||
"off": "Выключен",
|
||||
"on": "Включен"
|
||||
"off": "Выкл",
|
||||
"on": "Вкл"
|
||||
},
|
||||
"light": {
|
||||
"off": "Выключен",
|
||||
"on": "Включен"
|
||||
"off": "Выкл",
|
||||
"on": "Вкл"
|
||||
},
|
||||
"lock": {
|
||||
"locked": "Закрыт",
|
||||
"unlocked": "Открыт"
|
||||
},
|
||||
"media_player": {
|
||||
"off": "Выключен",
|
||||
"on": "Включен",
|
||||
"off": "Выкл",
|
||||
"on": "Вкл",
|
||||
"playing": "Воспроизведение",
|
||||
"paused": "Пауза",
|
||||
"idle": "Ожидание",
|
||||
@ -206,8 +206,8 @@
|
||||
"problem": "Проблема"
|
||||
},
|
||||
"remote": {
|
||||
"off": "Выключено",
|
||||
"on": "Включено"
|
||||
"off": "Выкл",
|
||||
"on": "Вкл"
|
||||
},
|
||||
"scene": {
|
||||
"scening": "Переход к сцене"
|
||||
@ -388,11 +388,11 @@
|
||||
"label": "Home Assistant",
|
||||
"event": "Событие:",
|
||||
"start": "Запуск",
|
||||
"shutdown": "Выключение"
|
||||
"shutdown": "Завершение работы"
|
||||
},
|
||||
"mqtt": {
|
||||
"label": "MQTT",
|
||||
"topic": "Топик",
|
||||
"topic": "Тема",
|
||||
"payload": "Значение (опционально)"
|
||||
},
|
||||
"numeric_state": {
|
||||
@ -423,6 +423,10 @@
|
||||
"event": "Событие:",
|
||||
"enter": "Войти",
|
||||
"leave": "Покинуть"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhook",
|
||||
"webhook_id": "Идентификатор Webhook"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -13,7 +13,8 @@
|
||||
"dev-templates": "Šablóny",
|
||||
"dev-mqtt": "MQTT",
|
||||
"dev-info": "Info",
|
||||
"calendar": "Kalendár"
|
||||
"calendar": "Kalendár",
|
||||
"profile": "Profil"
|
||||
},
|
||||
"state": {
|
||||
"default": {
|
||||
@ -116,8 +117,8 @@
|
||||
"on": "Otvorené"
|
||||
},
|
||||
"lock": {
|
||||
"off": "Zamknuté",
|
||||
"on": "Odomknuté"
|
||||
"off": "Zamknutý",
|
||||
"on": "Odomknutý"
|
||||
}
|
||||
},
|
||||
"calendar": {
|
||||
@ -166,17 +167,17 @@
|
||||
"on": "Zapnutý"
|
||||
},
|
||||
"group": {
|
||||
"off": "Vypnuté",
|
||||
"on": "Zapnuté",
|
||||
"off": "Vypnutá",
|
||||
"on": "Zapnutá",
|
||||
"home": "Doma",
|
||||
"not_home": "Preč",
|
||||
"open": "Otvorené",
|
||||
"open": "Otvorená",
|
||||
"opening": "Otvára sa",
|
||||
"closed": "Zatvorené",
|
||||
"closed": "Zatvorená",
|
||||
"closing": "Zatvára sa",
|
||||
"stopped": "Zastavené",
|
||||
"locked": "Zamknuté",
|
||||
"unlocked": "Odomknuté",
|
||||
"locked": "Zamknutá",
|
||||
"unlocked": "Odomknutá",
|
||||
"ok": "OK",
|
||||
"problem": "Problém"
|
||||
},
|
||||
@ -274,14 +275,14 @@
|
||||
"alarm_control_panel": {
|
||||
"armed": "Zakód",
|
||||
"disarmed": "Odkód",
|
||||
"armed_home": "Aktívny",
|
||||
"armed_away": "Aktívny",
|
||||
"armed_night": "Aktívny",
|
||||
"armed_home": "Zakód",
|
||||
"armed_away": "Zakód",
|
||||
"armed_night": "Zakód",
|
||||
"pending": "Čaká",
|
||||
"arming": "Aktivácia",
|
||||
"disarming": "Deakt",
|
||||
"triggered": "Alarm",
|
||||
"armed_custom_bypass": "Zapnutý"
|
||||
"armed_custom_bypass": "Zakódovaný"
|
||||
},
|
||||
"device_tracker": {
|
||||
"home": "Doma",
|
||||
@ -300,7 +301,7 @@
|
||||
"period": "Obdobie"
|
||||
},
|
||||
"logbook": {
|
||||
"showing_entries": "Zobrazujú sa záznamy pre"
|
||||
"showing_entries": "Zobrazujú sa záznamy za obdobie"
|
||||
},
|
||||
"mailbox": {
|
||||
"empty": "Nemáte žiadne správy",
|
||||
@ -380,7 +381,8 @@
|
||||
"state": {
|
||||
"label": "Stav",
|
||||
"from": "Z",
|
||||
"to": "Na"
|
||||
"to": "Na",
|
||||
"for": "Trvanie stavu"
|
||||
},
|
||||
"homeassistant": {
|
||||
"label": "Home Assistant",
|
||||
@ -421,6 +423,10 @@
|
||||
"event": "Udalosť:",
|
||||
"enter": "Vstúpenie",
|
||||
"leave": "Opustenie"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhook",
|
||||
"webhook_id": "Webhook ID"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -497,7 +503,7 @@
|
||||
},
|
||||
"event": {
|
||||
"label": "Odpáliť udalosť",
|
||||
"event": "Udalosť",
|
||||
"event": "Udalosť:",
|
||||
"service_data": "Dáta služby"
|
||||
}
|
||||
}
|
||||
@ -525,6 +531,31 @@
|
||||
"deactivate_user": "Deaktivovať používateľa",
|
||||
"delete_user": "Vymazať používateľa"
|
||||
}
|
||||
},
|
||||
"cloud": {
|
||||
"caption": "Home Assistant Cloud",
|
||||
"description_login": "Prihlásený ako {email}",
|
||||
"description_not_login": "Neprihlásený"
|
||||
},
|
||||
"integrations": {
|
||||
"caption": "Integrácie",
|
||||
"description": "Spravovať pripojené zariadenia a služby",
|
||||
"discovered": "Objavené",
|
||||
"configured": "Nakonfigurovaný",
|
||||
"new": "Nastaviť novú integráciu",
|
||||
"configure": "Konfigurovať",
|
||||
"none": "Nič zatiaľ nebolo nakonfigurované",
|
||||
"config_entry": {
|
||||
"no_devices": "Táto integrácia nemá žiadne zariadenia.",
|
||||
"no_device": "Entity bez zariadení",
|
||||
"delete_confirm": "Naozaj chcete odstrániť túto integráciu?",
|
||||
"restart_confirm": "Ak chcete dokončiť odstránenie tejto integrácie, reštartujte Home Assistant",
|
||||
"manuf": "od {manufacturer}",
|
||||
"hub": "Pripojené cez",
|
||||
"firmware": "Firmvér: {version}",
|
||||
"device_unavailable": "zariadenie nie je dostupné",
|
||||
"entity_unavailable": "Entita nie je dostupná"
|
||||
}
|
||||
}
|
||||
},
|
||||
"profile": {
|
||||
@ -546,20 +577,187 @@
|
||||
"error_no_theme": "Nie sú k dispozícii žiadne témy.",
|
||||
"link_promo": "Získajte viac informácií o témach",
|
||||
"dropdown_label": "Téma"
|
||||
},
|
||||
"refresh_tokens": {
|
||||
"header": "Obnovovacie Tokeny",
|
||||
"description": "Každý obnovovací token predstavuje prihlásenú reláciu. Obnovovacie tokeny sa po kliknutí na tlačidlo Odhlásiť sa automaticky odstránia. Pre váš účet sú aktívne nasledovné obnovovacie tokeny.",
|
||||
"token_title": "Obnovovací token pre {clientId}",
|
||||
"created_at": "Vytvorený {date}",
|
||||
"confirm_delete": "Naozaj chcete odstrániť obnovovací token pre {name} ?",
|
||||
"delete_failed": "Nepodarilo sa odstrániť obnovovací token",
|
||||
"last_used": "Naposledy použitý {date} z {location}",
|
||||
"not_used": "Nikdy nebol použitý",
|
||||
"current_token_tooltip": "Nedá sa odstrániť aktuálny obnovovací token"
|
||||
},
|
||||
"long_lived_access_tokens": {
|
||||
"header": "Prístupové tokeny s dlhou životnosťou",
|
||||
"description": "Vytvorte prístupové tokeny s dlhou životnosťou, ktoré umožnia vašim skriptom komunikovať s vašou inštanciou Home Assistant. Každý token bude platný 10 rokov od vytvorenia. Nasledujúce prístupové tokeny s dlhou životnosťou sú v súčasnosti aktívne.",
|
||||
"learn_auth_requests": "Zistite, ako vytvárať overené požiadavky.",
|
||||
"created_at": "Vytvorený {date}",
|
||||
"confirm_delete": "Naozaj chcete odstrániť prístupový token pre {name} ?",
|
||||
"delete_failed": "Nepodarilo sa odstrániť prístupový token.",
|
||||
"create": "Vytvoriť Token",
|
||||
"create_failed": "Nepodarilo sa vytvoriť prístupový token.",
|
||||
"prompt_name": "Názov?",
|
||||
"prompt_copy_token": "Skopírujte svoj nový prístupový token. Znova sa nezobrazí.",
|
||||
"empty_state": "Nemáte žiadne prístupové tokeny s dlhou životnosťou.",
|
||||
"last_used": "Naposledy použitý {date} z {location}",
|
||||
"not_used": "Nikdy nebol použitý"
|
||||
},
|
||||
"current_user": "Momentálne ste prihlásení ako {fullName} .",
|
||||
"is_owner": "Ste vlastníkom.",
|
||||
"logout": "Odhlásiť sa",
|
||||
"change_password": {
|
||||
"header": "Zmena hesla",
|
||||
"current_password": "Aktuálne heslo",
|
||||
"new_password": "Nové heslo",
|
||||
"confirm_new_password": "Potvrďte nové heslo",
|
||||
"error_required": "Požadované",
|
||||
"submit": "Odoslať"
|
||||
},
|
||||
"mfa": {
|
||||
"header": "Multifaktorové autentifikačné moduly",
|
||||
"disable": "Zakázať",
|
||||
"enable": "Povoliť",
|
||||
"confirm_disable": "Naozaj chcete zakázať {name} ?"
|
||||
},
|
||||
"mfa_setup": {
|
||||
"title_aborted": "Prerušené",
|
||||
"title_success": "Úspech!",
|
||||
"step_done": "Nastavenie dokončené krok {step} ",
|
||||
"close": "Zavrieť",
|
||||
"submit": "Odoslať"
|
||||
}
|
||||
},
|
||||
"page-authorize": {
|
||||
"initializing": "Inicializácia",
|
||||
"authorizing_client": "Chystáte sa poskytnúť {clientId} prístup k vašej inštancii Home Assistantu.",
|
||||
"logging_in_with": "Prihlasovanie pomocou ** {authProviderName} **.",
|
||||
"pick_auth_provider": "Alebo sa prihláste prostredníctvom",
|
||||
"abort_intro": "Prihlásenie bolo zrušené",
|
||||
"form": {
|
||||
"working": "Prosím čakajte",
|
||||
"unknown_error": "Niečo sa pokazilo",
|
||||
"providers": {
|
||||
"homeassistant": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"username": "Používateľské meno",
|
||||
"password": "Heslo"
|
||||
}
|
||||
},
|
||||
"mfa": {
|
||||
"data": {
|
||||
"code": "Kód dvojfaktorovej autentifikácie"
|
||||
},
|
||||
"description": "Otvorte ** {mfa_module_name} ** v zariadení, aby ste si pozreli svoj dvojfaktorový autentifikačný kód a overili svoju totožnosť:"
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"invalid_auth": "Nesprávne používateľské meno alebo heslo",
|
||||
"invalid_code": "Neplatný overovací kód"
|
||||
},
|
||||
"abort": {
|
||||
"login_expired": "Platnosť relácie skončila, prosím prihláste znova."
|
||||
}
|
||||
},
|
||||
"legacy_api_password": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"password": "Heslo API rozhrania"
|
||||
},
|
||||
"description": "Prosím zadajte heslo API rozhrania v konfigurácií http:"
|
||||
},
|
||||
"mfa": {
|
||||
"data": {
|
||||
"code": "Kód dvojfaktorovej autentifikácie"
|
||||
},
|
||||
"description": "Otvorte **{mfa_module_name}** na vašom zariadení a pozrite si kód dvojfaktorovej autentifikácie a overte svoju totožnosť."
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"invalid_auth": "Neplatné heslo rozhrania API",
|
||||
"invalid_code": "Neplatný autentifikačný kód"
|
||||
},
|
||||
"abort": {
|
||||
"no_api_password_set": "Nemáte nakonfigurované heslo rozhrania API.",
|
||||
"login_expired": "Relácia vypršala, prosím prihláste sa znova."
|
||||
}
|
||||
},
|
||||
"trusted_networks": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"user": "Používateľ"
|
||||
},
|
||||
"description": "Välj en användare att logga in som:"
|
||||
}
|
||||
},
|
||||
"abort": {
|
||||
"not_whitelisted": "Váš počítač nie je v zozname povolených zariadení."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"page-onboarding": {
|
||||
"intro": "Ste pripravení prebudiť váš domov, získať vaše súkromie a pripojiť sa k celosvetovej komunite bastličov?",
|
||||
"user": {
|
||||
"intro": "Poďme začať vytvorením používateľského konta.",
|
||||
"required_field": "Požadované",
|
||||
"data": {
|
||||
"name": "Meno",
|
||||
"username": "Používateľské meno",
|
||||
"password": "Heslo"
|
||||
},
|
||||
"create_account": "Vytvoriť účet",
|
||||
"error": {
|
||||
"required_fields": "Vyplňte všetky povinné polia"
|
||||
}
|
||||
}
|
||||
},
|
||||
"lovelace": {
|
||||
"cards": {
|
||||
"shopping-list": {
|
||||
"checked_items": "Začiarknuté položky",
|
||||
"clear_items": "Vymažte označené položky",
|
||||
"add_item": "Pridať položku"
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"edit_card": {
|
||||
"header": "Konfigurácia karty",
|
||||
"save": "Uložiť",
|
||||
"toggle_editor": "Prepnúť Editor",
|
||||
"pick_card": "Vyberte kartu, ktorú chcete pridať.",
|
||||
"add": "Pridať kartu",
|
||||
"edit": "Upraviť",
|
||||
"delete": "Vymazať"
|
||||
},
|
||||
"migrate": {
|
||||
"header": "Nekompatibilná konfigurácia",
|
||||
"para_no_id": "Tento prvok nemá ID. Pridajte ID k tomuto prvku v ui-lovelace.yaml.",
|
||||
"para_migrate": "Home Assistant dokáže automaticky pridať ID na všetky vaše karty a zobrazenia stlačením tlačidla Migrácia konfigurácie.",
|
||||
"migrate": "Migrácia konfigurácie"
|
||||
},
|
||||
"header": "Úprava používateľského rozhrania",
|
||||
"configure_ui": "Konfigurácia používateľského rozhrania",
|
||||
"edit_view": {
|
||||
"header": "Konfigurácia zobrazenia",
|
||||
"add": "Pridať zobrazenie",
|
||||
"edit": "Upraviť zobrazenie",
|
||||
"delete": "Odstrániť zobrazenie"
|
||||
},
|
||||
"save_config": {
|
||||
"header": "Prevziať kontrolu vášho Lovelace rozhrania",
|
||||
"para": "Štandardne Home Assistant bude udržiavať vaše užívateľské rozhranie a aktualizovať ho, keď budú k dispozícii nové entity alebo komponenty služby Lovelace. Ak prevezmete kontrolu, nebudeme už automaticky robiť zmeny.",
|
||||
"para_sure": "Naozaj chcete prevziať kontrolu vášho užívateľského rozhrania?",
|
||||
"cancel": "Nevadí",
|
||||
"save": "Prevziať kontrolu"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sidebar": {
|
||||
@ -568,7 +766,8 @@
|
||||
},
|
||||
"common": {
|
||||
"loading": "Načítava sa",
|
||||
"cancel": "Zrušiť"
|
||||
"cancel": "Zrušiť",
|
||||
"save": "Uložiť"
|
||||
},
|
||||
"duration": {
|
||||
"day": "{count} {count, plural,\none {deň}\nfew {dni}\nother {dní}\n}",
|
||||
@ -628,7 +827,9 @@
|
||||
"clear_code": "Zrušiť",
|
||||
"disarm": "Odkódovať",
|
||||
"arm_home": "Zakódovať doma",
|
||||
"arm_away": "Zakódovať odchod"
|
||||
"arm_away": "Zakódovať odchod",
|
||||
"arm_night": "Zakódovať na noc",
|
||||
"armed_custom_bypass": "Prispôsobené vylúčenie"
|
||||
},
|
||||
"automation": {
|
||||
"last_triggered": "Naposledy spustené",
|
||||
@ -678,6 +879,13 @@
|
||||
"turn_on": "Zapnúť",
|
||||
"turn_off": "Vypnúť"
|
||||
}
|
||||
},
|
||||
"water_heater": {
|
||||
"currently": "Aktuálne",
|
||||
"on_off": "Zapnúť \/ vypnúť",
|
||||
"target_temperature": "Cieľová teplota",
|
||||
"operation": "V prevádzke",
|
||||
"away_mode": "Režim neprítomnosti"
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
@ -724,6 +932,11 @@
|
||||
"ask": "Chcete tieto prihlasovacie údaje uložiť?",
|
||||
"decline": "Nie ďakujem",
|
||||
"confirm": "Uložiť prihlasovacie údaje"
|
||||
},
|
||||
"notification_drawer": {
|
||||
"click_to_configure": "Kliknutím na tlačidlo nakonfigurujete {entity}",
|
||||
"empty": "Žiadne upozornenia",
|
||||
"title": "Upozornenia"
|
||||
}
|
||||
},
|
||||
"domain": {
|
||||
|
||||
@ -423,6 +423,10 @@
|
||||
"event": "Dogodek:",
|
||||
"enter": "Vnesite",
|
||||
"leave": "Odidi"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhook",
|
||||
"webhook_id": "Webhook ID"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -423,6 +423,10 @@
|
||||
"event": "Händelse",
|
||||
"enter": "Ankommer",
|
||||
"leave": "Lämnar"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhook",
|
||||
"webhook_id": "Webhook ID"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -726,13 +730,31 @@
|
||||
"edit_card": {
|
||||
"header": "Kortkonfiguration",
|
||||
"save": "Spara",
|
||||
"toggle_editor": "Visa \/ Dölj redigerare"
|
||||
"toggle_editor": "Visa \/ Dölj redigerare",
|
||||
"pick_card": "Välj det kort du vill lägga till.",
|
||||
"add": "Lägg till kort",
|
||||
"edit": "Redigera",
|
||||
"delete": "Ta bort"
|
||||
},
|
||||
"migrate": {
|
||||
"header": "Konfigurationen är inte giltig",
|
||||
"para_no_id": "Det här elementet har inget ID. Lägg till ett ID till det här elementet i \"ui-lovelace.yaml\".",
|
||||
"para_migrate": "Home Assistant kan automatiskt lägga till IDn till alla dina kort och vyer genom att du klickar på \"Migrera konfiguration\".",
|
||||
"migrate": "Migrera konfigurationen"
|
||||
},
|
||||
"header": "Ändra användargränssnittet",
|
||||
"configure_ui": "Konfigurera användargränssnittet",
|
||||
"edit_view": {
|
||||
"header": "Visa konfiguration",
|
||||
"add": "Lägg till vy",
|
||||
"edit": "Redigera vy",
|
||||
"delete": "Radera vy"
|
||||
},
|
||||
"save_config": {
|
||||
"header": "Ta kontroll över din Lovelace UI",
|
||||
"para_sure": "Är du säker på att du vill ta kontroll över ditt användargränssnitt?",
|
||||
"cancel": "Glöm det",
|
||||
"save": "Ta kontroll"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -743,7 +765,8 @@
|
||||
},
|
||||
"common": {
|
||||
"loading": "Läser in",
|
||||
"cancel": "Avbryt"
|
||||
"cancel": "Avbryt",
|
||||
"save": "Spara"
|
||||
},
|
||||
"duration": {
|
||||
"day": "{count} {count, plural,\none {dag}\nother {dagar}\n}",
|
||||
|
||||
@ -50,7 +50,7 @@
|
||||
},
|
||||
"gas": {
|
||||
"off": "Чисто",
|
||||
"on": "Виявлено"
|
||||
"on": "Виявлено газ"
|
||||
},
|
||||
"motion": {
|
||||
"off": "Немає руху",
|
||||
@ -58,26 +58,26 @@
|
||||
},
|
||||
"occupancy": {
|
||||
"off": "Чисто",
|
||||
"on": "Виявлено"
|
||||
"on": "Виявлено присутність"
|
||||
},
|
||||
"smoke": {
|
||||
"off": "Чисто",
|
||||
"on": "Виявлено"
|
||||
"on": "Виявлено дим"
|
||||
},
|
||||
"sound": {
|
||||
"off": "Чисто",
|
||||
"on": "Виявлено"
|
||||
"on": "Виявлено звук"
|
||||
},
|
||||
"vibration": {
|
||||
"off": "Чисто",
|
||||
"on": "Виявлено"
|
||||
"off": "Не виявлено",
|
||||
"on": "Виявлена вібрація"
|
||||
},
|
||||
"opening": {
|
||||
"off": "Закрито",
|
||||
"on": "Відкритий"
|
||||
},
|
||||
"safety": {
|
||||
"off": "Безпека",
|
||||
"off": "Безпечно",
|
||||
"on": "Небезпечно"
|
||||
},
|
||||
"presence": {
|
||||
@ -106,14 +106,14 @@
|
||||
},
|
||||
"garage_door": {
|
||||
"off": "ЗачиненІ",
|
||||
"on": "Відкрито"
|
||||
"on": "Відкриті"
|
||||
},
|
||||
"heat": {
|
||||
"off": "Норма",
|
||||
"on": "Нагрівання"
|
||||
},
|
||||
"window": {
|
||||
"off": "Зачинено",
|
||||
"off": "Зачинене",
|
||||
"on": "Відчинене"
|
||||
},
|
||||
"lock": {
|
||||
@ -255,6 +255,9 @@
|
||||
"sunny": "Сонячно",
|
||||
"windy": "Вітряно",
|
||||
"windy-variant": "Вітряно"
|
||||
},
|
||||
"vacuum": {
|
||||
"idle": "Очікування"
|
||||
}
|
||||
},
|
||||
"state_badge": {
|
||||
@ -605,6 +608,8 @@
|
||||
"lovelace": {
|
||||
"cards": {
|
||||
"shopping-list": {
|
||||
"checked_items": "Позначені елементи",
|
||||
"clear_items": "Очистити позначені елементи",
|
||||
"add_item": "Додати елемент"
|
||||
}
|
||||
},
|
||||
@ -612,20 +617,32 @@
|
||||
"edit_card": {
|
||||
"header": "Конфігурація картки",
|
||||
"save": "Зберегти",
|
||||
"toggle_editor": "Перемкнути редактор",
|
||||
"pick_card": "Виберіть картку, яку хочете додати.",
|
||||
"add": "Додати картку",
|
||||
"edit": "Редагувати",
|
||||
"delete": "Видалити"
|
||||
},
|
||||
"migrate": {
|
||||
"header": "Конфігурація несумісна"
|
||||
"header": "Конфігурація несумісна",
|
||||
"para_no_id": "Цей елемент не має ID. Додайте ID до цього елемента в 'ui-lovelace.yaml'.",
|
||||
"para_migrate": "Домашній помічник може автоматично додавати ідентифікатори ID до всіх ваших карт і переглядів, натиснувши кнопку \"Перенести налаштування\".",
|
||||
"migrate": "Перенесення конфігурації"
|
||||
},
|
||||
"header": "Редагування інтерфейсу",
|
||||
"configure_ui": "Налаштувати інтерфейс користувача",
|
||||
"edit_view": {
|
||||
"header": "Перегляд Конфігурації "
|
||||
"header": "Перегляд Конфігурації ",
|
||||
"add": "Додати вигляд",
|
||||
"edit": "Редагувати вигляд",
|
||||
"delete": "Видалити вигляд"
|
||||
},
|
||||
"save_config": {
|
||||
"header": "Візьміть під свій контроль Lovelace UI"
|
||||
"header": "Візьміть під свій контроль Lovelace UI",
|
||||
"para": "За замовчуванням Home Assistant буде підтримувати ваш користувальницький інтерфейс, оновлюючи його, коли з'являться нові об'єкти або компоненти Lovelace. Якщо ви візьмете під контроль, ми більше не будемо автоматично вносити зміни для вас.",
|
||||
"para_sure": "Ви впевнені, що хочете взяти під свій контроль користувальницький інтерфейс?",
|
||||
"cancel": "Неважливо",
|
||||
"save": "Взяти під контроль"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -680,7 +697,8 @@
|
||||
"disarm": "Зняття з охорони",
|
||||
"arm_home": "Поставити на охорону",
|
||||
"arm_away": "Охорона (не вдома)",
|
||||
"arm_night": "Нічна охорона"
|
||||
"arm_night": "Нічна охорона",
|
||||
"armed_custom_bypass": "Користувацький обхід"
|
||||
},
|
||||
"automation": {
|
||||
"last_triggered": "Спрацьовано",
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
"disarmed": "Vô hiệu hóa",
|
||||
"armed_home": "Bảo vệ ở nhà",
|
||||
"armed_away": "Bảo vệ đi vắng",
|
||||
"armed_night": "An ninh ban đêm",
|
||||
"armed_night": "Ban đêm",
|
||||
"pending": "Đang chờ xử lý",
|
||||
"arming": "Kích hoạt",
|
||||
"disarming": "Giải giáp",
|
||||
@ -747,8 +747,8 @@
|
||||
"code": "Mã số",
|
||||
"clear_code": "Xóa",
|
||||
"disarm": "Vô hiệu hoá",
|
||||
"arm_home": "An ninh ở nhà",
|
||||
"arm_away": "An ninh đi vắng",
|
||||
"arm_home": "Ở nhà",
|
||||
"arm_away": "Đi vắng",
|
||||
"arm_night": "An ninh ban đêm",
|
||||
"armed_custom_bypass": "Bỏ qua tùy chỉnh"
|
||||
},
|
||||
|
||||
@ -423,6 +423,10 @@
|
||||
"event": "事件:",
|
||||
"enter": "進入區域",
|
||||
"leave": "離開區域"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhook",
|
||||
"webhook_id": "Webhook ID"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -750,8 +754,8 @@
|
||||
"header": "自行編輯 Lovelace UI",
|
||||
"para": "Home Assistant 於預設下,將維護您的使用者介面、於新物件或 Lovelace 元件可使用時進行更新。假如選擇自行編輯,系統將不再為您自動進行變更。",
|
||||
"para_sure": "確定要自行編輯使用者介面?",
|
||||
"cancel": "取消",
|
||||
"save": "儲存"
|
||||
"cancel": "我再想想",
|
||||
"save": "自行編輯"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user