mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-28 19:56:42 +00:00
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);
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user