mirror of
https://github.com/home-assistant/frontend.git
synced 2025-12-20 02:38:53 +00:00
Normalize all line endings
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
@@ -1,24 +1,24 @@
|
|||||||
export default function parseAspectRatio(input) {
|
export default function parseAspectRatio(input) {
|
||||||
// Handle 16x9, 16:9, 1.78x1, 1.78:1, 1.78
|
// Handle 16x9, 16:9, 1.78x1, 1.78:1, 1.78
|
||||||
// Ignore everything else
|
// Ignore everything else
|
||||||
function parseOrThrow(number) {
|
function parseOrThrow(number) {
|
||||||
const parsed = parseFloat(number);
|
const parsed = parseFloat(number);
|
||||||
if (isNaN(parsed)) throw new Error(`${number} is not a number`);
|
if (isNaN(parsed)) throw new Error(`${number} is not a number`);
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (input) {
|
if (input) {
|
||||||
const arr = input.replace(":", "x").split("x");
|
const arr = input.replace(":", "x").split("x");
|
||||||
if (arr.length === 0) {
|
if (arr.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return arr.length === 1
|
return arr.length === 1
|
||||||
? { w: parseOrThrow(arr[0]), h: 1 }
|
? { w: parseOrThrow(arr[0]), h: 1 }
|
||||||
: { w: parseOrThrow(arr[0]), h: parseOrThrow(arr[1]) };
|
: { w: parseOrThrow(arr[0]), h: parseOrThrow(arr[1]) };
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Ignore the error
|
// Ignore the error
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,59 +1,59 @@
|
|||||||
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
||||||
import "@polymer/paper-dialog/paper-dialog";
|
import "@polymer/paper-dialog/paper-dialog";
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import "../../resources/ha-style";
|
import "../../resources/ha-style";
|
||||||
|
|
||||||
import EventsMixin from "../../mixins/events-mixin";
|
import EventsMixin from "../../mixins/events-mixin";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin EventsMixin
|
* @appliesMixin EventsMixin
|
||||||
*/
|
*/
|
||||||
class HaLoadedComponents extends EventsMixin(PolymerElement) {
|
class HaLoadedComponents extends EventsMixin(PolymerElement) {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<style include="ha-style-dialog">
|
<style include="ha-style-dialog">
|
||||||
paper-dialog {
|
paper-dialog {
|
||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<paper-dialog id="dialog" with-backdrop="" opened="{{_opened}}">
|
<paper-dialog id="dialog" with-backdrop="" opened="{{_opened}}">
|
||||||
<h2>Loaded Components</h2>
|
<h2>Loaded Components</h2>
|
||||||
<paper-dialog-scrollable id="scrollable">
|
<paper-dialog-scrollable id="scrollable">
|
||||||
<p>The following components are currently loaded:</p>
|
<p>The following components are currently loaded:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<template is='dom-repeat' items='[[_components]]'>
|
<template is='dom-repeat' items='[[_components]]'>
|
||||||
<li>[[item]]</li>
|
<li>[[item]]</li>
|
||||||
</template>
|
</template>
|
||||||
</ul>
|
</ul>
|
||||||
</paper-dialog-scrollable>
|
</paper-dialog-scrollable>
|
||||||
</paper-dialog>
|
</paper-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
_hass: Object,
|
_hass: Object,
|
||||||
_components: Array,
|
_components: Array,
|
||||||
|
|
||||||
_opened: {
|
_opened: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
ready() {
|
ready() {
|
||||||
super.ready();
|
super.ready();
|
||||||
}
|
}
|
||||||
|
|
||||||
showDialog({ hass }) {
|
showDialog({ hass }) {
|
||||||
this.hass = hass;
|
this.hass = hass;
|
||||||
this._opened = true;
|
this._opened = true;
|
||||||
this._components = this.hass.config.components.sort();
|
this._components = this.hass.config.components.sort();
|
||||||
setTimeout(() => this.$.dialog.center(), 0);
|
setTimeout(() => this.$.dialog.center(), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("ha-loaded-components", HaLoadedComponents);
|
customElements.define("ha-loaded-components", HaLoadedComponents);
|
||||||
|
|||||||
@@ -1,258 +1,258 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
|
|
||||||
import EventsMixin from "../../../mixins/events-mixin";
|
import EventsMixin from "../../../mixins/events-mixin";
|
||||||
import LocalizeMixin from "../../../mixins/localize-mixin";
|
import LocalizeMixin from "../../../mixins/localize-mixin";
|
||||||
import "../../../components/ha-label-badge";
|
import "../../../components/ha-label-badge";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin EventsMixin
|
* @appliesMixin EventsMixin
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const Icons = {
|
const Icons = {
|
||||||
armed_away: "hass:security-lock",
|
armed_away: "hass:security-lock",
|
||||||
armed_custom_bypass: "hass:security",
|
armed_custom_bypass: "hass:security",
|
||||||
armed_home: "hass:security-home",
|
armed_home: "hass:security-home",
|
||||||
armed_night: "hass:security-home",
|
armed_night: "hass:security-home",
|
||||||
disarmed: "hass:verified",
|
disarmed: "hass:verified",
|
||||||
pending: "hass:shield-outline",
|
pending: "hass:shield-outline",
|
||||||
triggered: "hass:bell-ring",
|
triggered: "hass:bell-ring",
|
||||||
};
|
};
|
||||||
|
|
||||||
class HuiAlarmPanelCard extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
class HuiAlarmPanelCard extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
ha-card {
|
ha-card {
|
||||||
padding-bottom: 16px;
|
padding-bottom: 16px;
|
||||||
position: relative;
|
position: relative;
|
||||||
--alarm-color-disarmed: var(--label-badge-green);
|
--alarm-color-disarmed: var(--label-badge-green);
|
||||||
--alarm-color-pending: var(--label-badge-yellow);
|
--alarm-color-pending: var(--label-badge-yellow);
|
||||||
--alarm-color-triggered: var(--label-badge-red);
|
--alarm-color-triggered: var(--label-badge-red);
|
||||||
--alarm-color-armed: var(--label-badge-red);
|
--alarm-color-armed: var(--label-badge-red);
|
||||||
--alarm-color-autoarm: rgba(0, 153, 255, .1);
|
--alarm-color-autoarm: rgba(0, 153, 255, .1);
|
||||||
--alarm-state-color: var(--alarm-color-armed);
|
--alarm-state-color: var(--alarm-color-armed);
|
||||||
--base-unit: 15px;
|
--base-unit: 15px;
|
||||||
font-size: calc(var(--base-unit));
|
font-size: calc(var(--base-unit));
|
||||||
}
|
}
|
||||||
ha-label-badge {
|
ha-label-badge {
|
||||||
--ha-label-badge-color: var(--alarm-state-color);
|
--ha-label-badge-color: var(--alarm-state-color);
|
||||||
--label-badge-text-color: var(--alarm-state-color);
|
--label-badge-text-color: var(--alarm-state-color);
|
||||||
color: var(--alarm-state-color);
|
color: var(--alarm-state-color);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 12px;
|
right: 12px;
|
||||||
top: 12px;
|
top: 12px;
|
||||||
}
|
}
|
||||||
.disarmed {
|
.disarmed {
|
||||||
--alarm-state-color: var(--alarm-color-disarmed);
|
--alarm-state-color: var(--alarm-color-disarmed);
|
||||||
}
|
}
|
||||||
.triggered {
|
.triggered {
|
||||||
--alarm-state-color: var(--alarm-color-triggered);
|
--alarm-state-color: var(--alarm-color-triggered);
|
||||||
animation: pulse 1s infinite;
|
animation: pulse 1s infinite;
|
||||||
}
|
}
|
||||||
.arming {
|
.arming {
|
||||||
--alarm-state-color: var(--alarm-color-pending);
|
--alarm-state-color: var(--alarm-color-pending);
|
||||||
animation: pulse 1s infinite;
|
animation: pulse 1s infinite;
|
||||||
}
|
}
|
||||||
.pending {
|
.pending {
|
||||||
--alarm-state-color: var(--alarm-color-pending);
|
--alarm-state-color: var(--alarm-color-pending);
|
||||||
animation: pulse 1s infinite;
|
animation: pulse 1s infinite;
|
||||||
}
|
}
|
||||||
@keyframes pulse {
|
@keyframes pulse {
|
||||||
0% {
|
0% {
|
||||||
--ha-label-badge-color: var(--alarm-state-color);
|
--ha-label-badge-color: var(--alarm-state-color);
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
--ha-label-badge-color: rgba(255, 153, 0, 0.3);
|
--ha-label-badge-color: rgba(255, 153, 0, 0.3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
paper-input {
|
paper-input {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
max-width: 200px;
|
max-width: 200px;
|
||||||
font-size: calc(var(--base-unit));
|
font-size: calc(var(--base-unit));
|
||||||
}
|
}
|
||||||
.state {
|
.state {
|
||||||
margin-left: 16px;
|
margin-left: 16px;
|
||||||
font-size: calc(var(--base-unit) * 0.9);
|
font-size: calc(var(--base-unit) * 0.9);
|
||||||
position: relative;
|
position: relative;
|
||||||
bottom: 16px;
|
bottom: 16px;
|
||||||
color: var(--alarm-state-color);
|
color: var(--alarm-state-color);
|
||||||
animation: none;
|
animation: none;
|
||||||
}
|
}
|
||||||
#keypad {
|
#keypad {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
#keypad div {
|
#keypad div {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
#keypad paper-button {
|
#keypad paper-button {
|
||||||
margin-bottom: 10%;
|
margin-bottom: 10%;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: calc(var(--base-unit));
|
padding: calc(var(--base-unit));
|
||||||
font-size: calc(var(--base-unit) * 1.1);
|
font-size: calc(var(--base-unit) * 1.1);
|
||||||
}
|
}
|
||||||
.actions {
|
.actions {
|
||||||
margin: 0 8px;
|
margin: 0 8px;
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: calc(var(--base-unit) * 1);
|
font-size: calc(var(--base-unit) * 1);
|
||||||
}
|
}
|
||||||
.actions paper-button {
|
.actions paper-button {
|
||||||
min-width: calc(var(--base-unit) * 9);
|
min-width: calc(var(--base-unit) * 9);
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
paper-button#disarm {
|
paper-button#disarm {
|
||||||
color: var(--google-red-500);
|
color: var(--google-red-500);
|
||||||
}
|
}
|
||||||
.not-found {
|
.not-found {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background-color: yellow;
|
background-color: yellow;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<ha-card
|
<ha-card
|
||||||
header$="[[_computeHeader(localize, _stateObj)]]"
|
header$="[[_computeHeader(localize, _stateObj)]]"
|
||||||
class$="[[_computeClassName(_stateObj)]]"
|
class$="[[_computeClassName(_stateObj)]]"
|
||||||
>
|
>
|
||||||
<template is="dom-if" if="[[_stateObj]]">
|
<template is="dom-if" if="[[_stateObj]]">
|
||||||
<ha-label-badge
|
<ha-label-badge
|
||||||
class$="[[_stateObj.state]]"
|
class$="[[_stateObj.state]]"
|
||||||
icon="[[_computeIcon(_stateObj)]]"
|
icon="[[_computeIcon(_stateObj)]]"
|
||||||
label="[[_stateIconLabel(_stateObj.state)]]"
|
label="[[_stateIconLabel(_stateObj.state)]]"
|
||||||
></ha-label-badge>
|
></ha-label-badge>
|
||||||
<template is="dom-if" if="[[_showActionToggle(_stateObj.state)]]">
|
<template is="dom-if" if="[[_showActionToggle(_stateObj.state)]]">
|
||||||
<div id="armActions" class="actions">
|
<div id="armActions" class="actions">
|
||||||
<template is="dom-repeat" items="[[_config.states]]">
|
<template is="dom-repeat" items="[[_config.states]]">
|
||||||
<paper-button noink raised id="[[item]]" on-click="_handleActionClick">[[_label(localize, item)]]</paper-button>
|
<paper-button noink raised id="[[item]]" on-click="_handleActionClick">[[_label(localize, item)]]</paper-button>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template is="dom-if" if="[[!_showActionToggle(_stateObj.state)]]">
|
<template is="dom-if" if="[[!_showActionToggle(_stateObj.state)]]">
|
||||||
<div id="disarmActions" class="actions">
|
<div id="disarmActions" class="actions">
|
||||||
<paper-button noink raised id="disarm" on-click="_handleActionClick">[[_label(localize, "disarm")]]</paper-button>
|
<paper-button noink raised id="disarm" on-click="_handleActionClick">[[_label(localize, "disarm")]]</paper-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<paper-input label="Alarm Code" type="password" value="[[_value]]"></paper-input>
|
<paper-input label="Alarm Code" type="password" value="[[_value]]"></paper-input>
|
||||||
<div id="keypad">
|
<div id="keypad">
|
||||||
<div>
|
<div>
|
||||||
<paper-button noink raised value="1" on-click="_handlePadClick">1</paper-button>
|
<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="4" on-click="_handlePadClick">4</paper-button>
|
||||||
<paper-button noink raised value="7" on-click="_handlePadClick">7</paper-button>
|
<paper-button noink raised value="7" on-click="_handlePadClick">7</paper-button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<paper-button noink raised value="2" on-click="_handlePadClick">2</paper-button>
|
<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="5" on-click="_handlePadClick">5</paper-button>
|
||||||
<paper-button noink raised value="8" on-click="_handlePadClick">8</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>
|
<paper-button noink raised value="0" on-click="_handlePadClick">0</paper-button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<paper-button noink raised value="3" on-click="_handlePadClick">3</paper-button>
|
<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="6" on-click="_handlePadClick">6</paper-button>
|
||||||
<paper-button noink raised value="9" on-click="_handlePadClick">9</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>
|
<paper-button noink raised value="clear" on-click="_handlePadClick">[[_label(localize, "clear_code")]]</paper-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template is="dom-if" if="[[!_stateObj]]">
|
<template is="dom-if" if="[[!_stateObj]]">
|
||||||
<div>Entity not available: [[_config.entity]]</div>
|
<div>Entity not available: [[_config.entity]]</div>
|
||||||
</template>
|
</template>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
},
|
},
|
||||||
_config: Object,
|
_config: Object,
|
||||||
_stateObj: {
|
_stateObj: {
|
||||||
type: Object,
|
type: Object,
|
||||||
computed: "_computeStateObj(hass.states, _config.entity)",
|
computed: "_computeStateObj(hass.states, _config.entity)",
|
||||||
},
|
},
|
||||||
_value: {
|
_value: {
|
||||||
type: String,
|
type: String,
|
||||||
value: "",
|
value: "",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getCardSize() {
|
getCardSize() {
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
if (
|
if (
|
||||||
!config ||
|
!config ||
|
||||||
!config.entity ||
|
!config.entity ||
|
||||||
config.entity.split(".")[0] !== "alarm_control_panel"
|
config.entity.split(".")[0] !== "alarm_control_panel"
|
||||||
) {
|
) {
|
||||||
throw new Error("Invalid card configuration");
|
throw new Error("Invalid card configuration");
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaults = {
|
const defaults = {
|
||||||
states: ["arm_away", "arm_home"],
|
states: ["arm_away", "arm_home"],
|
||||||
};
|
};
|
||||||
|
|
||||||
this._config = { ...defaults, ...config };
|
this._config = { ...defaults, ...config };
|
||||||
this._icons = Icons;
|
this._icons = Icons;
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeStateObj(states, entityId) {
|
_computeStateObj(states, entityId) {
|
||||||
return states && entityId in states ? states[entityId] : null;
|
return states && entityId in states ? states[entityId] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeHeader(localize, stateObj) {
|
_computeHeader(localize, stateObj) {
|
||||||
if (!stateObj) return "";
|
if (!stateObj) return "";
|
||||||
return this._config.title
|
return this._config.title
|
||||||
? this._config.title
|
? this._config.title
|
||||||
: this._label(localize, stateObj.state);
|
: this._label(localize, stateObj.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeIcon(stateObj) {
|
_computeIcon(stateObj) {
|
||||||
return this._icons[stateObj.state] || "hass:shield-outline";
|
return this._icons[stateObj.state] || "hass:shield-outline";
|
||||||
}
|
}
|
||||||
|
|
||||||
_label(localize, state) {
|
_label(localize, state) {
|
||||||
return (
|
return (
|
||||||
localize(`state.alarm_control_panel.${state}`) ||
|
localize(`state.alarm_control_panel.${state}`) ||
|
||||||
localize(`ui.card.alarm_control_panel.${state}`)
|
localize(`ui.card.alarm_control_panel.${state}`)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_stateIconLabel(state) {
|
_stateIconLabel(state) {
|
||||||
const stateLabel = state.split("_").pop();
|
const stateLabel = state.split("_").pop();
|
||||||
return stateLabel === "disarmed" || stateLabel === "triggered"
|
return stateLabel === "disarmed" || stateLabel === "triggered"
|
||||||
? ""
|
? ""
|
||||||
: stateLabel;
|
: stateLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
_showActionToggle(state) {
|
_showActionToggle(state) {
|
||||||
return state === "disarmed";
|
return state === "disarmed";
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeClassName(stateObj) {
|
_computeClassName(stateObj) {
|
||||||
if (!stateObj) return "not-found";
|
if (!stateObj) return "not-found";
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
_handlePadClick(e) {
|
_handlePadClick(e) {
|
||||||
const val = e.target.getAttribute("value");
|
const val = e.target.getAttribute("value");
|
||||||
this._value = val === "clear" ? "" : this._value + val;
|
this._value = val === "clear" ? "" : this._value + val;
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleActionClick(e) {
|
_handleActionClick(e) {
|
||||||
this.hass.callService("alarm_control_panel", "alarm_" + e.target.id, {
|
this.hass.callService("alarm_control_panel", "alarm_" + e.target.id, {
|
||||||
entity_id: this._stateObj.entity_id,
|
entity_id: this._stateObj.entity_id,
|
||||||
code: this._value,
|
code: this._value,
|
||||||
});
|
});
|
||||||
this._value = "";
|
this._value = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-alarm-panel-card", HuiAlarmPanelCard);
|
customElements.define("hui-alarm-panel-card", HuiAlarmPanelCard);
|
||||||
|
|||||||
@@ -1,83 +1,83 @@
|
|||||||
import computeCardSize from "../common/compute-card-size";
|
import computeCardSize from "../common/compute-card-size";
|
||||||
import createCardElement from "../common/create-card-element";
|
import createCardElement from "../common/create-card-element";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { LovelaceCard, LovelaceConfig } from "../types";
|
import { LovelaceCard, LovelaceConfig } from "../types";
|
||||||
|
|
||||||
interface Condition {
|
interface Condition {
|
||||||
entity: string;
|
entity: string;
|
||||||
state?: string;
|
state?: string;
|
||||||
state_not?: string;
|
state_not?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Config extends LovelaceConfig {
|
interface Config extends LovelaceConfig {
|
||||||
card: LovelaceConfig;
|
card: LovelaceConfig;
|
||||||
conditions: Condition[];
|
conditions: Condition[];
|
||||||
}
|
}
|
||||||
|
|
||||||
class HuiConditionalCard extends HTMLElement implements LovelaceCard {
|
class HuiConditionalCard extends HTMLElement implements LovelaceCard {
|
||||||
private _hass?: HomeAssistant;
|
private _hass?: HomeAssistant;
|
||||||
private _config?: Config;
|
private _config?: Config;
|
||||||
private _card?: LovelaceCard;
|
private _card?: LovelaceCard;
|
||||||
|
|
||||||
public setConfig(config) {
|
public setConfig(config) {
|
||||||
if (
|
if (
|
||||||
!config.card ||
|
!config.card ||
|
||||||
!config.conditions ||
|
!config.conditions ||
|
||||||
!Array.isArray(config.conditions) ||
|
!Array.isArray(config.conditions) ||
|
||||||
!config.conditions.every((c) => c.entity && (c.state || c.state_not))
|
!config.conditions.every((c) => c.entity && (c.state || c.state_not))
|
||||||
) {
|
) {
|
||||||
throw new Error("Error in card configuration.");
|
throw new Error("Error in card configuration.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._card && this._card.parentElement) {
|
if (this._card && this._card.parentElement) {
|
||||||
this.removeChild(this._card);
|
this.removeChild(this._card);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = config;
|
this._config = config;
|
||||||
this._card = createCardElement(config.card);
|
this._card = createCardElement(config.card);
|
||||||
if (this._hass) {
|
if (this._hass) {
|
||||||
this.hass = this._hass;
|
this.hass = this._hass;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
set hass(hass: HomeAssistant) {
|
set hass(hass: HomeAssistant) {
|
||||||
this._hass = hass;
|
this._hass = hass;
|
||||||
|
|
||||||
if (!this._card) {
|
if (!this._card) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const visible =
|
const visible =
|
||||||
this._config &&
|
this._config &&
|
||||||
this._config.conditions.every((c) => {
|
this._config.conditions.every((c) => {
|
||||||
if (!(c.entity in hass.states)) {
|
if (!(c.entity in hass.states)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (c.state) {
|
if (c.state) {
|
||||||
return hass.states[c.entity].state === c.state;
|
return hass.states[c.entity].state === c.state;
|
||||||
}
|
}
|
||||||
return hass.states[c.entity].state !== c.state_not;
|
return hass.states[c.entity].state !== c.state_not;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (visible) {
|
if (visible) {
|
||||||
this._card.hass = hass;
|
this._card.hass = hass;
|
||||||
if (!this._card.parentElement) {
|
if (!this._card.parentElement) {
|
||||||
this.appendChild(this._card);
|
this.appendChild(this._card);
|
||||||
}
|
}
|
||||||
} else if (this._card.parentElement) {
|
} else if (this._card.parentElement) {
|
||||||
this.removeChild(this._card);
|
this.removeChild(this._card);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCardSize() {
|
public getCardSize() {
|
||||||
return computeCardSize(this._card);
|
return computeCardSize(this._card);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-conditional-card": HuiConditionalCard;
|
"hui-conditional-card": HuiConditionalCard;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-conditional-card", HuiConditionalCard);
|
customElements.define("hui-conditional-card", HuiConditionalCard);
|
||||||
|
|||||||
@@ -1,187 +1,187 @@
|
|||||||
import {
|
import {
|
||||||
html,
|
html,
|
||||||
LitElement,
|
LitElement,
|
||||||
PropertyDeclarations,
|
PropertyDeclarations,
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
} from "@polymer/lit-element";
|
} from "@polymer/lit-element";
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../components/hui-entities-toggle";
|
import "../components/hui-entities-toggle";
|
||||||
|
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { DOMAINS_HIDE_MORE_INFO } from "../../../common/const";
|
import { DOMAINS_HIDE_MORE_INFO } from "../../../common/const";
|
||||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { EntityConfig, EntityRow } from "../entity-rows/types";
|
import { EntityConfig, EntityRow } from "../entity-rows/types";
|
||||||
import { LovelaceCard, LovelaceConfig } from "../types";
|
import { LovelaceCard, LovelaceConfig } from "../types";
|
||||||
import processConfigEntities from "../common/process-config-entities";
|
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 computeDomain from "../../../common/entity/compute_domain";
|
||||||
import applyThemesOnElement from "../../../common/dom/apply_themes_on_element";
|
import applyThemesOnElement from "../../../common/dom/apply_themes_on_element";
|
||||||
|
|
||||||
interface ConfigEntity extends EntityConfig {
|
interface ConfigEntity extends EntityConfig {
|
||||||
type?: string;
|
type?: string;
|
||||||
secondary_info: "entity-id" | "last-changed";
|
secondary_info: "entity-id" | "last-changed";
|
||||||
action_name?: string;
|
action_name?: string;
|
||||||
service?: string;
|
service?: string;
|
||||||
service_data?: object;
|
service_data?: object;
|
||||||
url?: string;
|
url?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Config extends LovelaceConfig {
|
interface Config extends LovelaceConfig {
|
||||||
show_header_toggle?: boolean;
|
show_header_toggle?: boolean;
|
||||||
title?: string;
|
title?: string;
|
||||||
entities: ConfigEntity[];
|
entities: ConfigEntity[];
|
||||||
theme?: string;
|
theme?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
class HuiEntitiesCard extends hassLocalizeLitMixin(LitElement)
|
class HuiEntitiesCard extends hassLocalizeLitMixin(LitElement)
|
||||||
implements LovelaceCard {
|
implements LovelaceCard {
|
||||||
protected _hass?: HomeAssistant;
|
protected _hass?: HomeAssistant;
|
||||||
protected _config?: Config;
|
protected _config?: Config;
|
||||||
protected _configEntities?: ConfigEntity[];
|
protected _configEntities?: ConfigEntity[];
|
||||||
|
|
||||||
set hass(hass: HomeAssistant) {
|
set hass(hass: HomeAssistant) {
|
||||||
this._hass = hass;
|
this._hass = hass;
|
||||||
this.shadowRoot!.querySelectorAll("#states > div > *").forEach(
|
this.shadowRoot!.querySelectorAll("#states > div > *").forEach(
|
||||||
(element: unknown) => {
|
(element: unknown) => {
|
||||||
(element as EntityRow).hass = hass;
|
(element as EntityRow).hass = hass;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const entitiesToggle = this.shadowRoot!.querySelector(
|
const entitiesToggle = this.shadowRoot!.querySelector(
|
||||||
"hui-entities-toggle"
|
"hui-entities-toggle"
|
||||||
);
|
);
|
||||||
if (entitiesToggle) {
|
if (entitiesToggle) {
|
||||||
(entitiesToggle as any).hass = hass;
|
(entitiesToggle as any).hass = hass;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties(): PropertyDeclarations {
|
static get properties(): PropertyDeclarations {
|
||||||
return {
|
return {
|
||||||
_config: {},
|
_config: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCardSize(): number {
|
public getCardSize(): number {
|
||||||
if (!this._config) {
|
if (!this._config) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// +1 for the header
|
// +1 for the header
|
||||||
return (this._config.title ? 1 : 0) + this._config.entities.length;
|
return (this._config.title ? 1 : 0) + this._config.entities.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setConfig(config: Config): void {
|
public setConfig(config: Config): void {
|
||||||
const entities = processConfigEntities(config.entities);
|
const entities = processConfigEntities(config.entities);
|
||||||
|
|
||||||
this._config = { theme: "default", ...config };
|
this._config = { theme: "default", ...config };
|
||||||
this._configEntities = entities;
|
this._configEntities = entities;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updated(_changedProperties: PropertyValues): void {
|
protected updated(_changedProperties: PropertyValues): void {
|
||||||
if (this._hass && this._config) {
|
if (this._hass && this._config) {
|
||||||
applyThemesOnElement(this, this._hass.themes, this._config.theme);
|
applyThemesOnElement(this, this._hass.themes, this._config.theme);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this._config || !this._hass) {
|
if (!this._config || !this._hass) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
const { show_header_toggle, title } = this._config;
|
const { show_header_toggle, title } = this._config;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${this.renderStyle()}
|
${this.renderStyle()}
|
||||||
<ha-card>
|
<ha-card>
|
||||||
${
|
${
|
||||||
!title && !show_header_toggle
|
!title && !show_header_toggle
|
||||||
? html``
|
? html``
|
||||||
: html`
|
: html`
|
||||||
<div class='header'>
|
<div class='header'>
|
||||||
<div class="name">${title}</div>
|
<div class="name">${title}</div>
|
||||||
${
|
${
|
||||||
show_header_toggle === false
|
show_header_toggle === false
|
||||||
? html``
|
? html``
|
||||||
: html`
|
: html`
|
||||||
<hui-entities-toggle
|
<hui-entities-toggle
|
||||||
.hass="${this._hass}"
|
.hass="${this._hass}"
|
||||||
.entities="${this._configEntities!.map(
|
.entities="${this._configEntities!.map(
|
||||||
(conf) => conf.entity
|
(conf) => conf.entity
|
||||||
)}"
|
)}"
|
||||||
></hui-entities-toggle>`
|
></hui-entities-toggle>`
|
||||||
}
|
}
|
||||||
</div>`
|
</div>`
|
||||||
}
|
}
|
||||||
<div id="states">
|
<div id="states">
|
||||||
${this._configEntities!.map((entityConf) =>
|
${this._configEntities!.map((entityConf) =>
|
||||||
this.renderEntity(entityConf)
|
this.renderEntity(entityConf)
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderStyle(): TemplateResult {
|
private renderStyle(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
ha-card {
|
ha-card {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
#states {
|
#states {
|
||||||
margin: -4px 0;
|
margin: -4px 0;
|
||||||
}
|
}
|
||||||
#states > * {
|
#states > * {
|
||||||
margin: 8px 0;
|
margin: 8px 0;
|
||||||
}
|
}
|
||||||
#states > div > * {
|
#states > div > * {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.header {
|
.header {
|
||||||
@apply --paper-font-headline;
|
@apply --paper-font-headline;
|
||||||
/* overwriting line-height +8 because entity-toggle can be 40px height,
|
/* overwriting line-height +8 because entity-toggle can be 40px height,
|
||||||
compensating this with reduced padding */
|
compensating this with reduced padding */
|
||||||
line-height: 40px;
|
line-height: 40px;
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
padding: 4px 0 12px;
|
padding: 4px 0 12px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
.header .name {
|
.header .name {
|
||||||
@apply --paper-font-common-nowrap;
|
@apply --paper-font-common-nowrap;
|
||||||
}
|
}
|
||||||
.state-card-dialog {
|
.state-card-dialog {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderEntity(entityConf: ConfigEntity): TemplateResult {
|
private renderEntity(entityConf: ConfigEntity): TemplateResult {
|
||||||
const element = createRowElement(entityConf);
|
const element = createRowElement(entityConf);
|
||||||
if (this._hass) {
|
if (this._hass) {
|
||||||
element.hass = this._hass;
|
element.hass = this._hass;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
entityConf.entity &&
|
entityConf.entity &&
|
||||||
!DOMAINS_HIDE_MORE_INFO.includes(computeDomain(entityConf.entity))
|
!DOMAINS_HIDE_MORE_INFO.includes(computeDomain(entityConf.entity))
|
||||||
) {
|
) {
|
||||||
element.classList.add("state-card-dialog");
|
element.classList.add("state-card-dialog");
|
||||||
element.addEventListener("click", () => this._handleClick(entityConf));
|
element.addEventListener("click", () => this._handleClick(entityConf));
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`<div>${element}</div>`;
|
return html`<div>${element}</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleClick(entityConf: ConfigEntity): void {
|
private _handleClick(entityConf: ConfigEntity): void {
|
||||||
const entityId = entityConf.entity;
|
const entityId = entityConf.entity;
|
||||||
fireEvent(this, "hass-more-info", { entityId });
|
fireEvent(this, "hass-more-info", { entityId });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-entities-card": HuiEntitiesCard;
|
"hui-entities-card": HuiEntitiesCard;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-entities-card", HuiEntitiesCard);
|
customElements.define("hui-entities-card", HuiEntitiesCard);
|
||||||
|
|||||||
@@ -1,77 +1,77 @@
|
|||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
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";
|
import processConfigEntities from "../common/process-config-entities";
|
||||||
|
|
||||||
function getEntities(hass, filterState, entities) {
|
function getEntities(hass, filterState, entities) {
|
||||||
return entities.filter((entityConf) => {
|
return entities.filter((entityConf) => {
|
||||||
const stateObj = hass.states[entityConf.entity];
|
const stateObj = hass.states[entityConf.entity];
|
||||||
return stateObj && filterState.includes(stateObj.state);
|
return stateObj && filterState.includes(stateObj.state);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class HuiEntitiesCard extends PolymerElement {
|
class HuiEntitiesCard extends PolymerElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
observer: "_hassChanged",
|
observer: "_hassChanged",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getCardSize() {
|
getCardSize() {
|
||||||
return this.lastChild ? this.lastChild.getCardSize() : 1;
|
return this.lastChild ? this.lastChild.getCardSize() : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
if (!config.state_filter || !Array.isArray(config.state_filter)) {
|
if (!config.state_filter || !Array.isArray(config.state_filter)) {
|
||||||
throw new Error("Incorrect filter config.");
|
throw new Error("Incorrect filter config.");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = config;
|
this._config = config;
|
||||||
this._configEntities = processConfigEntities(config.entities);
|
this._configEntities = processConfigEntities(config.entities);
|
||||||
|
|
||||||
if (this.lastChild) {
|
if (this.lastChild) {
|
||||||
this.removeChild(this.lastChild);
|
this.removeChild(this.lastChild);
|
||||||
this._element = null;
|
this._element = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const card = "card" in config ? { ...config.card } : {};
|
const card = "card" in config ? { ...config.card } : {};
|
||||||
if (!card.type) card.type = "entities";
|
if (!card.type) card.type = "entities";
|
||||||
card.entities = [];
|
card.entities = [];
|
||||||
|
|
||||||
const element = createCardElement(card);
|
const element = createCardElement(card);
|
||||||
element._filterRawConfig = card;
|
element._filterRawConfig = card;
|
||||||
this._updateCardConfig(element);
|
this._updateCardConfig(element);
|
||||||
|
|
||||||
this._element = element;
|
this._element = element;
|
||||||
}
|
}
|
||||||
|
|
||||||
_hassChanged() {
|
_hassChanged() {
|
||||||
this._updateCardConfig(this._element);
|
this._updateCardConfig(this._element);
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateCardConfig(element) {
|
_updateCardConfig(element) {
|
||||||
if (!element || element.tagName === "HUI-ERROR-CARD" || !this.hass) return;
|
if (!element || element.tagName === "HUI-ERROR-CARD" || !this.hass) return;
|
||||||
const entitiesList = getEntities(
|
const entitiesList = getEntities(
|
||||||
this.hass,
|
this.hass,
|
||||||
this._config.state_filter,
|
this._config.state_filter,
|
||||||
this._configEntities
|
this._configEntities
|
||||||
);
|
);
|
||||||
|
|
||||||
if (entitiesList.length === 0 && this._config.show_empty === false) {
|
if (entitiesList.length === 0 && this._config.show_empty === false) {
|
||||||
this.style.display = "none";
|
this.style.display = "none";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.style.display = "block";
|
this.style.display = "block";
|
||||||
element.setConfig({ ...element._filterRawConfig, entities: entitiesList });
|
element.setConfig({ ...element._filterRawConfig, entities: entitiesList });
|
||||||
element.isPanel = this.isPanel;
|
element.isPanel = this.isPanel;
|
||||||
element.hass = this.hass;
|
element.hass = this.hass;
|
||||||
|
|
||||||
// Attach element if it has never been attached.
|
// Attach element if it has never been attached.
|
||||||
if (!this.lastChild) this.appendChild(element);
|
if (!this.lastChild) this.appendChild(element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define("hui-entity-filter-card", HuiEntitiesCard);
|
customElements.define("hui-entity-filter-card", HuiEntitiesCard);
|
||||||
|
|||||||
@@ -1,273 +1,273 @@
|
|||||||
import {
|
import {
|
||||||
html,
|
html,
|
||||||
LitElement,
|
LitElement,
|
||||||
PropertyDeclarations,
|
PropertyDeclarations,
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
} from "@polymer/lit-element";
|
} from "@polymer/lit-element";
|
||||||
import { LovelaceCard, LovelaceConfig } from "../types";
|
import { LovelaceCard, LovelaceConfig } from "../types";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
import isValidEntityId from "../../../common/entity/valid_entity_id";
|
import isValidEntityId from "../../../common/entity/valid_entity_id";
|
||||||
|
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
|
|
||||||
interface Config extends LovelaceConfig {
|
interface Config extends LovelaceConfig {
|
||||||
entity: string;
|
entity: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
unit_of_measurement?: string;
|
unit_of_measurement?: string;
|
||||||
min?: number;
|
min?: number;
|
||||||
max?: number;
|
max?: number;
|
||||||
severity?: object;
|
severity?: object;
|
||||||
}
|
}
|
||||||
|
|
||||||
const severityMap = {
|
const severityMap = {
|
||||||
red: "var(--label-badge-red)",
|
red: "var(--label-badge-red)",
|
||||||
green: "var(--label-badge-green)",
|
green: "var(--label-badge-green)",
|
||||||
yellow: "var(--label-badge-yellow)",
|
yellow: "var(--label-badge-yellow)",
|
||||||
normal: "var(--label-badge-blue)",
|
normal: "var(--label-badge-blue)",
|
||||||
};
|
};
|
||||||
|
|
||||||
class HuiGaugeCard extends LitElement implements LovelaceCard {
|
class HuiGaugeCard extends LitElement implements LovelaceCard {
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
private _config?: Config;
|
private _config?: Config;
|
||||||
|
|
||||||
static get properties(): PropertyDeclarations {
|
static get properties(): PropertyDeclarations {
|
||||||
return {
|
return {
|
||||||
hass: {},
|
hass: {},
|
||||||
_config: {},
|
_config: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCardSize(): number {
|
public getCardSize(): number {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setConfig(config: Config): void {
|
public setConfig(config: Config): void {
|
||||||
if (!config || !config.entity) {
|
if (!config || !config.entity) {
|
||||||
throw new Error("Invalid card configuration");
|
throw new Error("Invalid card configuration");
|
||||||
}
|
}
|
||||||
if (!isValidEntityId(config.entity)) {
|
if (!isValidEntityId(config.entity)) {
|
||||||
throw new Error("Invalid Entity");
|
throw new Error("Invalid Entity");
|
||||||
}
|
}
|
||||||
this._config = { min: 0, max: 100, ...config };
|
this._config = { min: 0, max: 100, ...config };
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this._config || !this.hass) {
|
if (!this._config || !this.hass) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
const stateObj = this.hass.states[this._config.entity];
|
const stateObj = this.hass.states[this._config.entity];
|
||||||
let error;
|
let error;
|
||||||
if (!stateObj) {
|
if (!stateObj) {
|
||||||
error = "Entity not available: " + this._config.entity;
|
error = "Entity not available: " + this._config.entity;
|
||||||
} else if (isNaN(Number(stateObj.state))) {
|
} else if (isNaN(Number(stateObj.state))) {
|
||||||
error = "Entity is non-numeric: " + this._config.entity;
|
error = "Entity is non-numeric: " + this._config.entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${this.renderStyle()}
|
${this.renderStyle()}
|
||||||
<ha-card @click="${this._handleClick}">
|
<ha-card @click="${this._handleClick}">
|
||||||
${
|
${
|
||||||
error
|
error
|
||||||
? html`<div class="not-found">${error}</div>`
|
? html`<div class="not-found">${error}</div>`
|
||||||
: html`
|
: html`
|
||||||
<div class='container'>
|
<div class='container'>
|
||||||
<div class='gauge-a'></div>
|
<div class='gauge-a'></div>
|
||||||
<div class='gauge-b'></div>
|
<div class='gauge-b'></div>
|
||||||
<div class='gauge-c' id='gauge'></div>
|
<div class='gauge-c' id='gauge'></div>
|
||||||
<div class='gauge-data'>
|
<div class='gauge-data'>
|
||||||
<div id='percent'>${stateObj.state}
|
<div id='percent'>${stateObj.state}
|
||||||
${this._config.unit_of_measurement ||
|
${this._config.unit_of_measurement ||
|
||||||
stateObj.attributes.unit_of_measurement ||
|
stateObj.attributes.unit_of_measurement ||
|
||||||
""}
|
""}
|
||||||
</div>
|
</div>
|
||||||
<div id='title'>${this._config.title}
|
<div id='title'>${this._config.title}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||||
if (changedProps.get("hass")) {
|
if (changedProps.get("hass")) {
|
||||||
return (
|
return (
|
||||||
(changedProps.get("hass") as any).states[this._config!.entity] !==
|
(changedProps.get("hass") as any).states[this._config!.entity] !==
|
||||||
this.hass!.states[this._config!.entity]
|
this.hass!.states[this._config!.entity]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (changedProps.get("_config")) {
|
if (changedProps.get("_config")) {
|
||||||
return changedProps.get("_config") !== this._config;
|
return changedProps.get("_config") !== this._config;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updated(): void {
|
protected updated(): void {
|
||||||
if (
|
if (
|
||||||
!this._config ||
|
!this._config ||
|
||||||
!this.hass ||
|
!this.hass ||
|
||||||
!this.shadowRoot!.getElementById("gauge")
|
!this.shadowRoot!.getElementById("gauge")
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const stateObj = this.hass.states[this._config.entity];
|
const stateObj = this.hass.states[this._config.entity];
|
||||||
if (isNaN(Number(stateObj.state))) {
|
if (isNaN(Number(stateObj.state))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const turn = this._translateTurn(Number(stateObj.state), this._config);
|
const turn = this._translateTurn(Number(stateObj.state), this._config);
|
||||||
|
|
||||||
this.shadowRoot!.getElementById(
|
this.shadowRoot!.getElementById(
|
||||||
"gauge"
|
"gauge"
|
||||||
)!.style.cssText = `transform: rotate(${turn}turn); background-color: ${this._computeSeverity(
|
)!.style.cssText = `transform: rotate(${turn}turn); background-color: ${this._computeSeverity(
|
||||||
stateObj.state,
|
stateObj.state,
|
||||||
this._config.severity!
|
this._config.severity!
|
||||||
)}`;
|
)}`;
|
||||||
|
|
||||||
(this.shadowRoot!.querySelector(
|
(this.shadowRoot!.querySelector(
|
||||||
"ha-card"
|
"ha-card"
|
||||||
)! as HTMLElement).style.setProperty(
|
)! as HTMLElement).style.setProperty(
|
||||||
"--base-unit",
|
"--base-unit",
|
||||||
this._computeBaseUnit()
|
this._computeBaseUnit()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderStyle(): TemplateResult {
|
private renderStyle(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
ha-card {
|
ha-card {
|
||||||
--base-unit: 50px;
|
--base-unit: 50px;
|
||||||
height: calc(var(--base-unit)*3);
|
height: calc(var(--base-unit)*3);
|
||||||
position: relative;
|
position: relative;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.container{
|
.container{
|
||||||
width: calc(var(--base-unit) * 4);
|
width: calc(var(--base-unit) * 4);
|
||||||
height: calc(var(--base-unit) * 2);
|
height: calc(var(--base-unit) * 2);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: calc(var(--base-unit)*1.5);
|
top: calc(var(--base-unit)*1.5);
|
||||||
left: 50%;
|
left: 50%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
}
|
}
|
||||||
.gauge-a{
|
.gauge-a{
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background-color: var(--primary-background-color);
|
background-color: var(--primary-background-color);
|
||||||
width: calc(var(--base-unit) * 4);
|
width: calc(var(--base-unit) * 4);
|
||||||
height: calc(var(--base-unit) * 2);
|
height: calc(var(--base-unit) * 2);
|
||||||
top: 0%;
|
top: 0%;
|
||||||
border-radius:calc(var(--base-unit) * 2.5) calc(var(--base-unit) * 2.5) 0px 0px ;
|
border-radius:calc(var(--base-unit) * 2.5) calc(var(--base-unit) * 2.5) 0px 0px ;
|
||||||
}
|
}
|
||||||
.gauge-b{
|
.gauge-b{
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background-color: var(--paper-card-background-color);
|
background-color: var(--paper-card-background-color);
|
||||||
width: calc(var(--base-unit) * 2.5);
|
width: calc(var(--base-unit) * 2.5);
|
||||||
height: calc(var(--base-unit) * 1.25);
|
height: calc(var(--base-unit) * 1.25);
|
||||||
top: calc(var(--base-unit) * 0.75);
|
top: calc(var(--base-unit) * 0.75);
|
||||||
margin-left: calc(var(--base-unit) * 0.75);
|
margin-left: calc(var(--base-unit) * 0.75);
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
border-radius: calc(var(--base-unit) * 2.5) calc(var(--base-unit) * 2.5) 0px 0px ;
|
border-radius: calc(var(--base-unit) * 2.5) calc(var(--base-unit) * 2.5) 0px 0px ;
|
||||||
}
|
}
|
||||||
.gauge-c{
|
.gauge-c{
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background-color: var(--label-badge-blue);
|
background-color: var(--label-badge-blue);
|
||||||
width: calc(var(--base-unit) * 4);
|
width: calc(var(--base-unit) * 4);
|
||||||
height: calc(var(--base-unit) * 2);
|
height: calc(var(--base-unit) * 2);
|
||||||
top: calc(var(--base-unit) * 2);
|
top: calc(var(--base-unit) * 2);
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
border-radius: 0px 0px calc(var(--base-unit) * 2) calc(var(--base-unit) * 2) ;
|
border-radius: 0px 0px calc(var(--base-unit) * 2) calc(var(--base-unit) * 2) ;
|
||||||
transform-origin: center top;
|
transform-origin: center top;
|
||||||
transition: all 1.3s ease-in-out;
|
transition: all 1.3s ease-in-out;
|
||||||
}
|
}
|
||||||
.gauge-data{
|
.gauge-data{
|
||||||
z-index: 4;
|
z-index: 4;
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
line-height: calc(var(--base-unit) * 0.3);
|
line-height: calc(var(--base-unit) * 0.3);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: calc(var(--base-unit) * 4);
|
width: calc(var(--base-unit) * 4);
|
||||||
height: calc(var(--base-unit) * 2.1);
|
height: calc(var(--base-unit) * 2.1);
|
||||||
top: calc(var(--base-unit) * 1.2);
|
top: calc(var(--base-unit) * 1.2);
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
transition: all 1s ease-out;
|
transition: all 1s ease-out;
|
||||||
}
|
}
|
||||||
.gauge-data #percent{
|
.gauge-data #percent{
|
||||||
font-size: calc(var(--base-unit) * 0.55);
|
font-size: calc(var(--base-unit) * 0.55);
|
||||||
}
|
}
|
||||||
.gauge-data #title{
|
.gauge-data #title{
|
||||||
padding-top: calc(var(--base-unit) * 0.15);
|
padding-top: calc(var(--base-unit) * 0.15);
|
||||||
font-size: calc(var(--base-unit) * 0.30);
|
font-size: calc(var(--base-unit) * 0.30);
|
||||||
}
|
}
|
||||||
.not-found {
|
.not-found {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background-color: yellow;
|
background-color: yellow;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _computeSeverity(stateValue: string, sections: object): string {
|
private _computeSeverity(stateValue: string, sections: object): string {
|
||||||
const numberValue = Number(stateValue);
|
const numberValue = Number(stateValue);
|
||||||
|
|
||||||
if (!sections) {
|
if (!sections) {
|
||||||
return severityMap.normal;
|
return severityMap.normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sectionsArray = Object.keys(sections);
|
const sectionsArray = Object.keys(sections);
|
||||||
const sortable = sectionsArray.map((severity) => [
|
const sortable = sectionsArray.map((severity) => [
|
||||||
severity,
|
severity,
|
||||||
sections[severity],
|
sections[severity],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
for (const severity of sortable) {
|
for (const severity of sortable) {
|
||||||
if (severityMap[severity[0]] == null || isNaN(severity[1])) {
|
if (severityMap[severity[0]] == null || isNaN(severity[1])) {
|
||||||
return severityMap.normal;
|
return severityMap.normal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sortable.sort((a, b) => a[1] - b[1]);
|
sortable.sort((a, b) => a[1] - b[1]);
|
||||||
|
|
||||||
if (numberValue >= sortable[0][1] && numberValue < sortable[1][1]) {
|
if (numberValue >= sortable[0][1] && numberValue < sortable[1][1]) {
|
||||||
return severityMap[sortable[0][0]];
|
return severityMap[sortable[0][0]];
|
||||||
}
|
}
|
||||||
if (numberValue >= sortable[1][1] && numberValue < sortable[2][1]) {
|
if (numberValue >= sortable[1][1] && numberValue < sortable[2][1]) {
|
||||||
return severityMap[sortable[1][0]];
|
return severityMap[sortable[1][0]];
|
||||||
}
|
}
|
||||||
if (numberValue >= sortable[2][1]) {
|
if (numberValue >= sortable[2][1]) {
|
||||||
return severityMap[sortable[2][0]];
|
return severityMap[sortable[2][0]];
|
||||||
}
|
}
|
||||||
return severityMap.normal;
|
return severityMap.normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _translateTurn(value: number, config: Config): number {
|
private _translateTurn(value: number, config: Config): number {
|
||||||
const maxTurnValue = Math.min(Math.max(value, config.min!), config.max!);
|
const maxTurnValue = Math.min(Math.max(value, config.min!), config.max!);
|
||||||
return (
|
return (
|
||||||
(5 * (maxTurnValue - config.min!)) / (config.max! - config.min!) / 10
|
(5 * (maxTurnValue - config.min!)) / (config.max! - config.min!) / 10
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _computeBaseUnit(): string {
|
private _computeBaseUnit(): string {
|
||||||
return this.clientWidth < 200 ? this.clientWidth / 5 + "px" : "50px";
|
return this.clientWidth < 200 ? this.clientWidth / 5 + "px" : "50px";
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleClick(): void {
|
private _handleClick(): void {
|
||||||
fireEvent(this, "hass-more-info", { entityId: this._config!.entity });
|
fireEvent(this, "hass-more-info", { entityId: this._config!.entity });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-gauge-card": HuiGaugeCard;
|
"hui-gauge-card": HuiGaugeCard;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-gauge-card", HuiGaugeCard);
|
customElements.define("hui-gauge-card", HuiGaugeCard);
|
||||||
|
|||||||
@@ -1,86 +1,86 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/state-history-charts";
|
import "../../../components/state-history-charts";
|
||||||
import "../../../data/ha-state-history-data";
|
import "../../../data/ha-state-history-data";
|
||||||
|
|
||||||
import processConfigEntities from "../common/process-config-entities";
|
import processConfigEntities from "../common/process-config-entities";
|
||||||
|
|
||||||
class HuiHistoryGraphCard extends PolymerElement {
|
class HuiHistoryGraphCard extends PolymerElement {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
ha-card {
|
ha-card {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
ha-card[header] {
|
ha-card[header] {
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<ha-card header$='[[_config.title]]'>
|
<ha-card header$='[[_config.title]]'>
|
||||||
<ha-state-history-data
|
<ha-state-history-data
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
filter-type="recent-entity"
|
filter-type="recent-entity"
|
||||||
entity-id="[[_entities]]"
|
entity-id="[[_entities]]"
|
||||||
data="{{_stateHistory}}"
|
data="{{_stateHistory}}"
|
||||||
is-loading="{{_stateHistoryLoading}}"
|
is-loading="{{_stateHistoryLoading}}"
|
||||||
cache-config="[[_cacheConfig]]"
|
cache-config="[[_cacheConfig]]"
|
||||||
></ha-state-history-data>
|
></ha-state-history-data>
|
||||||
<state-history-charts
|
<state-history-charts
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
history-data="[[_stateHistory]]"
|
history-data="[[_stateHistory]]"
|
||||||
is-loading-data="[[_stateHistoryLoading]]"
|
is-loading-data="[[_stateHistoryLoading]]"
|
||||||
names="[[_names]]"
|
names="[[_names]]"
|
||||||
up-to-now
|
up-to-now
|
||||||
no-single
|
no-single
|
||||||
></state-history-charts>
|
></state-history-charts>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
_config: Object,
|
_config: Object,
|
||||||
_names: Object,
|
_names: Object,
|
||||||
_entities: Array,
|
_entities: Array,
|
||||||
|
|
||||||
_stateHistory: Object,
|
_stateHistory: Object,
|
||||||
_stateHistoryLoading: Boolean,
|
_stateHistoryLoading: Boolean,
|
||||||
_cacheConfig: Object,
|
_cacheConfig: Object,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getCardSize() {
|
getCardSize() {
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
const entities = processConfigEntities(config.entities);
|
const entities = processConfigEntities(config.entities);
|
||||||
|
|
||||||
this._config = config;
|
this._config = config;
|
||||||
|
|
||||||
const _entities = [];
|
const _entities = [];
|
||||||
const _names = {};
|
const _names = {};
|
||||||
for (const entity of entities) {
|
for (const entity of entities) {
|
||||||
_entities.push(entity.entity);
|
_entities.push(entity.entity);
|
||||||
if (entity.name) {
|
if (entity.name) {
|
||||||
_names[entity.entity] = entity.name;
|
_names[entity.entity] = entity.name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
_cacheConfig: {
|
_cacheConfig: {
|
||||||
cacheKey: _entities.sort().join(),
|
cacheKey: _entities.sort().join(),
|
||||||
hoursToShow: config.hours_to_show || 24,
|
hoursToShow: config.hours_to_show || 24,
|
||||||
refresh: config.refresh_interval || 0,
|
refresh: config.refresh_interval || 0,
|
||||||
},
|
},
|
||||||
_entities,
|
_entities,
|
||||||
_names,
|
_names,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-history-graph-card", HuiHistoryGraphCard);
|
customElements.define("hui-history-graph-card", HuiHistoryGraphCard);
|
||||||
|
|||||||
@@ -1,50 +1,50 @@
|
|||||||
import { html } from "@polymer/lit-element";
|
import { html } from "@polymer/lit-element";
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
import computeCardSize from "../common/compute-card-size";
|
import computeCardSize from "../common/compute-card-size";
|
||||||
|
|
||||||
import { HuiStackCard } from "./hui-stack-card";
|
import { HuiStackCard } from "./hui-stack-card";
|
||||||
|
|
||||||
class HuiHorizontalStackCard extends HuiStackCard {
|
class HuiHorizontalStackCard extends HuiStackCard {
|
||||||
public getCardSize(): number {
|
public getCardSize(): number {
|
||||||
let totalSize = 0;
|
let totalSize = 0;
|
||||||
|
|
||||||
if (this._cards) {
|
if (this._cards) {
|
||||||
for (const element of this._cards) {
|
for (const element of this._cards) {
|
||||||
const elementSize = computeCardSize(element);
|
const elementSize = computeCardSize(element);
|
||||||
totalSize = elementSize > totalSize ? elementSize : totalSize;
|
totalSize = elementSize > totalSize ? elementSize : totalSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return totalSize;
|
return totalSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected renderStyle(): TemplateResult {
|
protected renderStyle(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
#root {
|
#root {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
#root > * {
|
#root > * {
|
||||||
flex: 1 1 0;
|
flex: 1 1 0;
|
||||||
margin: 0 4px;
|
margin: 0 4px;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
#root > *:first-child {
|
#root > *:first-child {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
#root > *:last-child {
|
#root > *:last-child {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-horitzontal-stack-card": HuiHorizontalStackCard;
|
"hui-horitzontal-stack-card": HuiHorizontalStackCard;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-horizontal-stack-card", HuiHorizontalStackCard);
|
customElements.define("hui-horizontal-stack-card", HuiHorizontalStackCard);
|
||||||
|
|||||||
@@ -1,80 +1,80 @@
|
|||||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||||
|
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
|
|
||||||
import { LovelaceCard, LovelaceConfig } from "../types";
|
import { LovelaceCard, LovelaceConfig } from "../types";
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
interface Config extends LovelaceConfig {
|
interface Config extends LovelaceConfig {
|
||||||
aspect_ratio?: string;
|
aspect_ratio?: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class HuiIframeCard extends LitElement implements LovelaceCard {
|
export class HuiIframeCard extends LitElement implements LovelaceCard {
|
||||||
protected _config?: Config;
|
protected _config?: Config;
|
||||||
|
|
||||||
static get properties(): PropertyDeclarations {
|
static get properties(): PropertyDeclarations {
|
||||||
return {
|
return {
|
||||||
_config: {},
|
_config: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCardSize(): number {
|
public getCardSize(): number {
|
||||||
return 1 + this.offsetHeight / 50;
|
return 1 + this.offsetHeight / 50;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setConfig(config: Config): void {
|
public setConfig(config: Config): void {
|
||||||
if (!config.url) {
|
if (!config.url) {
|
||||||
throw new Error("URL required");
|
throw new Error("URL required");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this._config) {
|
if (!this._config) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${this.renderStyle()}
|
${this.renderStyle()}
|
||||||
<ha-card .header="${this._config.title}">
|
<ha-card .header="${this._config.title}">
|
||||||
<div id="root">
|
<div id="root">
|
||||||
<iframe src="${this._config.url}"></iframe>
|
<iframe src="${this._config.url}"></iframe>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderStyle(): TemplateResult {
|
private renderStyle(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
ha-card {
|
ha-card {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
#root {
|
#root {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-top: ${this._config!.aspect_ratio || "50%"};
|
padding-top: ${this._config!.aspect_ratio || "50%"};
|
||||||
}
|
}
|
||||||
iframe {
|
iframe {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border: none;
|
border: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-iframe-card": HuiIframeCard;
|
"hui-iframe-card": HuiIframeCard;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-iframe-card", HuiIframeCard);
|
customElements.define("hui-iframe-card", HuiIframeCard);
|
||||||
|
|||||||
@@ -1,57 +1,57 @@
|
|||||||
import createErrorCardConfig from "../common/create-error-card-config";
|
import createErrorCardConfig from "../common/create-error-card-config";
|
||||||
import computeDomain from "../../../common/entity/compute_domain";
|
import computeDomain from "../../../common/entity/compute_domain";
|
||||||
|
|
||||||
export default class LegacyWrapperCard extends HTMLElement {
|
export default class LegacyWrapperCard extends HTMLElement {
|
||||||
constructor(tag, domain) {
|
constructor(tag, domain) {
|
||||||
super();
|
super();
|
||||||
this._tag = tag.toUpperCase();
|
this._tag = tag.toUpperCase();
|
||||||
this._domain = domain;
|
this._domain = domain;
|
||||||
this._element = null;
|
this._element = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getCardSize() {
|
getCardSize() {
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
if (!config.entity) {
|
if (!config.entity) {
|
||||||
throw new Error("No entity specified");
|
throw new Error("No entity specified");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (computeDomain(config.entity) !== this._domain) {
|
if (computeDomain(config.entity) !== this._domain) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Specified entity needs to be of domain ${this._domain}.`
|
`Specified entity needs to be of domain ${this._domain}.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
set hass(hass) {
|
set hass(hass) {
|
||||||
const entityId = this._config.entity;
|
const entityId = this._config.entity;
|
||||||
|
|
||||||
if (entityId in hass.states) {
|
if (entityId in hass.states) {
|
||||||
this._ensureElement(this._tag);
|
this._ensureElement(this._tag);
|
||||||
this.lastChild.hass = hass;
|
this.lastChild.hass = hass;
|
||||||
this.lastChild.stateObj = hass.states[entityId];
|
this.lastChild.stateObj = hass.states[entityId];
|
||||||
} else {
|
} else {
|
||||||
this._ensureElement("HUI-ERROR-CARD");
|
this._ensureElement("HUI-ERROR-CARD");
|
||||||
this.lastChild.setConfig(
|
this.lastChild.setConfig(
|
||||||
createErrorCardConfig(
|
createErrorCardConfig(
|
||||||
`No state available for ${entityId}`,
|
`No state available for ${entityId}`,
|
||||||
this._config
|
this._config
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ensureElement(tag) {
|
_ensureElement(tag) {
|
||||||
if (this.lastChild && this.lastChild.tagName === tag) return;
|
if (this.lastChild && this.lastChild.tagName === tag) return;
|
||||||
|
|
||||||
if (this.lastChild) {
|
if (this.lastChild) {
|
||||||
this.removeChild(this.lastChild);
|
this.removeChild(this.lastChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.appendChild(document.createElement(tag));
|
this.appendChild(document.createElement(tag));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,327 +1,327 @@
|
|||||||
import {
|
import {
|
||||||
html,
|
html,
|
||||||
LitElement,
|
LitElement,
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
PropertyDeclarations,
|
PropertyDeclarations,
|
||||||
} from "@polymer/lit-element";
|
} from "@polymer/lit-element";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { styleMap } from "lit-html/directives/styleMap";
|
import { styleMap } from "lit-html/directives/styleMap";
|
||||||
import computeStateName from "../../../common/entity/compute_state_name";
|
import computeStateName from "../../../common/entity/compute_state_name";
|
||||||
import stateIcon from "../../../common/entity/state_icon";
|
import stateIcon from "../../../common/entity/state_icon";
|
||||||
import { jQuery } from "../../../resources/jquery";
|
import { jQuery } from "../../../resources/jquery";
|
||||||
|
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-icon";
|
import "../../../components/ha-icon";
|
||||||
import { roundSliderStyle } from "../../../resources/jquery.roundslider";
|
import { roundSliderStyle } from "../../../resources/jquery.roundslider";
|
||||||
|
|
||||||
import { HomeAssistant, LightEntity } from "../../../types";
|
import { HomeAssistant, LightEntity } from "../../../types";
|
||||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
||||||
import { LovelaceCard, LovelaceConfig } from "../types";
|
import { LovelaceCard, LovelaceConfig } from "../types";
|
||||||
import { longPress } from "../common/directives/long-press-directive";
|
import { longPress } from "../common/directives/long-press-directive";
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
const lightConfig = {
|
const lightConfig = {
|
||||||
radius: 80,
|
radius: 80,
|
||||||
step: 1,
|
step: 1,
|
||||||
circleShape: "pie",
|
circleShape: "pie",
|
||||||
startAngle: 315,
|
startAngle: 315,
|
||||||
width: 5,
|
width: 5,
|
||||||
min: 1,
|
min: 1,
|
||||||
max: 100,
|
max: 100,
|
||||||
sliderType: "min-range",
|
sliderType: "min-range",
|
||||||
lineCap: "round",
|
lineCap: "round",
|
||||||
handleSize: "+12",
|
handleSize: "+12",
|
||||||
showTooltip: false,
|
showTooltip: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
interface Config extends LovelaceConfig {
|
interface Config extends LovelaceConfig {
|
||||||
entity: string;
|
entity: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class HuiLightCard extends hassLocalizeLitMixin(LitElement)
|
export class HuiLightCard extends hassLocalizeLitMixin(LitElement)
|
||||||
implements LovelaceCard {
|
implements LovelaceCard {
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
private _config?: Config;
|
private _config?: Config;
|
||||||
private _brightnessTimout?: number;
|
private _brightnessTimout?: number;
|
||||||
|
|
||||||
static get properties(): PropertyDeclarations {
|
static get properties(): PropertyDeclarations {
|
||||||
return {
|
return {
|
||||||
hass: {},
|
hass: {},
|
||||||
_config: {},
|
_config: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCardSize(): number {
|
public getCardSize(): number {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setConfig(config: Config): void {
|
public setConfig(config: Config): void {
|
||||||
if (!config.entity || config.entity.split(".")[0] !== "light") {
|
if (!config.entity || config.entity.split(".")[0] !== "light") {
|
||||||
throw new Error("Specify an entity from within the light domain.");
|
throw new Error("Specify an entity from within the light domain.");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this.hass || !this._config) {
|
if (!this.hass || !this._config) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
const stateObj = this.hass.states[this._config!.entity] as LightEntity;
|
const stateObj = this.hass.states[this._config!.entity] as LightEntity;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${this.renderStyle()}
|
${this.renderStyle()}
|
||||||
<ha-card>
|
<ha-card>
|
||||||
${
|
${
|
||||||
!stateObj
|
!stateObj
|
||||||
? html`
|
? html`
|
||||||
<div class="not-found">Entity not available: ${
|
<div class="not-found">Entity not available: ${
|
||||||
this._config.entity
|
this._config.entity
|
||||||
}</div>`
|
}</div>`
|
||||||
: html`
|
: html`
|
||||||
<div id="light"></div>
|
<div id="light"></div>
|
||||||
<div id="tooltip">
|
<div id="tooltip">
|
||||||
<div class="icon-state">
|
<div class="icon-state">
|
||||||
<ha-icon
|
<ha-icon
|
||||||
data-state="${stateObj.state}"
|
data-state="${stateObj.state}"
|
||||||
.icon="${stateIcon(stateObj)}"
|
.icon="${stateIcon(stateObj)}"
|
||||||
style="${styleMap({
|
style="${styleMap({
|
||||||
filter: this._computeBrightness(stateObj),
|
filter: this._computeBrightness(stateObj),
|
||||||
color: this._computeColor(stateObj),
|
color: this._computeColor(stateObj),
|
||||||
})}"
|
})}"
|
||||||
@ha-click="${() => this._handleClick(false)}"
|
@ha-click="${() => this._handleClick(false)}"
|
||||||
@ha-hold="${() => this._handleClick(true)}"
|
@ha-hold="${() => this._handleClick(true)}"
|
||||||
.longPress="${longPress()}"
|
.longPress="${longPress()}"
|
||||||
></ha-icon>
|
></ha-icon>
|
||||||
<div
|
<div
|
||||||
class="brightness"
|
class="brightness"
|
||||||
@ha-click="${() => this._handleClick(false)}"
|
@ha-click="${() => this._handleClick(false)}"
|
||||||
@ha-hold="${() => this._handleClick(true)}"
|
@ha-hold="${() => this._handleClick(true)}"
|
||||||
.longPress="${longPress()}"
|
.longPress="${longPress()}"
|
||||||
></div>
|
></div>
|
||||||
<div class="name">${this._config.name ||
|
<div class="name">${this._config.name ||
|
||||||
computeStateName(stateObj)}</div>
|
computeStateName(stateObj)}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||||
if (changedProps.get("hass")) {
|
if (changedProps.get("hass")) {
|
||||||
return (
|
return (
|
||||||
(changedProps.get("hass") as any).states[this._config!.entity] !==
|
(changedProps.get("hass") as any).states[this._config!.entity] !==
|
||||||
this.hass!.states[this._config!.entity]
|
this.hass!.states[this._config!.entity]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (changedProps as unknown) as boolean;
|
return (changedProps as unknown) as boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected firstUpdated(): void {
|
protected firstUpdated(): void {
|
||||||
const brightness = this.hass!.states[this._config!.entity].attributes
|
const brightness = this.hass!.states[this._config!.entity].attributes
|
||||||
.brightness;
|
.brightness;
|
||||||
jQuery("#light", this.shadowRoot).roundSlider({
|
jQuery("#light", this.shadowRoot).roundSlider({
|
||||||
...lightConfig,
|
...lightConfig,
|
||||||
change: (value) => this._setBrightness(value),
|
change: (value) => this._setBrightness(value),
|
||||||
drag: (value) => this._dragEvent(value),
|
drag: (value) => this._dragEvent(value),
|
||||||
start: () => this._showBrightness(),
|
start: () => this._showBrightness(),
|
||||||
stop: () => this._hideBrightness(),
|
stop: () => this._hideBrightness(),
|
||||||
});
|
});
|
||||||
this.shadowRoot!.querySelector(".brightness")!.innerHTML =
|
this.shadowRoot!.querySelector(".brightness")!.innerHTML =
|
||||||
(Math.round((brightness / 254) * 100) || 0) + "%";
|
(Math.round((brightness / 254) * 100) || 0) + "%";
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updated(): void {
|
protected updated(): void {
|
||||||
const attrs = this.hass!.states[this._config!.entity].attributes;
|
const attrs = this.hass!.states[this._config!.entity].attributes;
|
||||||
|
|
||||||
jQuery("#light", this.shadowRoot).roundSlider({
|
jQuery("#light", this.shadowRoot).roundSlider({
|
||||||
value: Math.round((attrs.brightness / 254) * 100) || 0,
|
value: Math.round((attrs.brightness / 254) * 100) || 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderStyle(): TemplateResult {
|
private renderStyle(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
${roundSliderStyle}
|
${roundSliderStyle}
|
||||||
<style>
|
<style>
|
||||||
:host {
|
:host {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
ha-card {
|
ha-card {
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
--brightness-font-color: white;
|
--brightness-font-color: white;
|
||||||
--brightness-font-text-shadow:
|
--brightness-font-text-shadow:
|
||||||
-1px -1px 0 #000,
|
-1px -1px 0 #000,
|
||||||
1px -1px 0 #000,
|
1px -1px 0 #000,
|
||||||
-1px 1px 0 #000,
|
-1px 1px 0 #000,
|
||||||
1px 1px 0 #000;
|
1px 1px 0 #000;
|
||||||
--name-font-size: 1.2rem;
|
--name-font-size: 1.2rem;
|
||||||
--brightness-font-size: 1.2rem;
|
--brightness-font-size: 1.2rem;
|
||||||
--rail-border-color: transparent;
|
--rail-border-color: transparent;
|
||||||
}
|
}
|
||||||
#tooltip {
|
#tooltip {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
z-index: 15;
|
z-index: 15;
|
||||||
}
|
}
|
||||||
.icon-state {
|
.icon-state {
|
||||||
display: block;
|
display: block;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
transform: translate(0,25%);
|
transform: translate(0,25%);
|
||||||
}
|
}
|
||||||
#light {
|
#light {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding-top: 16px;
|
padding-top: 16px;
|
||||||
padding-bottom: 16px;
|
padding-bottom: 16px;
|
||||||
}
|
}
|
||||||
#light .rs-bar.rs-transition.rs-first, .rs-bar.rs-transition.rs-second{
|
#light .rs-bar.rs-transition.rs-first, .rs-bar.rs-transition.rs-second{
|
||||||
z-index: 20 !important;
|
z-index: 20 !important;
|
||||||
}
|
}
|
||||||
#light .rs-range-color {
|
#light .rs-range-color {
|
||||||
background-color: var(--primary-color);
|
background-color: var(--primary-color);
|
||||||
}
|
}
|
||||||
#light .rs-path-color {
|
#light .rs-path-color {
|
||||||
background-color: var(--disabled-text-color);
|
background-color: var(--disabled-text-color);
|
||||||
}
|
}
|
||||||
#light .rs-handle {
|
#light .rs-handle {
|
||||||
background-color: var(--paper-card-background-color, white);
|
background-color: var(--paper-card-background-color, white);
|
||||||
padding: 7px;
|
padding: 7px;
|
||||||
border: 2px solid var(--disabled-text-color);
|
border: 2px solid var(--disabled-text-color);
|
||||||
}
|
}
|
||||||
#light .rs-handle.rs-focus {
|
#light .rs-handle.rs-focus {
|
||||||
border-color:var(--primary-color);
|
border-color:var(--primary-color);
|
||||||
}
|
}
|
||||||
#light .rs-handle:after {
|
#light .rs-handle:after {
|
||||||
border-color: var(--primary-color);
|
border-color: var(--primary-color);
|
||||||
background-color: var(--primary-color);
|
background-color: var(--primary-color);
|
||||||
}
|
}
|
||||||
#light .rs-border {
|
#light .rs-border {
|
||||||
border-color: var(--rail-border-color);
|
border-color: var(--rail-border-color);
|
||||||
}
|
}
|
||||||
#light .rs-inner.rs-bg-color.rs-border,
|
#light .rs-inner.rs-bg-color.rs-border,
|
||||||
#light .rs-overlay.rs-transition.rs-bg-color {
|
#light .rs-overlay.rs-transition.rs-bg-color {
|
||||||
background-color: var(--paper-card-background-color, white);
|
background-color: var(--paper-card-background-color, white);
|
||||||
}
|
}
|
||||||
ha-icon {
|
ha-icon {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
width: 76px;
|
width: 76px;
|
||||||
height: 76px;
|
height: 76px;
|
||||||
color: var(--paper-item-icon-color, #44739e);
|
color: var(--paper-item-icon-color, #44739e);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
ha-icon[data-state=on] {
|
ha-icon[data-state=on] {
|
||||||
color: var(--paper-item-icon-active-color, #FDD835);
|
color: var(--paper-item-icon-active-color, #FDD835);
|
||||||
}
|
}
|
||||||
ha-icon[data-state=unavailable] {
|
ha-icon[data-state=unavailable] {
|
||||||
color: var(--state-icon-unavailable-color);
|
color: var(--state-icon-unavailable-color);
|
||||||
}
|
}
|
||||||
.name {
|
.name {
|
||||||
padding-top: 40px;
|
padding-top: 40px;
|
||||||
font-size: var(--name-font-size);
|
font-size: var(--name-font-size);
|
||||||
}
|
}
|
||||||
.brightness {
|
.brightness {
|
||||||
font-size: var(--brightness-font-size);
|
font-size: var(--brightness-font-size);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
top: 10%;
|
top: 10%;
|
||||||
transform: translate(-50%);
|
transform: translate(-50%);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity .5s ease-in-out;
|
transition: opacity .5s ease-in-out;
|
||||||
-moz-transition: opacity .5s ease-in-out;
|
-moz-transition: opacity .5s ease-in-out;
|
||||||
-webkit-transition: opacity .5s ease-in-out;
|
-webkit-transition: opacity .5s ease-in-out;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: var(--brightness-font-color);
|
color: var(--brightness-font-color);
|
||||||
text-shadow: var(--brightness-font-text-shadow)
|
text-shadow: var(--brightness-font-text-shadow)
|
||||||
}
|
}
|
||||||
.show_brightness {
|
.show_brightness {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
.not-found {
|
.not-found {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background-color: yellow;
|
background-color: yellow;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _dragEvent(e: any): void {
|
private _dragEvent(e: any): void {
|
||||||
this.shadowRoot!.querySelector(".brightness")!.innerHTML = e.value + "%";
|
this.shadowRoot!.querySelector(".brightness")!.innerHTML = e.value + "%";
|
||||||
}
|
}
|
||||||
|
|
||||||
private _showBrightness(): void {
|
private _showBrightness(): void {
|
||||||
clearTimeout(this._brightnessTimout);
|
clearTimeout(this._brightnessTimout);
|
||||||
this.shadowRoot!.querySelector(".brightness")!.classList.add(
|
this.shadowRoot!.querySelector(".brightness")!.classList.add(
|
||||||
"show_brightness"
|
"show_brightness"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _hideBrightness(): void {
|
private _hideBrightness(): void {
|
||||||
this._brightnessTimout = window.setTimeout(() => {
|
this._brightnessTimout = window.setTimeout(() => {
|
||||||
this.shadowRoot!.querySelector(".brightness")!.classList.remove(
|
this.shadowRoot!.querySelector(".brightness")!.classList.remove(
|
||||||
"show_brightness"
|
"show_brightness"
|
||||||
);
|
);
|
||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setBrightness(e: any): void {
|
private _setBrightness(e: any): void {
|
||||||
this.hass!.callService("light", "turn_on", {
|
this.hass!.callService("light", "turn_on", {
|
||||||
entity_id: this._config!.entity,
|
entity_id: this._config!.entity,
|
||||||
brightness_pct: e.value,
|
brightness_pct: e.value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _computeBrightness(stateObj: LightEntity): string {
|
private _computeBrightness(stateObj: LightEntity): string {
|
||||||
if (!stateObj.attributes.brightness) {
|
if (!stateObj.attributes.brightness) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
const brightness = stateObj.attributes.brightness;
|
const brightness = stateObj.attributes.brightness;
|
||||||
return `brightness(${(brightness + 245) / 5}%)`;
|
return `brightness(${(brightness + 245) / 5}%)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _computeColor(stateObj: LightEntity): string {
|
private _computeColor(stateObj: LightEntity): string {
|
||||||
if (!stateObj.attributes.hs_color) {
|
if (!stateObj.attributes.hs_color) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
const [hue, sat] = stateObj.attributes.hs_color;
|
const [hue, sat] = stateObj.attributes.hs_color;
|
||||||
if (sat <= 10) {
|
if (sat <= 10) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return `hsl(${hue}, 100%, ${100 - sat / 2}%)`;
|
return `hsl(${hue}, 100%, ${100 - sat / 2}%)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleClick(hold: boolean): void {
|
private _handleClick(hold: boolean): void {
|
||||||
const entityId = this._config!.entity;
|
const entityId = this._config!.entity;
|
||||||
|
|
||||||
if (hold) {
|
if (hold) {
|
||||||
fireEvent(this, "hass-more-info", {
|
fireEvent(this, "hass-more-info", {
|
||||||
entityId,
|
entityId,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.hass!.callService("light", "toggle", {
|
this.hass!.callService("light", "toggle", {
|
||||||
entity_id: entityId,
|
entity_id: entityId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-light-card": HuiLightCard;
|
"hui-light-card": HuiLightCard;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-light-card", HuiLightCard);
|
customElements.define("hui-light-card", HuiLightCard);
|
||||||
|
|||||||
@@ -1,305 +1,305 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
import "@polymer/paper-icon-button/paper-icon-button";
|
import "@polymer/paper-icon-button/paper-icon-button";
|
||||||
import Leaflet from "leaflet";
|
import Leaflet from "leaflet";
|
||||||
|
|
||||||
import "../../map/ha-entity-marker";
|
import "../../map/ha-entity-marker";
|
||||||
|
|
||||||
import setupLeafletMap from "../../../common/dom/setup-leaflet-map";
|
import setupLeafletMap from "../../../common/dom/setup-leaflet-map";
|
||||||
import processConfigEntities from "../common/process-config-entities";
|
import processConfigEntities from "../common/process-config-entities";
|
||||||
import computeStateDomain from "../../../common/entity/compute_state_domain";
|
import computeStateDomain from "../../../common/entity/compute_state_domain";
|
||||||
import computeStateName from "../../../common/entity/compute_state_name";
|
import computeStateName from "../../../common/entity/compute_state_name";
|
||||||
import debounce from "../../../common/util/debounce";
|
import debounce from "../../../common/util/debounce";
|
||||||
|
|
||||||
Leaflet.Icon.Default.imagePath = "/static/images/leaflet";
|
Leaflet.Icon.Default.imagePath = "/static/images/leaflet";
|
||||||
|
|
||||||
class HuiMapCard extends PolymerElement {
|
class HuiMapCard extends PolymerElement {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
:host([is-panel]) ha-card {
|
:host([is-panel]) ha-card {
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
/**
|
/**
|
||||||
* In panel mode we want a full height map. Since parent #view
|
* In panel mode we want a full height map. Since parent #view
|
||||||
* only sets min-height, we need absolute positioning here
|
* only sets min-height, we need absolute positioning here
|
||||||
*/
|
*/
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-card {
|
ha-card {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
#map {
|
#map {
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
border: none;
|
border: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
paper-icon-button {
|
paper-icon-button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 75px;
|
top: 75px;
|
||||||
left: 7px;
|
left: 7px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#root {
|
#root {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host([is-panel]) #root {
|
:host([is-panel]) #root {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<ha-card id="card" header="[[_config.title]]">
|
<ha-card id="card" header="[[_config.title]]">
|
||||||
<div id="root">
|
<div id="root">
|
||||||
<div id="map"></div>
|
<div id="map"></div>
|
||||||
<paper-icon-button
|
<paper-icon-button
|
||||||
on-click="_fitMap"
|
on-click="_fitMap"
|
||||||
icon="hass:image-filter-center-focus"
|
icon="hass:image-filter-center-focus"
|
||||||
title="Reset focus"
|
title="Reset focus"
|
||||||
></paper-icon-button>
|
></paper-icon-button>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
observer: "_drawEntities",
|
observer: "_drawEntities",
|
||||||
},
|
},
|
||||||
_config: Object,
|
_config: Object,
|
||||||
isPanel: {
|
isPanel: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
reflectToAttribute: true,
|
reflectToAttribute: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this._debouncedResizeListener = debounce(this._resetMap.bind(this), 100);
|
this._debouncedResizeListener = debounce(this._resetMap.bind(this), 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
ready() {
|
ready() {
|
||||||
super.ready();
|
super.ready();
|
||||||
|
|
||||||
if (!this._config || this.isPanel) {
|
if (!this._config || this.isPanel) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$.root.style.paddingTop = this._config.aspect_ratio || "100%";
|
this.$.root.style.paddingTop = this._config.aspect_ratio || "100%";
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
if (!config) {
|
if (!config) {
|
||||||
throw new Error("Error in card configuration.");
|
throw new Error("Error in card configuration.");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._configEntities = processConfigEntities(config.entities);
|
this._configEntities = processConfigEntities(config.entities);
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
getCardSize() {
|
getCardSize() {
|
||||||
let ar = this._config.aspect_ratio || "100%";
|
let ar = this._config.aspect_ratio || "100%";
|
||||||
ar = ar.substr(0, ar.length - 1);
|
ar = ar.substr(0, ar.length - 1);
|
||||||
return 1 + Math.floor(ar / 25) || 3;
|
return 1 + Math.floor(ar / 25) || 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
|
|
||||||
// Observe changes to map size and invalidate to prevent broken rendering
|
// Observe changes to map size and invalidate to prevent broken rendering
|
||||||
// Uses ResizeObserver in Chrome, otherwise window resize event
|
// Uses ResizeObserver in Chrome, otherwise window resize event
|
||||||
if (typeof ResizeObserver === "function") {
|
if (typeof ResizeObserver === "function") {
|
||||||
this._resizeObserver = new ResizeObserver(() =>
|
this._resizeObserver = new ResizeObserver(() =>
|
||||||
this._debouncedResizeListener()
|
this._debouncedResizeListener()
|
||||||
);
|
);
|
||||||
this._resizeObserver.observe(this.$.map);
|
this._resizeObserver.observe(this.$.map);
|
||||||
} else {
|
} else {
|
||||||
window.addEventListener("resize", this._debouncedResizeListener);
|
window.addEventListener("resize", this._debouncedResizeListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._map = setupLeafletMap(this.$.map);
|
this._map = setupLeafletMap(this.$.map);
|
||||||
this._drawEntities(this.hass);
|
this._drawEntities(this.hass);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this._resetMap();
|
this._resetMap();
|
||||||
this._fitMap();
|
this._fitMap();
|
||||||
}, 1);
|
}, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
|
|
||||||
if (this._map) {
|
if (this._map) {
|
||||||
this._map.remove();
|
this._map.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._resizeObserver) {
|
if (this._resizeObserver) {
|
||||||
this._resizeObserver.unobserve(this.$.map);
|
this._resizeObserver.unobserve(this.$.map);
|
||||||
} else {
|
} else {
|
||||||
window.removeEventListener("resize", this._debouncedResizeListener);
|
window.removeEventListener("resize", this._debouncedResizeListener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_resetMap() {
|
_resetMap() {
|
||||||
if (!this._map) {
|
if (!this._map) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._map.invalidateSize();
|
this._map.invalidateSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
_fitMap() {
|
_fitMap() {
|
||||||
const zoom = this._config.default_zoom;
|
const zoom = this._config.default_zoom;
|
||||||
if (this._mapItems.length === 0) {
|
if (this._mapItems.length === 0) {
|
||||||
this._map.setView(
|
this._map.setView(
|
||||||
new Leaflet.LatLng(
|
new Leaflet.LatLng(
|
||||||
this.hass.config.latitude,
|
this.hass.config.latitude,
|
||||||
this.hass.config.longitude
|
this.hass.config.longitude
|
||||||
),
|
),
|
||||||
zoom || 14
|
zoom || 14
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bounds = new Leaflet.latLngBounds(
|
const bounds = new Leaflet.latLngBounds(
|
||||||
this._mapItems.map((item) => item.getLatLng())
|
this._mapItems.map((item) => item.getLatLng())
|
||||||
);
|
);
|
||||||
this._map.fitBounds(bounds.pad(0.5));
|
this._map.fitBounds(bounds.pad(0.5));
|
||||||
|
|
||||||
if (zoom && this._map.getZoom() > zoom) {
|
if (zoom && this._map.getZoom() > zoom) {
|
||||||
this._map.setZoom(zoom);
|
this._map.setZoom(zoom);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_drawEntities(hass) {
|
_drawEntities(hass) {
|
||||||
const map = this._map;
|
const map = this._map;
|
||||||
if (!map) {
|
if (!map) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._mapItems) {
|
if (this._mapItems) {
|
||||||
this._mapItems.forEach((marker) => marker.remove());
|
this._mapItems.forEach((marker) => marker.remove());
|
||||||
}
|
}
|
||||||
const mapItems = (this._mapItems = []);
|
const mapItems = (this._mapItems = []);
|
||||||
|
|
||||||
this._configEntities.forEach((entity) => {
|
this._configEntities.forEach((entity) => {
|
||||||
const entityId = entity.entity;
|
const entityId = entity.entity;
|
||||||
if (!(entityId in hass.states)) {
|
if (!(entityId in hass.states)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const stateObj = hass.states[entityId];
|
const stateObj = hass.states[entityId];
|
||||||
const title = computeStateName(stateObj);
|
const title = computeStateName(stateObj);
|
||||||
const {
|
const {
|
||||||
latitude,
|
latitude,
|
||||||
longitude,
|
longitude,
|
||||||
passive,
|
passive,
|
||||||
icon,
|
icon,
|
||||||
radius,
|
radius,
|
||||||
entity_picture: entityPicture,
|
entity_picture: entityPicture,
|
||||||
gps_accuracy: gpsAccuracy,
|
gps_accuracy: gpsAccuracy,
|
||||||
} = stateObj.attributes;
|
} = stateObj.attributes;
|
||||||
|
|
||||||
if (!(latitude && longitude)) {
|
if (!(latitude && longitude)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let markerIcon;
|
let markerIcon;
|
||||||
let iconHTML;
|
let iconHTML;
|
||||||
let el;
|
let el;
|
||||||
|
|
||||||
if (computeStateDomain(stateObj) === "zone") {
|
if (computeStateDomain(stateObj) === "zone") {
|
||||||
// DRAW ZONE
|
// DRAW ZONE
|
||||||
if (passive) return;
|
if (passive) return;
|
||||||
|
|
||||||
// create icon
|
// create icon
|
||||||
if (icon) {
|
if (icon) {
|
||||||
el = document.createElement("ha-icon");
|
el = document.createElement("ha-icon");
|
||||||
el.setAttribute("icon", icon);
|
el.setAttribute("icon", icon);
|
||||||
iconHTML = el.outerHTML;
|
iconHTML = el.outerHTML;
|
||||||
} else {
|
} else {
|
||||||
iconHTML = title;
|
iconHTML = title;
|
||||||
}
|
}
|
||||||
|
|
||||||
markerIcon = Leaflet.divIcon({
|
markerIcon = Leaflet.divIcon({
|
||||||
html: iconHTML,
|
html: iconHTML,
|
||||||
iconSize: [24, 24],
|
iconSize: [24, 24],
|
||||||
className: "",
|
className: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
// create market with the icon
|
// create market with the icon
|
||||||
mapItems.push(
|
mapItems.push(
|
||||||
Leaflet.marker([latitude, longitude], {
|
Leaflet.marker([latitude, longitude], {
|
||||||
icon: markerIcon,
|
icon: markerIcon,
|
||||||
interactive: false,
|
interactive: false,
|
||||||
title: title,
|
title: title,
|
||||||
}).addTo(map)
|
}).addTo(map)
|
||||||
);
|
);
|
||||||
|
|
||||||
// create circle around it
|
// create circle around it
|
||||||
mapItems.push(
|
mapItems.push(
|
||||||
Leaflet.circle([latitude, longitude], {
|
Leaflet.circle([latitude, longitude], {
|
||||||
interactive: false,
|
interactive: false,
|
||||||
color: "#FF9800",
|
color: "#FF9800",
|
||||||
radius: radius,
|
radius: radius,
|
||||||
}).addTo(map)
|
}).addTo(map)
|
||||||
);
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// DRAW ENTITY
|
// DRAW ENTITY
|
||||||
// create icon
|
// create icon
|
||||||
const entityName = title
|
const entityName = title
|
||||||
.split(" ")
|
.split(" ")
|
||||||
.map((part) => part[0])
|
.map((part) => part[0])
|
||||||
.join("")
|
.join("")
|
||||||
.substr(0, 3);
|
.substr(0, 3);
|
||||||
|
|
||||||
el = document.createElement("ha-entity-marker");
|
el = document.createElement("ha-entity-marker");
|
||||||
el.setAttribute("entity-id", entityId);
|
el.setAttribute("entity-id", entityId);
|
||||||
el.setAttribute("entity-name", entityName);
|
el.setAttribute("entity-name", entityName);
|
||||||
el.setAttribute("entity-picture", entityPicture || "");
|
el.setAttribute("entity-picture", entityPicture || "");
|
||||||
|
|
||||||
/* Leaflet clones this element before adding it to the map. This messes up
|
/* Leaflet clones this element before adding it to the map. This messes up
|
||||||
our Polymer object and we can't pass data through. Thus we hack like this. */
|
our Polymer object and we can't pass data through. Thus we hack like this. */
|
||||||
markerIcon = Leaflet.divIcon({
|
markerIcon = Leaflet.divIcon({
|
||||||
html: el.outerHTML,
|
html: el.outerHTML,
|
||||||
iconSize: [48, 48],
|
iconSize: [48, 48],
|
||||||
className: "",
|
className: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
// create market with the icon
|
// create market with the icon
|
||||||
mapItems.push(
|
mapItems.push(
|
||||||
Leaflet.marker([latitude, longitude], {
|
Leaflet.marker([latitude, longitude], {
|
||||||
icon: markerIcon,
|
icon: markerIcon,
|
||||||
title: computeStateName(stateObj),
|
title: computeStateName(stateObj),
|
||||||
}).addTo(map)
|
}).addTo(map)
|
||||||
);
|
);
|
||||||
|
|
||||||
// create circle around if entity has accuracy
|
// create circle around if entity has accuracy
|
||||||
if (gpsAccuracy) {
|
if (gpsAccuracy) {
|
||||||
mapItems.push(
|
mapItems.push(
|
||||||
Leaflet.circle([latitude, longitude], {
|
Leaflet.circle([latitude, longitude], {
|
||||||
interactive: false,
|
interactive: false,
|
||||||
color: "#0288D1",
|
color: "#0288D1",
|
||||||
radius: gpsAccuracy,
|
radius: gpsAccuracy,
|
||||||
}).addTo(map)
|
}).addTo(map)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-map-card", HuiMapCard);
|
customElements.define("hui-map-card", HuiMapCard);
|
||||||
|
|||||||
@@ -1,93 +1,93 @@
|
|||||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||||
import { classMap } from "lit-html/directives/classMap";
|
import { classMap } from "lit-html/directives/classMap";
|
||||||
|
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-markdown";
|
import "../../../components/ha-markdown";
|
||||||
|
|
||||||
import { LovelaceCard, LovelaceConfig } from "../types";
|
import { LovelaceCard, LovelaceConfig } from "../types";
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
interface Config extends LovelaceConfig {
|
interface Config extends LovelaceConfig {
|
||||||
content: string;
|
content: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class HuiMarkdownCard extends LitElement implements LovelaceCard {
|
export class HuiMarkdownCard extends LitElement implements LovelaceCard {
|
||||||
private _config?: Config;
|
private _config?: Config;
|
||||||
|
|
||||||
static get properties(): PropertyDeclarations {
|
static get properties(): PropertyDeclarations {
|
||||||
return {
|
return {
|
||||||
_config: {},
|
_config: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCardSize(): number {
|
public getCardSize(): number {
|
||||||
return this._config!.content.split("\n").length;
|
return this._config!.content.split("\n").length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setConfig(config: Config): void {
|
public setConfig(config: Config): void {
|
||||||
if (!config.content) {
|
if (!config.content) {
|
||||||
throw new Error("Invalid Configuration: Content Required");
|
throw new Error("Invalid Configuration: Content Required");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this._config) {
|
if (!this._config) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${this.renderStyle()}
|
${this.renderStyle()}
|
||||||
<ha-card .header="${this._config.title}">
|
<ha-card .header="${this._config.title}">
|
||||||
<ha-markdown
|
<ha-markdown
|
||||||
class="markdown ${classMap({
|
class="markdown ${classMap({
|
||||||
"no-header": !this._config.title,
|
"no-header": !this._config.title,
|
||||||
})}"
|
})}"
|
||||||
.content="${this._config.content}"
|
.content="${this._config.content}"
|
||||||
></ha-markdown>
|
></ha-markdown>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderStyle(): TemplateResult {
|
private renderStyle(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
:host {
|
:host {
|
||||||
@apply --paper-font-body1;
|
@apply --paper-font-body1;
|
||||||
}
|
}
|
||||||
ha-markdown {
|
ha-markdown {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 0 16px 16px;
|
padding: 0 16px 16px;
|
||||||
-ms-user-select: initial;
|
-ms-user-select: initial;
|
||||||
-webkit-user-select: initial;
|
-webkit-user-select: initial;
|
||||||
-moz-user-select: initial;
|
-moz-user-select: initial;
|
||||||
}
|
}
|
||||||
.markdown.no-header {
|
.markdown.no-header {
|
||||||
padding-top: 16px;
|
padding-top: 16px;
|
||||||
}
|
}
|
||||||
ha-markdown > *:first-child {
|
ha-markdown > *:first-child {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
ha-markdown > *:last-child {
|
ha-markdown > *:last-child {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
ha-markdown a {
|
ha-markdown a {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
ha-markdown img {
|
ha-markdown img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-markdown-card": HuiMarkdownCard;
|
"hui-markdown-card": HuiMarkdownCard;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-markdown-card", HuiMarkdownCard);
|
customElements.define("hui-markdown-card", HuiMarkdownCard);
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import "../../../cards/ha-media_player-card";
|
import "../../../cards/ha-media_player-card";
|
||||||
|
|
||||||
import LegacyWrapperCard from "./hui-legacy-wrapper-card";
|
import LegacyWrapperCard from "./hui-legacy-wrapper-card";
|
||||||
|
|
||||||
class HuiMediaControlCard extends LegacyWrapperCard {
|
class HuiMediaControlCard extends LegacyWrapperCard {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("ha-media_player-card", "media_player");
|
super("ha-media_player-card", "media_player");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-media-control-card", HuiMediaControlCard);
|
customElements.define("hui-media-control-card", HuiMediaControlCard);
|
||||||
|
|||||||
@@ -1,67 +1,67 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
|
|
||||||
import NavigateMixin from "../../../mixins/navigate-mixin";
|
import NavigateMixin from "../../../mixins/navigate-mixin";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin NavigateMixin
|
* @appliesMixin NavigateMixin
|
||||||
*/
|
*/
|
||||||
class HuiPictureCard extends NavigateMixin(PolymerElement) {
|
class HuiPictureCard extends NavigateMixin(PolymerElement) {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
ha-card {
|
ha-card {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
ha-card[clickable] {
|
ha-card[clickable] {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
img {
|
img {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<ha-card on-click="_cardClicked" clickable$='[[_computeClickable(_config)]]'>
|
<ha-card on-click="_cardClicked" clickable$='[[_computeClickable(_config)]]'>
|
||||||
<img src='[[_config.image]]' />
|
<img src='[[_config.image]]' />
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
_config: Object,
|
_config: Object,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getCardSize() {
|
getCardSize() {
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
if (!config || !config.image) {
|
if (!config || !config.image) {
|
||||||
throw new Error("Error in card configuration.");
|
throw new Error("Error in card configuration.");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeClickable(config) {
|
_computeClickable(config) {
|
||||||
return config.navigation_path || config.service;
|
return config.navigation_path || config.service;
|
||||||
}
|
}
|
||||||
|
|
||||||
_cardClicked() {
|
_cardClicked() {
|
||||||
if (this._config.navigation_path) {
|
if (this._config.navigation_path) {
|
||||||
this.navigate(this._config.navigation_path);
|
this.navigate(this._config.navigation_path);
|
||||||
}
|
}
|
||||||
if (this._config.service) {
|
if (this._config.service) {
|
||||||
const [domain, service] = this._config.service.split(".", 2);
|
const [domain, service] = this._config.service.split(".", 2);
|
||||||
this.hass.callService(domain, service, this._config.service_data);
|
this.hass.callService(domain, service, this._config.service_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-picture-card", HuiPictureCard);
|
customElements.define("hui-picture-card", HuiPictureCard);
|
||||||
|
|||||||
@@ -1,111 +1,111 @@
|
|||||||
import { html, LitElement } from "@polymer/lit-element";
|
import { html, LitElement } from "@polymer/lit-element";
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
import createHuiElement from "../common/create-hui-element";
|
import createHuiElement from "../common/create-hui-element";
|
||||||
|
|
||||||
import { LovelaceCard, LovelaceConfig } from "../types";
|
import { LovelaceCard, LovelaceConfig } from "../types";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { LovelaceElementConfig, LovelaceElement } from "../elements/types";
|
import { LovelaceElementConfig, LovelaceElement } from "../elements/types";
|
||||||
|
|
||||||
interface Config extends LovelaceConfig {
|
interface Config extends LovelaceConfig {
|
||||||
title?: string;
|
title?: string;
|
||||||
image: string;
|
image: string;
|
||||||
elements: LovelaceElementConfig[];
|
elements: LovelaceElementConfig[];
|
||||||
}
|
}
|
||||||
|
|
||||||
class HuiPictureElementsCard extends LitElement implements LovelaceCard {
|
class HuiPictureElementsCard extends LitElement implements LovelaceCard {
|
||||||
private _config?: Config;
|
private _config?: Config;
|
||||||
private _hass?: HomeAssistant;
|
private _hass?: HomeAssistant;
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
_config: {},
|
_config: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
set hass(hass: HomeAssistant) {
|
set hass(hass: HomeAssistant) {
|
||||||
this._hass = hass;
|
this._hass = hass;
|
||||||
for (const el of this.shadowRoot!.querySelectorAll("#root > *")) {
|
for (const el of this.shadowRoot!.querySelectorAll("#root > *")) {
|
||||||
const element = el as LovelaceElement;
|
const element = el as LovelaceElement;
|
||||||
element.hass = this._hass;
|
element.hass = this._hass;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCardSize(): number {
|
public getCardSize(): number {
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setConfig(config: Config): void {
|
public setConfig(config: Config): void {
|
||||||
if (!config) {
|
if (!config) {
|
||||||
throw new Error("Invalid Configuration");
|
throw new Error("Invalid Configuration");
|
||||||
} else if (!config.image) {
|
} else if (!config.image) {
|
||||||
throw new Error("Invalid Configuration: image required");
|
throw new Error("Invalid Configuration: image required");
|
||||||
} else if (!Array.isArray(config.elements)) {
|
} else if (!Array.isArray(config.elements)) {
|
||||||
throw new Error("Invalid Configuration: elements required");
|
throw new Error("Invalid Configuration: elements required");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this._config) {
|
if (!this._config) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${this.renderStyle()}
|
${this.renderStyle()}
|
||||||
<ha-card .header="${this._config.title}">
|
<ha-card .header="${this._config.title}">
|
||||||
<div id="root">
|
<div id="root">
|
||||||
<img src="${this._config.image}">
|
<img src="${this._config.image}">
|
||||||
${this._config.elements.map((elementConfig: LovelaceElementConfig) =>
|
${this._config.elements.map((elementConfig: LovelaceElementConfig) =>
|
||||||
this._createHuiElement(elementConfig)
|
this._createHuiElement(elementConfig)
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderStyle(): TemplateResult {
|
private renderStyle(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
ha-card {
|
ha-card {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
#root {
|
#root {
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
#root img {
|
#root img {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.element {
|
.element {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createHuiElement(
|
private _createHuiElement(
|
||||||
elementConfig: LovelaceElementConfig
|
elementConfig: LovelaceElementConfig
|
||||||
): LovelaceElement {
|
): LovelaceElement {
|
||||||
const element = createHuiElement(elementConfig) as LovelaceElement;
|
const element = createHuiElement(elementConfig) as LovelaceElement;
|
||||||
element.hass = this._hass;
|
element.hass = this._hass;
|
||||||
element.classList.add("element");
|
element.classList.add("element");
|
||||||
|
|
||||||
Object.keys(elementConfig.style).forEach((prop) => {
|
Object.keys(elementConfig.style).forEach((prop) => {
|
||||||
element.style.setProperty(prop, elementConfig.style[prop]);
|
element.style.setProperty(prop, elementConfig.style[prop]);
|
||||||
});
|
});
|
||||||
|
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-picture-elements-card": HuiPictureElementsCard;
|
"hui-picture-elements-card": HuiPictureElementsCard;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-picture-elements-card", HuiPictureElementsCard);
|
customElements.define("hui-picture-elements-card", HuiPictureElementsCard);
|
||||||
|
|||||||
@@ -1,201 +1,201 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../components/hui-image";
|
import "../components/hui-image";
|
||||||
|
|
||||||
import computeDomain from "../../../common/entity/compute_domain";
|
import computeDomain from "../../../common/entity/compute_domain";
|
||||||
import computeStateDisplay from "../../../common/entity/compute_state_display";
|
import computeStateDisplay from "../../../common/entity/compute_state_display";
|
||||||
import computeStateName from "../../../common/entity/compute_state_name";
|
import computeStateName from "../../../common/entity/compute_state_name";
|
||||||
import toggleEntity from "../common/entity/toggle-entity";
|
import toggleEntity from "../common/entity/toggle-entity";
|
||||||
|
|
||||||
import EventsMixin from "../../../mixins/events-mixin";
|
import EventsMixin from "../../../mixins/events-mixin";
|
||||||
import LocalizeMixin from "../../../mixins/localize-mixin";
|
import LocalizeMixin from "../../../mixins/localize-mixin";
|
||||||
import { longPressBind } from "../common/directives/long-press-directive";
|
import { longPressBind } from "../common/directives/long-press-directive";
|
||||||
|
|
||||||
const UNAVAILABLE = "Unavailable";
|
const UNAVAILABLE = "Unavailable";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin LocalizeMixin
|
* @appliesMixin LocalizeMixin
|
||||||
* @appliesMixin EventsMixin
|
* @appliesMixin EventsMixin
|
||||||
*/
|
*/
|
||||||
class HuiPictureEntityCard extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
class HuiPictureEntityCard extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
ha-card {
|
ha-card {
|
||||||
min-height: 75px;
|
min-height: 75px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
ha-card.canInteract {
|
ha-card.canInteract {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.footer {
|
.footer {
|
||||||
@apply --paper-font-common-nowrap;
|
@apply --paper-font-common-nowrap;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background-color: rgba(0, 0, 0, 0.3);
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 16px;
|
line-height: 16px;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
.both {
|
.both {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
.state {
|
.state {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<ha-card id='card'>
|
<ha-card id='card'>
|
||||||
<hui-image
|
<hui-image
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
image="[[_config.image]]"
|
image="[[_config.image]]"
|
||||||
state-image="[[_config.state_image]]"
|
state-image="[[_config.state_image]]"
|
||||||
camera-image="[[_getCameraImage(_config)]]"
|
camera-image="[[_getCameraImage(_config)]]"
|
||||||
entity="[[_config.entity]]"
|
entity="[[_config.entity]]"
|
||||||
aspect-ratio="[[_config.aspect_ratio]]"
|
aspect-ratio="[[_config.aspect_ratio]]"
|
||||||
></hui-image>
|
></hui-image>
|
||||||
<template is="dom-if" if="[[_showNameAndState(_config)]]">
|
<template is="dom-if" if="[[_showNameAndState(_config)]]">
|
||||||
<div class="footer both">
|
<div class="footer both">
|
||||||
<div>[[_name]]</div>
|
<div>[[_name]]</div>
|
||||||
<div>[[_state]]</div>
|
<div>[[_state]]</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template is="dom-if" if="[[_showName(_config)]]">
|
<template is="dom-if" if="[[_showName(_config)]]">
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
[[_name]]
|
[[_name]]
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template is="dom-if" if="[[_showState(_config)]]">
|
<template is="dom-if" if="[[_showState(_config)]]">
|
||||||
<div class="footer state">
|
<div class="footer state">
|
||||||
[[_state]]
|
[[_state]]
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
observer: "_hassChanged",
|
observer: "_hassChanged",
|
||||||
},
|
},
|
||||||
_config: Object,
|
_config: Object,
|
||||||
_name: String,
|
_name: String,
|
||||||
_state: String,
|
_state: String,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getCardSize() {
|
getCardSize() {
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
if (!config || !config.entity) {
|
if (!config || !config.entity) {
|
||||||
throw new Error("Error in card configuration.");
|
throw new Error("Error in card configuration.");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._entityDomain = computeDomain(config.entity);
|
this._entityDomain = computeDomain(config.entity);
|
||||||
if (
|
if (
|
||||||
this._entityDomain !== "camera" &&
|
this._entityDomain !== "camera" &&
|
||||||
(!config.image && !config.state_image && !config.camera_image)
|
(!config.image && !config.state_image && !config.camera_image)
|
||||||
) {
|
) {
|
||||||
throw new Error("No image source configured.");
|
throw new Error("No image source configured.");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
ready() {
|
ready() {
|
||||||
super.ready();
|
super.ready();
|
||||||
const card = this.shadowRoot.querySelector("#card");
|
const card = this.shadowRoot.querySelector("#card");
|
||||||
longPressBind(card);
|
longPressBind(card);
|
||||||
card.addEventListener("ha-click", () => this._cardClicked(false));
|
card.addEventListener("ha-click", () => this._cardClicked(false));
|
||||||
card.addEventListener("ha-hold", () => this._cardClicked(true));
|
card.addEventListener("ha-hold", () => this._cardClicked(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
_hassChanged(hass) {
|
_hassChanged(hass) {
|
||||||
const config = this._config;
|
const config = this._config;
|
||||||
const entityId = config.entity;
|
const entityId = config.entity;
|
||||||
const stateObj = hass.states[entityId];
|
const stateObj = hass.states[entityId];
|
||||||
|
|
||||||
// Nothing changed
|
// Nothing changed
|
||||||
if (
|
if (
|
||||||
(!stateObj && this._oldState === UNAVAILABLE) ||
|
(!stateObj && this._oldState === UNAVAILABLE) ||
|
||||||
(stateObj && stateObj.state === this._oldState)
|
(stateObj && stateObj.state === this._oldState)
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let name;
|
let name;
|
||||||
let state;
|
let state;
|
||||||
let stateLabel;
|
let stateLabel;
|
||||||
let available;
|
let available;
|
||||||
|
|
||||||
if (stateObj) {
|
if (stateObj) {
|
||||||
name = config.name || computeStateName(stateObj);
|
name = config.name || computeStateName(stateObj);
|
||||||
state = stateObj.state;
|
state = stateObj.state;
|
||||||
stateLabel = computeStateDisplay(this.localize, stateObj);
|
stateLabel = computeStateDisplay(this.localize, stateObj);
|
||||||
available = true;
|
available = true;
|
||||||
} else {
|
} else {
|
||||||
name = config.name || entityId;
|
name = config.name || entityId;
|
||||||
state = UNAVAILABLE;
|
state = UNAVAILABLE;
|
||||||
stateLabel = this.localize("state.default.unavailable");
|
stateLabel = this.localize("state.default.unavailable");
|
||||||
available = false;
|
available = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
_name: name,
|
_name: name,
|
||||||
_state: stateLabel,
|
_state: stateLabel,
|
||||||
_oldState: state,
|
_oldState: state,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$.card.classList.toggle("canInteract", available);
|
this.$.card.classList.toggle("canInteract", available);
|
||||||
}
|
}
|
||||||
|
|
||||||
_showNameAndState(config) {
|
_showNameAndState(config) {
|
||||||
return config.show_name !== false && config.show_state !== false;
|
return config.show_name !== false && config.show_state !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_showName(config) {
|
_showName(config) {
|
||||||
return config.show_name !== false && config.show_state === false;
|
return config.show_name !== false && config.show_state === false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_showState(config) {
|
_showState(config) {
|
||||||
return config.show_name === false && config.show_state !== false;
|
return config.show_name === false && config.show_state !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_cardClicked(hold) {
|
_cardClicked(hold) {
|
||||||
const config = this._config;
|
const config = this._config;
|
||||||
const entityId = config.entity;
|
const entityId = config.entity;
|
||||||
|
|
||||||
if (!(entityId in this.hass.states)) return;
|
if (!(entityId in this.hass.states)) return;
|
||||||
|
|
||||||
const action = hold ? config.hold_action : config.tap_action || "more-info";
|
const action = hold ? config.hold_action : config.tap_action || "more-info";
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case "toggle":
|
case "toggle":
|
||||||
toggleEntity(this.hass, entityId);
|
toggleEntity(this.hass, entityId);
|
||||||
break;
|
break;
|
||||||
case "more-info":
|
case "more-info":
|
||||||
this.fire("hass-more-info", { entityId });
|
this.fire("hass-more-info", { entityId });
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_getCameraImage(config) {
|
_getCameraImage(config) {
|
||||||
return this._entityDomain === "camera"
|
return this._entityDomain === "camera"
|
||||||
? config.entity
|
? config.entity
|
||||||
: config.camera_image;
|
: config.camera_image;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-picture-entity-card", HuiPictureEntityCard);
|
customElements.define("hui-picture-entity-card", HuiPictureEntityCard);
|
||||||
|
|||||||
@@ -1,195 +1,195 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-icon";
|
import "../../../components/ha-icon";
|
||||||
import "../components/hui-image";
|
import "../components/hui-image";
|
||||||
|
|
||||||
import computeStateDisplay from "../../../common/entity/compute_state_display";
|
import computeStateDisplay from "../../../common/entity/compute_state_display";
|
||||||
import computeStateName from "../../../common/entity/compute_state_name";
|
import computeStateName from "../../../common/entity/compute_state_name";
|
||||||
import { DOMAINS_TOGGLE } from "../../../common/const";
|
import { DOMAINS_TOGGLE } from "../../../common/const";
|
||||||
import stateIcon from "../../../common/entity/state_icon";
|
import stateIcon from "../../../common/entity/state_icon";
|
||||||
import toggleEntity from "../common/entity/toggle-entity";
|
import toggleEntity from "../common/entity/toggle-entity";
|
||||||
import processConfigEntities from "../common/process-config-entities";
|
import processConfigEntities from "../common/process-config-entities";
|
||||||
|
|
||||||
import EventsMixin from "../../../mixins/events-mixin";
|
import EventsMixin from "../../../mixins/events-mixin";
|
||||||
import LocalizeMixin from "../../../mixins/localize-mixin";
|
import LocalizeMixin from "../../../mixins/localize-mixin";
|
||||||
import NavigateMixin from "../../../mixins/navigate-mixin";
|
import NavigateMixin from "../../../mixins/navigate-mixin";
|
||||||
import computeDomain from "../../../common/entity/compute_domain";
|
import computeDomain from "../../../common/entity/compute_domain";
|
||||||
|
|
||||||
const STATES_OFF = new Set(["closed", "locked", "not_home", "off"]);
|
const STATES_OFF = new Set(["closed", "locked", "not_home", "off"]);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin EventsMixin
|
* @appliesMixin EventsMixin
|
||||||
* @appliesMixin LocalizeMixin
|
* @appliesMixin LocalizeMixin
|
||||||
* @appliesMixin NavigateMixin
|
* @appliesMixin NavigateMixin
|
||||||
*/
|
*/
|
||||||
class HuiPictureGlanceCard extends NavigateMixin(
|
class HuiPictureGlanceCard extends NavigateMixin(
|
||||||
LocalizeMixin(EventsMixin(PolymerElement))
|
LocalizeMixin(EventsMixin(PolymerElement))
|
||||||
) {
|
) {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
ha-card {
|
ha-card {
|
||||||
position: relative;
|
position: relative;
|
||||||
min-height: 48px;
|
min-height: 48px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
hui-image.clickable {
|
hui-image.clickable {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.box {
|
.box {
|
||||||
@apply --paper-font-common-nowrap;
|
@apply --paper-font-common-nowrap;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background-color: rgba(0, 0, 0, 0.3);
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 40px;
|
line-height: 40px;
|
||||||
color: white;
|
color: white;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
.box .title {
|
.box .title {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
ha-icon {
|
ha-icon {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
color: #A9A9A9;
|
color: #A9A9A9;
|
||||||
}
|
}
|
||||||
ha-icon.state-on {
|
ha-icon.state-on {
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<ha-card>
|
<ha-card>
|
||||||
<hui-image
|
<hui-image
|
||||||
class$='[[_computeImageClass(_config)]]'
|
class$='[[_computeImageClass(_config)]]'
|
||||||
on-click='_handleImageClick'
|
on-click='_handleImageClick'
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
image="[[_config.image]]"
|
image="[[_config.image]]"
|
||||||
state-image="[[_config.state_image]]"
|
state-image="[[_config.state_image]]"
|
||||||
camera-image="[[_config.camera_image]]"
|
camera-image="[[_config.camera_image]]"
|
||||||
entity="[[_config.entity]]"
|
entity="[[_config.entity]]"
|
||||||
aspect-ratio="[[_config.aspect_ratio]]"
|
aspect-ratio="[[_config.aspect_ratio]]"
|
||||||
></hui-image>
|
></hui-image>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<template is="dom-if" if="[[_config.title]]">
|
<template is="dom-if" if="[[_config.title]]">
|
||||||
<div class="title">[[_config.title]]</div>
|
<div class="title">[[_config.title]]</div>
|
||||||
</template>
|
</template>
|
||||||
<div>
|
<div>
|
||||||
<template is="dom-repeat" items="[[_computeVisible(_entitiesDialog, hass.states)]]">
|
<template is="dom-repeat" items="[[_computeVisible(_entitiesDialog, hass.states)]]">
|
||||||
<ha-icon
|
<ha-icon
|
||||||
on-click="_openDialog"
|
on-click="_openDialog"
|
||||||
class$="[[_computeButtonClass(item.entity, hass.states)]]"
|
class$="[[_computeButtonClass(item.entity, hass.states)]]"
|
||||||
icon="[[_computeIcon(item, hass.states)]]"
|
icon="[[_computeIcon(item, hass.states)]]"
|
||||||
title="[[_computeTooltip(item.entity, hass.states)]]"
|
title="[[_computeTooltip(item.entity, hass.states)]]"
|
||||||
></ha-icon>
|
></ha-icon>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<template is="dom-repeat" items="[[_computeVisible(_entitiesToggle, hass.states)]]">
|
<template is="dom-repeat" items="[[_computeVisible(_entitiesToggle, hass.states)]]">
|
||||||
<ha-icon
|
<ha-icon
|
||||||
on-click="_callService"
|
on-click="_callService"
|
||||||
class$="[[_computeButtonClass(item.entity, hass.states)]]"
|
class$="[[_computeButtonClass(item.entity, hass.states)]]"
|
||||||
icon="[[_computeIcon(item, hass.states)]]"
|
icon="[[_computeIcon(item, hass.states)]]"
|
||||||
title="[[_computeTooltip(item.entity, hass.states)]]"
|
title="[[_computeTooltip(item.entity, hass.states)]]"
|
||||||
></ha-icon>
|
></ha-icon>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
_config: Object,
|
_config: Object,
|
||||||
_entitiesDialog: Array,
|
_entitiesDialog: Array,
|
||||||
_entitiesToggle: Array,
|
_entitiesToggle: Array,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getCardSize() {
|
getCardSize() {
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
if (
|
if (
|
||||||
!config ||
|
!config ||
|
||||||
!config.entities ||
|
!config.entities ||
|
||||||
!Array.isArray(config.entities) ||
|
!Array.isArray(config.entities) ||
|
||||||
!(config.image || config.camera_image || config.state_image) ||
|
!(config.image || config.camera_image || config.state_image) ||
|
||||||
(config.state_image && !config.entity)
|
(config.state_image && !config.entity)
|
||||||
) {
|
) {
|
||||||
throw new Error("Invalid card configuration");
|
throw new Error("Invalid card configuration");
|
||||||
}
|
}
|
||||||
const entities = processConfigEntities(config.entities);
|
const entities = processConfigEntities(config.entities);
|
||||||
const dialog = [];
|
const dialog = [];
|
||||||
const toggle = [];
|
const toggle = [];
|
||||||
|
|
||||||
entities.forEach((item) => {
|
entities.forEach((item) => {
|
||||||
if (
|
if (
|
||||||
config.force_dialog ||
|
config.force_dialog ||
|
||||||
!DOMAINS_TOGGLE.has(computeDomain(item.entity))
|
!DOMAINS_TOGGLE.has(computeDomain(item.entity))
|
||||||
) {
|
) {
|
||||||
dialog.push(item);
|
dialog.push(item);
|
||||||
} else {
|
} else {
|
||||||
toggle.push(item);
|
toggle.push(item);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
_config: config,
|
_config: config,
|
||||||
_entitiesDialog: dialog,
|
_entitiesDialog: dialog,
|
||||||
_entitiesToggle: toggle,
|
_entitiesToggle: toggle,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeVisible(collection, states) {
|
_computeVisible(collection, states) {
|
||||||
return collection.filter((el) => el.entity in states);
|
return collection.filter((el) => el.entity in states);
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeIcon(item, states) {
|
_computeIcon(item, states) {
|
||||||
return item.icon || stateIcon(states[item.entity]);
|
return item.icon || stateIcon(states[item.entity]);
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeButtonClass(entityId, states) {
|
_computeButtonClass(entityId, states) {
|
||||||
return STATES_OFF.has(states[entityId].state) ? "" : "state-on";
|
return STATES_OFF.has(states[entityId].state) ? "" : "state-on";
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeTooltip(entityId, states) {
|
_computeTooltip(entityId, states) {
|
||||||
return `${computeStateName(states[entityId])}: ${computeStateDisplay(
|
return `${computeStateName(states[entityId])}: ${computeStateDisplay(
|
||||||
this.localize,
|
this.localize,
|
||||||
states[entityId]
|
states[entityId]
|
||||||
)}`;
|
)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeImageClass(config) {
|
_computeImageClass(config) {
|
||||||
return config.navigation_path || config.camera_image ? "clickable" : "";
|
return config.navigation_path || config.camera_image ? "clickable" : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
_openDialog(ev) {
|
_openDialog(ev) {
|
||||||
this.fire("hass-more-info", { entityId: ev.model.item.entity });
|
this.fire("hass-more-info", { entityId: ev.model.item.entity });
|
||||||
}
|
}
|
||||||
|
|
||||||
_callService(ev) {
|
_callService(ev) {
|
||||||
toggleEntity(this.hass, ev.model.item.entity);
|
toggleEntity(this.hass, ev.model.item.entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleImageClick() {
|
_handleImageClick() {
|
||||||
if (this._config.navigation_path) {
|
if (this._config.navigation_path) {
|
||||||
this.navigate(this._config.navigation_path);
|
this.navigate(this._config.navigation_path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._config.camera_image) {
|
if (this._config.camera_image) {
|
||||||
this.fire("hass-more-info", { entityId: this._config.camera_image });
|
this.fire("hass-more-info", { entityId: this._config.camera_image });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define("hui-picture-glance-card", HuiPictureGlanceCard);
|
customElements.define("hui-picture-glance-card", HuiPictureGlanceCard);
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import "../../../cards/ha-plant-card";
|
import "../../../cards/ha-plant-card";
|
||||||
|
|
||||||
import LegacyWrapperCard from "./hui-legacy-wrapper-card";
|
import LegacyWrapperCard from "./hui-legacy-wrapper-card";
|
||||||
|
|
||||||
class HuiPlantStatusCard extends LegacyWrapperCard {
|
class HuiPlantStatusCard extends LegacyWrapperCard {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("ha-plant-card", "plant");
|
super("ha-plant-card", "plant");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-plant-status-card", HuiPlantStatusCard);
|
customElements.define("hui-plant-status-card", HuiPlantStatusCard);
|
||||||
|
|||||||
@@ -1,292 +1,292 @@
|
|||||||
import { LitElement, html, svg } from "@polymer/lit-element";
|
import { LitElement, html, svg } from "@polymer/lit-element";
|
||||||
|
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-icon";
|
import "../../../components/ha-icon";
|
||||||
|
|
||||||
import computeStateName from "../../../common/entity/compute_state_name";
|
import computeStateName from "../../../common/entity/compute_state_name";
|
||||||
import stateIcon from "../../../common/entity/state_icon";
|
import stateIcon from "../../../common/entity/state_icon";
|
||||||
|
|
||||||
import EventsMixin from "../../../mixins/events-mixin";
|
import EventsMixin from "../../../mixins/events-mixin";
|
||||||
|
|
||||||
class HuiSensorCard extends EventsMixin(LitElement) {
|
class HuiSensorCard extends EventsMixin(LitElement) {
|
||||||
set hass(hass) {
|
set hass(hass) {
|
||||||
this._hass = hass;
|
this._hass = hass;
|
||||||
const entity = hass.states[this._config.entity];
|
const entity = hass.states[this._config.entity];
|
||||||
if (entity && this._entity !== entity) {
|
if (entity && this._entity !== entity) {
|
||||||
this._entity = entity;
|
this._entity = entity;
|
||||||
if (
|
if (
|
||||||
this._config.graph !== "none" &&
|
this._config.graph !== "none" &&
|
||||||
entity.attributes.unit_of_measurement
|
entity.attributes.unit_of_measurement
|
||||||
) {
|
) {
|
||||||
this._getHistory();
|
this._getHistory();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
_hass: {},
|
_hass: {},
|
||||||
_config: {},
|
_config: {},
|
||||||
_entity: {},
|
_entity: {},
|
||||||
_line: String,
|
_line: String,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
if (!config.entity || config.entity.split(".")[0] !== "sensor") {
|
if (!config.entity || config.entity.split(".")[0] !== "sensor") {
|
||||||
throw new Error("Specify an entity from within the sensor domain.");
|
throw new Error("Specify an entity from within the sensor domain.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const cardConfig = {
|
const cardConfig = {
|
||||||
icon: false,
|
icon: false,
|
||||||
hours_to_show: 24,
|
hours_to_show: 24,
|
||||||
accuracy: 10,
|
accuracy: 10,
|
||||||
height: 100,
|
height: 100,
|
||||||
line_width: 5,
|
line_width: 5,
|
||||||
line_color: "var(--accent-color)",
|
line_color: "var(--accent-color)",
|
||||||
...config,
|
...config,
|
||||||
};
|
};
|
||||||
cardConfig.hours_to_show = Number(cardConfig.hours_to_show);
|
cardConfig.hours_to_show = Number(cardConfig.hours_to_show);
|
||||||
cardConfig.accuracy = Number(cardConfig.accuracy);
|
cardConfig.accuracy = Number(cardConfig.accuracy);
|
||||||
cardConfig.height = Number(cardConfig.height);
|
cardConfig.height = Number(cardConfig.height);
|
||||||
cardConfig.line_width = Number(cardConfig.line_width);
|
cardConfig.line_width = Number(cardConfig.line_width);
|
||||||
|
|
||||||
this._config = cardConfig;
|
this._config = cardConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldUpdate(changedProps) {
|
shouldUpdate(changedProps) {
|
||||||
const change = changedProps.has("_entity") || changedProps.has("_line");
|
const change = changedProps.has("_entity") || changedProps.has("_line");
|
||||||
return change;
|
return change;
|
||||||
}
|
}
|
||||||
|
|
||||||
render({ _config, _entity, _line } = this) {
|
render({ _config, _entity, _line } = this) {
|
||||||
return html`
|
return html`
|
||||||
${this._style()}
|
${this._style()}
|
||||||
<ha-card @click=${this._handleClick}>
|
<ha-card @click=${this._handleClick}>
|
||||||
<div class='flex'>
|
<div class='flex'>
|
||||||
<div class='icon'>
|
<div class='icon'>
|
||||||
<ha-icon .icon=${this._computeIcon(_entity)}></ha-icon>
|
<ha-icon .icon=${this._computeIcon(_entity)}></ha-icon>
|
||||||
</div>
|
</div>
|
||||||
<div class='header'>
|
<div class='header'>
|
||||||
<span class='name'>${this._computeName(_entity)}</span>
|
<span class='name'>${this._computeName(_entity)}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class='flex info'>
|
<div class='flex info'>
|
||||||
<span id='value'>${_entity.state}</span>
|
<span id='value'>${_entity.state}</span>
|
||||||
<span id='measurement'>${this._computeUom(_entity)}</span>
|
<span id='measurement'>${this._computeUom(_entity)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class='graph'>
|
<div class='graph'>
|
||||||
<div>
|
<div>
|
||||||
${
|
${
|
||||||
_line
|
_line
|
||||||
? svg`
|
? svg`
|
||||||
<svg width='100%' height='100%' viewBox='0 0 500 ${_config.height}'>
|
<svg width='100%' height='100%' viewBox='0 0 500 ${_config.height}'>
|
||||||
<path d=${_line} fill='none' stroke=${_config.line_color}
|
<path d=${_line} fill='none' stroke=${_config.line_color}
|
||||||
stroke-width=${_config.line_width}
|
stroke-width=${_config.line_width}
|
||||||
stroke-linecap='round' stroke-linejoin='round' />
|
stroke-linecap='round' stroke-linejoin='round' />
|
||||||
</svg>`
|
</svg>`
|
||||||
: ""
|
: ""
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>`;
|
</ha-card>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleClick() {
|
_handleClick() {
|
||||||
this.fire("hass-more-info", { entityId: this._config.entity });
|
this.fire("hass-more-info", { entityId: this._config.entity });
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeIcon(item) {
|
_computeIcon(item) {
|
||||||
return this._config.icon || stateIcon(item);
|
return this._config.icon || stateIcon(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeName(item) {
|
_computeName(item) {
|
||||||
return this._config.name || computeStateName(item);
|
return this._config.name || computeStateName(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeUom(item) {
|
_computeUom(item) {
|
||||||
return this._config.unit || item.attributes.unit_of_measurement;
|
return this._config.unit || item.attributes.unit_of_measurement;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getGraph(items, width, height) {
|
_getGraph(items, width, height) {
|
||||||
const values = this._getValueArr(items);
|
const values = this._getValueArr(items);
|
||||||
const coords = this._calcCoordinates(values, width, height);
|
const coords = this._calcCoordinates(values, width, height);
|
||||||
return this._getPath(coords);
|
return this._getPath(coords);
|
||||||
}
|
}
|
||||||
|
|
||||||
_getValueArr(items) {
|
_getValueArr(items) {
|
||||||
return items.map((item) => Number(item.state) || 0);
|
return items.map((item) => Number(item.state) || 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
_calcCoordinates(values, width, height) {
|
_calcCoordinates(values, width, height) {
|
||||||
const margin = this._config.line_width;
|
const margin = this._config.line_width;
|
||||||
width -= margin * 2;
|
width -= margin * 2;
|
||||||
height -= margin * 2;
|
height -= margin * 2;
|
||||||
const min = Math.floor(Math.min.apply(null, values) * 0.95);
|
const min = Math.floor(Math.min.apply(null, values) * 0.95);
|
||||||
const max = Math.ceil(Math.max.apply(null, values) * 1.05);
|
const max = Math.ceil(Math.max.apply(null, values) * 1.05);
|
||||||
|
|
||||||
if (values.length === 1) values.push(values[0]);
|
if (values.length === 1) values.push(values[0]);
|
||||||
|
|
||||||
const yRatio = (max - min) / height;
|
const yRatio = (max - min) / height;
|
||||||
const xRatio = width / (values.length - 1);
|
const xRatio = width / (values.length - 1);
|
||||||
|
|
||||||
return values.map((value, i) => {
|
return values.map((value, i) => {
|
||||||
const y = height - (value - min) / yRatio || 0;
|
const y = height - (value - min) / yRatio || 0;
|
||||||
const x = xRatio * i + margin;
|
const x = xRatio * i + margin;
|
||||||
return [x, y];
|
return [x, y];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_getPath(points) {
|
_getPath(points) {
|
||||||
const SPACE = " ";
|
const SPACE = " ";
|
||||||
let next;
|
let next;
|
||||||
let Z;
|
let Z;
|
||||||
const X = 0;
|
const X = 0;
|
||||||
const Y = 1;
|
const Y = 1;
|
||||||
let path = "";
|
let path = "";
|
||||||
let point = points[0];
|
let point = points[0];
|
||||||
|
|
||||||
path += "M" + point[X] + "," + point[Y];
|
path += "M" + point[X] + "," + point[Y];
|
||||||
const first = point;
|
const first = point;
|
||||||
|
|
||||||
for (let i = 0; i < points.length; i++) {
|
for (let i = 0; i < points.length; i++) {
|
||||||
next = points[i];
|
next = points[i];
|
||||||
Z = this._midPoint(point[X], point[Y], next[X], next[Y]);
|
Z = this._midPoint(point[X], point[Y], next[X], next[Y]);
|
||||||
path += SPACE + Z[X] + "," + Z[Y];
|
path += SPACE + Z[X] + "," + Z[Y];
|
||||||
path += "Q" + Math.floor(next[X]) + "," + next[Y];
|
path += "Q" + Math.floor(next[X]) + "," + next[Y];
|
||||||
point = next;
|
point = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
const second = points[1];
|
const second = points[1];
|
||||||
Z = this._midPoint(first[X], first[Y], second[X], second[Y]);
|
Z = this._midPoint(first[X], first[Y], second[X], second[Y]);
|
||||||
path += SPACE + Math.floor(next[X]) + "." + points[points.length - 1];
|
path += SPACE + Math.floor(next[X]) + "." + points[points.length - 1];
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
_midPoint(Ax, Ay, Bx, By) {
|
_midPoint(Ax, Ay, Bx, By) {
|
||||||
const Zx = (Ax - Bx) / 2 + Bx;
|
const Zx = (Ax - Bx) / 2 + Bx;
|
||||||
const Zy = (Ay - By) / 2 + By;
|
const Zy = (Ay - By) / 2 + By;
|
||||||
return [Zx, Zy];
|
return [Zx, Zy];
|
||||||
}
|
}
|
||||||
|
|
||||||
async _getHistory() {
|
async _getHistory() {
|
||||||
const endTime = new Date();
|
const endTime = new Date();
|
||||||
const startTime = new Date();
|
const startTime = new Date();
|
||||||
startTime.setHours(endTime.getHours() - this._config.hours_to_show);
|
startTime.setHours(endTime.getHours() - this._config.hours_to_show);
|
||||||
const stateHistory = await this._fetchRecent(
|
const stateHistory = await this._fetchRecent(
|
||||||
this._config.entity,
|
this._config.entity,
|
||||||
startTime,
|
startTime,
|
||||||
endTime
|
endTime
|
||||||
);
|
);
|
||||||
const history = stateHistory[0];
|
const history = stateHistory[0];
|
||||||
const valArray = [history[history.length - 1]];
|
const valArray = [history[history.length - 1]];
|
||||||
|
|
||||||
let pos = history.length - 1;
|
let pos = history.length - 1;
|
||||||
const accuracy = this._config.accuracy <= pos ? this._config.accuracy : pos;
|
const accuracy = this._config.accuracy <= pos ? this._config.accuracy : pos;
|
||||||
let increment = Math.ceil(history.length / accuracy);
|
let increment = Math.ceil(history.length / accuracy);
|
||||||
increment = increment <= 0 ? 1 : increment;
|
increment = increment <= 0 ? 1 : increment;
|
||||||
for (let i = accuracy; i >= 2; i--) {
|
for (let i = accuracy; i >= 2; i--) {
|
||||||
pos -= increment;
|
pos -= increment;
|
||||||
valArray.unshift(pos >= 0 ? history[pos] : history[0]);
|
valArray.unshift(pos >= 0 ? history[pos] : history[0]);
|
||||||
}
|
}
|
||||||
this._line = this._getGraph(valArray, 500, this._config.height);
|
this._line = this._getGraph(valArray, 500, this._config.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _fetchRecent(entityId, startTime, endTime) {
|
async _fetchRecent(entityId, startTime, endTime) {
|
||||||
let url = "history/period";
|
let url = "history/period";
|
||||||
if (startTime) url += "/" + startTime.toISOString();
|
if (startTime) url += "/" + startTime.toISOString();
|
||||||
url += "?filter_entity_id=" + entityId;
|
url += "?filter_entity_id=" + entityId;
|
||||||
if (endTime) url += "&end_time=" + endTime.toISOString();
|
if (endTime) url += "&end_time=" + endTime.toISOString();
|
||||||
|
|
||||||
return await this._hass.callApi("GET", url);
|
return await this._hass.callApi("GET", url);
|
||||||
}
|
}
|
||||||
|
|
||||||
getCardSize() {
|
getCardSize() {
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
_style() {
|
_style() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
:host {
|
:host {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
ha-card {
|
ha-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
position: relative;
|
position: relative;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.flex {
|
.flex {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
.header {
|
.header {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
opacity: .8;
|
opacity: .8;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.name {
|
.name {
|
||||||
display: block;
|
display: block;
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
max-height: 1.4rem;
|
max-height: 1.4rem;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
opacity: .8;
|
opacity: .8;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
-webkit-line-clamp: 1;
|
-webkit-line-clamp: 1;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
.icon {
|
.icon {
|
||||||
color: var(--paper-item-icon-color, #44739e);
|
color: var(--paper-item-icon-color, #44739e);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
flex: 0 0 40px;
|
flex: 0 0 40px;
|
||||||
line-height: 40px;
|
line-height: 40px;
|
||||||
position: relative;
|
position: relative;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
}
|
}
|
||||||
.info {
|
.info {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
margin: 16px 0 16px 8px;
|
margin: 16px 0 16px 8px;
|
||||||
}
|
}
|
||||||
#value {
|
#value {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: 1em;
|
line-height: 1em;
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
#measurement {
|
#measurement {
|
||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: 1.3rem;
|
font-size: 1.3rem;
|
||||||
line-height: 1.2em;
|
line-height: 1.2em;
|
||||||
margin-top: .1em;
|
margin-top: .1em;
|
||||||
opacity: .6;
|
opacity: .6;
|
||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
}
|
}
|
||||||
.graph {
|
.graph {
|
||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
margin-bottom: 0px;
|
margin-bottom: 0px;
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.graph > div {
|
.graph > div {
|
||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
margin: auto 8px;
|
margin: auto 8px;
|
||||||
}
|
}
|
||||||
</style>`;
|
</style>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-sensor-card", HuiSensorCard);
|
customElements.define("hui-sensor-card", HuiSensorCard);
|
||||||
|
|||||||
@@ -1,66 +1,66 @@
|
|||||||
import { html, LitElement } from "@polymer/lit-element";
|
import { html, LitElement } from "@polymer/lit-element";
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
import createCardElement from "../common/create-card-element";
|
import createCardElement from "../common/create-card-element";
|
||||||
|
|
||||||
import { LovelaceCard, LovelaceConfig } from "../types";
|
import { LovelaceCard, LovelaceConfig } from "../types";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
|
|
||||||
interface Config extends LovelaceConfig {
|
interface Config extends LovelaceConfig {
|
||||||
cards: LovelaceConfig[];
|
cards: LovelaceConfig[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class HuiStackCard extends LitElement implements LovelaceCard {
|
export abstract class HuiStackCard extends LitElement implements LovelaceCard {
|
||||||
protected _cards?: LovelaceCard[];
|
protected _cards?: LovelaceCard[];
|
||||||
private _config?: Config;
|
private _config?: Config;
|
||||||
private _hass?: HomeAssistant;
|
private _hass?: HomeAssistant;
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
_config: {},
|
_config: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
set hass(hass: HomeAssistant) {
|
set hass(hass: HomeAssistant) {
|
||||||
this._hass = hass;
|
this._hass = hass;
|
||||||
|
|
||||||
if (!this._cards) {
|
if (!this._cards) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const element of this._cards) {
|
for (const element of this._cards) {
|
||||||
element.hass = this._hass;
|
element.hass = this._hass;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract getCardSize(): number;
|
public abstract getCardSize(): number;
|
||||||
|
|
||||||
public setConfig(config: Config): void {
|
public setConfig(config: Config): void {
|
||||||
if (!config || !config.cards || !Array.isArray(config.cards)) {
|
if (!config || !config.cards || !Array.isArray(config.cards)) {
|
||||||
throw new Error("Card config incorrect");
|
throw new Error("Card config incorrect");
|
||||||
}
|
}
|
||||||
this._config = config;
|
this._config = config;
|
||||||
this._cards = config.cards.map((card) => {
|
this._cards = config.cards.map((card) => {
|
||||||
const element = createCardElement(card) as LovelaceCard;
|
const element = createCardElement(card) as LovelaceCard;
|
||||||
if (this._hass) {
|
if (this._hass) {
|
||||||
element.hass = this._hass;
|
element.hass = this._hass;
|
||||||
}
|
}
|
||||||
return element;
|
return element;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this._config) {
|
if (!this._config) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${this.renderStyle()}
|
${this.renderStyle()}
|
||||||
<div id="root">
|
<div id="root">
|
||||||
${this._cards}
|
${this._cards}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract renderStyle(): TemplateResult;
|
protected abstract renderStyle(): TemplateResult;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,390 +1,390 @@
|
|||||||
import {
|
import {
|
||||||
html,
|
html,
|
||||||
LitElement,
|
LitElement,
|
||||||
PropertyDeclarations,
|
PropertyDeclarations,
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
} from "@polymer/lit-element";
|
} from "@polymer/lit-element";
|
||||||
import { classMap } from "lit-html/directives/classMap";
|
import { classMap } from "lit-html/directives/classMap";
|
||||||
import { jQuery } from "../../../resources/jquery";
|
import { jQuery } from "../../../resources/jquery";
|
||||||
|
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-icon";
|
import "../../../components/ha-icon";
|
||||||
import { roundSliderStyle } from "../../../resources/jquery.roundslider";
|
import { roundSliderStyle } from "../../../resources/jquery.roundslider";
|
||||||
|
|
||||||
import { HomeAssistant, ClimateEntity } from "../../../types";
|
import { HomeAssistant, ClimateEntity } from "../../../types";
|
||||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
||||||
import { LovelaceCard, LovelaceConfig } from "../types";
|
import { LovelaceCard, LovelaceConfig } from "../types";
|
||||||
import computeStateName from "../../../common/entity/compute_state_name";
|
import computeStateName from "../../../common/entity/compute_state_name";
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
const thermostatConfig = {
|
const thermostatConfig = {
|
||||||
radius: 150,
|
radius: 150,
|
||||||
step: 1,
|
step: 1,
|
||||||
circleShape: "pie",
|
circleShape: "pie",
|
||||||
startAngle: 315,
|
startAngle: 315,
|
||||||
width: 5,
|
width: 5,
|
||||||
lineCap: "round",
|
lineCap: "round",
|
||||||
handleSize: "+10",
|
handleSize: "+10",
|
||||||
showTooltip: false,
|
showTooltip: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const modeIcons = {
|
const modeIcons = {
|
||||||
auto: "hass:autorenew",
|
auto: "hass:autorenew",
|
||||||
heat: "hass:fire",
|
heat: "hass:fire",
|
||||||
cool: "hass:snowflake",
|
cool: "hass:snowflake",
|
||||||
off: "hass:power",
|
off: "hass:power",
|
||||||
};
|
};
|
||||||
|
|
||||||
interface Config extends LovelaceConfig {
|
interface Config extends LovelaceConfig {
|
||||||
entity: string;
|
entity: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatTemp(temps: string[]): string {
|
function formatTemp(temps: string[]): string {
|
||||||
return temps.filter(Boolean).join("-");
|
return temps.filter(Boolean).join("-");
|
||||||
}
|
}
|
||||||
|
|
||||||
export class HuiThermostatCard extends hassLocalizeLitMixin(LitElement)
|
export class HuiThermostatCard extends hassLocalizeLitMixin(LitElement)
|
||||||
implements LovelaceCard {
|
implements LovelaceCard {
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
private _config?: Config;
|
private _config?: Config;
|
||||||
|
|
||||||
static get properties(): PropertyDeclarations {
|
static get properties(): PropertyDeclarations {
|
||||||
return {
|
return {
|
||||||
hass: {},
|
hass: {},
|
||||||
_config: {},
|
_config: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCardSize(): number {
|
public getCardSize(): number {
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setConfig(config: Config): void {
|
public setConfig(config: Config): void {
|
||||||
if (!config.entity || config.entity.split(".")[0] !== "climate") {
|
if (!config.entity || config.entity.split(".")[0] !== "climate") {
|
||||||
throw new Error("Specify an entity from within the climate domain.");
|
throw new Error("Specify an entity from within the climate domain.");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this.hass || !this._config) {
|
if (!this.hass || !this._config) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
const stateObj = this.hass.states[this._config.entity] as ClimateEntity;
|
const stateObj = this.hass.states[this._config.entity] as ClimateEntity;
|
||||||
const broadCard = this.clientWidth > 390;
|
const broadCard = this.clientWidth > 390;
|
||||||
const mode = modeIcons[stateObj.attributes.operation_mode || ""]
|
const mode = modeIcons[stateObj.attributes.operation_mode || ""]
|
||||||
? stateObj.attributes.operation_mode!
|
? stateObj.attributes.operation_mode!
|
||||||
: "unknown-mode";
|
: "unknown-mode";
|
||||||
return html`
|
return html`
|
||||||
${this.renderStyle()}
|
${this.renderStyle()}
|
||||||
<ha-card
|
<ha-card
|
||||||
class="${classMap({
|
class="${classMap({
|
||||||
[mode]: true,
|
[mode]: true,
|
||||||
large: broadCard,
|
large: broadCard,
|
||||||
small: !broadCard,
|
small: !broadCard,
|
||||||
})}">
|
})}">
|
||||||
<div id="root">
|
<div id="root">
|
||||||
<div id="thermostat"></div>
|
<div id="thermostat"></div>
|
||||||
<div id="tooltip">
|
<div id="tooltip">
|
||||||
<div class="title">${computeStateName(stateObj)}</div>
|
<div class="title">${computeStateName(stateObj)}</div>
|
||||||
<div class="current-temperature">
|
<div class="current-temperature">
|
||||||
<span class="current-temperature-text">${
|
<span class="current-temperature-text">${
|
||||||
stateObj.attributes.current_temperature
|
stateObj.attributes.current_temperature
|
||||||
}
|
}
|
||||||
<span class="uom">${
|
<span class="uom">${
|
||||||
this.hass.config.unit_system.temperature
|
this.hass.config.unit_system.temperature
|
||||||
}</span>
|
}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="climate-info">
|
<div class="climate-info">
|
||||||
<div id="set-temperature"></div>
|
<div id="set-temperature"></div>
|
||||||
<div class="current-mode">${this.localize(
|
<div class="current-mode">${this.localize(
|
||||||
`state.climate.${stateObj.state}`
|
`state.climate.${stateObj.state}`
|
||||||
)}</div>
|
)}</div>
|
||||||
<div class="modes">
|
<div class="modes">
|
||||||
${(stateObj.attributes.operation_list || []).map((modeItem) =>
|
${(stateObj.attributes.operation_list || []).map((modeItem) =>
|
||||||
this._renderIcon(modeItem, mode)
|
this._renderIcon(modeItem, mode)
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||||
if (changedProps.get("hass")) {
|
if (changedProps.get("hass")) {
|
||||||
return (
|
return (
|
||||||
(changedProps.get("hass") as any).states[this._config!.entity] !==
|
(changedProps.get("hass") as any).states[this._config!.entity] !==
|
||||||
this.hass!.states[this._config!.entity]
|
this.hass!.states[this._config!.entity]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (changedProps.has("_config")) {
|
if (changedProps.has("_config")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected firstUpdated(): void {
|
protected firstUpdated(): void {
|
||||||
const stateObj = this.hass!.states[this._config!.entity] as ClimateEntity;
|
const stateObj = this.hass!.states[this._config!.entity] as ClimateEntity;
|
||||||
|
|
||||||
const _sliderType =
|
const _sliderType =
|
||||||
stateObj.attributes.target_temp_low &&
|
stateObj.attributes.target_temp_low &&
|
||||||
stateObj.attributes.target_temp_high
|
stateObj.attributes.target_temp_high
|
||||||
? "range"
|
? "range"
|
||||||
: "min-range";
|
: "min-range";
|
||||||
|
|
||||||
jQuery("#thermostat", this.shadowRoot).roundSlider({
|
jQuery("#thermostat", this.shadowRoot).roundSlider({
|
||||||
...thermostatConfig,
|
...thermostatConfig,
|
||||||
radius: this.clientWidth / 3,
|
radius: this.clientWidth / 3,
|
||||||
min: stateObj.attributes.min_temp,
|
min: stateObj.attributes.min_temp,
|
||||||
max: stateObj.attributes.max_temp,
|
max: stateObj.attributes.max_temp,
|
||||||
sliderType: _sliderType,
|
sliderType: _sliderType,
|
||||||
change: (value) => this._setTemperature(value),
|
change: (value) => this._setTemperature(value),
|
||||||
drag: (value) => this._dragEvent(value),
|
drag: (value) => this._dragEvent(value),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updated(): void {
|
protected updated(): void {
|
||||||
const stateObj = this.hass!.states[this._config!.entity] as ClimateEntity;
|
const stateObj = this.hass!.states[this._config!.entity] as ClimateEntity;
|
||||||
|
|
||||||
let sliderValue;
|
let sliderValue;
|
||||||
let uiValue;
|
let uiValue;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
stateObj.attributes.target_temp_low &&
|
stateObj.attributes.target_temp_low &&
|
||||||
stateObj.attributes.target_temp_high
|
stateObj.attributes.target_temp_high
|
||||||
) {
|
) {
|
||||||
sliderValue = `${stateObj.attributes.target_temp_low}, ${
|
sliderValue = `${stateObj.attributes.target_temp_low}, ${
|
||||||
stateObj.attributes.target_temp_high
|
stateObj.attributes.target_temp_high
|
||||||
}`;
|
}`;
|
||||||
uiValue = formatTemp([
|
uiValue = formatTemp([
|
||||||
String(stateObj.attributes.target_temp_low),
|
String(stateObj.attributes.target_temp_low),
|
||||||
String(stateObj.attributes.target_temp_high),
|
String(stateObj.attributes.target_temp_high),
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
sliderValue = uiValue = stateObj.attributes.temperature;
|
sliderValue = uiValue = stateObj.attributes.temperature;
|
||||||
}
|
}
|
||||||
|
|
||||||
jQuery("#thermostat", this.shadowRoot).roundSlider({
|
jQuery("#thermostat", this.shadowRoot).roundSlider({
|
||||||
value: sliderValue,
|
value: sliderValue,
|
||||||
});
|
});
|
||||||
this.shadowRoot!.querySelector("#set-temperature")!.innerHTML = uiValue;
|
this.shadowRoot!.querySelector("#set-temperature")!.innerHTML = uiValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderStyle(): TemplateResult {
|
private renderStyle(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
${roundSliderStyle}
|
${roundSliderStyle}
|
||||||
<style>
|
<style>
|
||||||
:host {
|
:host {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
ha-card {
|
ha-card {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
--rail-border-color: transparent;
|
--rail-border-color: transparent;
|
||||||
--auto-color: green;
|
--auto-color: green;
|
||||||
--cool-color: #2b9af9;
|
--cool-color: #2b9af9;
|
||||||
--heat-color: #FF8100;
|
--heat-color: #FF8100;
|
||||||
--off-color: #8a8a8a;
|
--off-color: #8a8a8a;
|
||||||
--unknown-color: #bac;
|
--unknown-color: #bac;
|
||||||
}
|
}
|
||||||
#root {
|
#root {
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.auto {
|
.auto {
|
||||||
--mode-color: var(--auto-color);
|
--mode-color: var(--auto-color);
|
||||||
}
|
}
|
||||||
.cool {
|
.cool {
|
||||||
--mode-color: var(--cool-color);
|
--mode-color: var(--cool-color);
|
||||||
}
|
}
|
||||||
.heat {
|
.heat {
|
||||||
--mode-color: var(--heat-color);
|
--mode-color: var(--heat-color);
|
||||||
}
|
}
|
||||||
.off {
|
.off {
|
||||||
--mode-color: var(--off-color);
|
--mode-color: var(--off-color);
|
||||||
}
|
}
|
||||||
.unknown-mode {
|
.unknown-mode {
|
||||||
--mode-color: var(--unknown-color);
|
--mode-color: var(--unknown-color);
|
||||||
}
|
}
|
||||||
.no-title {
|
.no-title {
|
||||||
--title-margin-top: 33% !important;
|
--title-margin-top: 33% !important;
|
||||||
}
|
}
|
||||||
.large {
|
.large {
|
||||||
--thermostat-padding-top: 25px;
|
--thermostat-padding-top: 25px;
|
||||||
--thermostat-margin-bottom: 25px;
|
--thermostat-margin-bottom: 25px;
|
||||||
--title-font-size: 28px;
|
--title-font-size: 28px;
|
||||||
--title-margin-top: 20%;
|
--title-margin-top: 20%;
|
||||||
--climate-info-margin-top: 17%;
|
--climate-info-margin-top: 17%;
|
||||||
--modes-margin-top: 2%;
|
--modes-margin-top: 2%;
|
||||||
--set-temperature-font-size: 25px;
|
--set-temperature-font-size: 25px;
|
||||||
--current-temperature-font-size: 71px;
|
--current-temperature-font-size: 71px;
|
||||||
--current-temperature-margin-top: 10%;
|
--current-temperature-margin-top: 10%;
|
||||||
--current-temperature-text-padding-left: 15px;
|
--current-temperature-text-padding-left: 15px;
|
||||||
--uom-font-size: 20px;
|
--uom-font-size: 20px;
|
||||||
--uom-margin-left: -18px;
|
--uom-margin-left: -18px;
|
||||||
--current-mode-font-size: 18px;
|
--current-mode-font-size: 18px;
|
||||||
--set-temperature-padding-bottom: 5px;
|
--set-temperature-padding-bottom: 5px;
|
||||||
}
|
}
|
||||||
.small {
|
.small {
|
||||||
--thermostat-padding-top: 15px;
|
--thermostat-padding-top: 15px;
|
||||||
--thermostat-margin-bottom: 15px;
|
--thermostat-margin-bottom: 15px;
|
||||||
--title-font-size: 18px;
|
--title-font-size: 18px;
|
||||||
--title-margin-top: 20%;
|
--title-margin-top: 20%;
|
||||||
--climate-info-margin-top: 7.5%;
|
--climate-info-margin-top: 7.5%;
|
||||||
--modes-margin-top: 1%;
|
--modes-margin-top: 1%;
|
||||||
--set-temperature-font-size: 16px;
|
--set-temperature-font-size: 16px;
|
||||||
--current-temperature-font-size: 25px;
|
--current-temperature-font-size: 25px;
|
||||||
--current-temperature-margin-top: 5%;
|
--current-temperature-margin-top: 5%;
|
||||||
--current-temperature-text-padding-left: 7px;
|
--current-temperature-text-padding-left: 7px;
|
||||||
--uom-font-size: 12px;
|
--uom-font-size: 12px;
|
||||||
--uom-margin-left: -5px;
|
--uom-margin-left: -5px;
|
||||||
--current-mode-font-size: 14px;
|
--current-mode-font-size: 14px;
|
||||||
--set-temperature-padding-bottom: 0px;
|
--set-temperature-padding-bottom: 0px;
|
||||||
}
|
}
|
||||||
#thermostat {
|
#thermostat {
|
||||||
margin: 0 auto var(--thermostat-margin-bottom);
|
margin: 0 auto var(--thermostat-margin-bottom);
|
||||||
padding-top: var(--thermostat-padding-top);
|
padding-top: var(--thermostat-padding-top);
|
||||||
}
|
}
|
||||||
#thermostat .rs-range-color {
|
#thermostat .rs-range-color {
|
||||||
background-color: var(--mode-color, var(--disabled-text-color));
|
background-color: var(--mode-color, var(--disabled-text-color));
|
||||||
}
|
}
|
||||||
#thermostat .rs-path-color {
|
#thermostat .rs-path-color {
|
||||||
background-color: var(--disabled-text-color);
|
background-color: var(--disabled-text-color);
|
||||||
}
|
}
|
||||||
#thermostat .rs-handle {
|
#thermostat .rs-handle {
|
||||||
background-color: var(--paper-card-background-color, white);
|
background-color: var(--paper-card-background-color, white);
|
||||||
padding: 7px;
|
padding: 7px;
|
||||||
border: 2px solid var(--disabled-text-color);
|
border: 2px solid var(--disabled-text-color);
|
||||||
}
|
}
|
||||||
#thermostat .rs-handle.rs-focus {
|
#thermostat .rs-handle.rs-focus {
|
||||||
border-color: var(--mode-color, var(--disabled-text-color));
|
border-color: var(--mode-color, var(--disabled-text-color));
|
||||||
}
|
}
|
||||||
#thermostat .rs-handle:after {
|
#thermostat .rs-handle:after {
|
||||||
border-color: var(--mode-color, var(--disabled-text-color));
|
border-color: var(--mode-color, var(--disabled-text-color));
|
||||||
background-color: var(--mode-color, var(--disabled-text-color));
|
background-color: var(--mode-color, var(--disabled-text-color));
|
||||||
}
|
}
|
||||||
#thermostat .rs-border {
|
#thermostat .rs-border {
|
||||||
border-color: var(--rail-border-color);
|
border-color: var(--rail-border-color);
|
||||||
}
|
}
|
||||||
#thermostat .rs-bar.rs-transition.rs-first, .rs-bar.rs-transition.rs-second{
|
#thermostat .rs-bar.rs-transition.rs-first, .rs-bar.rs-transition.rs-second{
|
||||||
z-index: 20 !important;
|
z-index: 20 !important;
|
||||||
}
|
}
|
||||||
#thermostat .rs-inner.rs-bg-color.rs-border,
|
#thermostat .rs-inner.rs-bg-color.rs-border,
|
||||||
#thermostat .rs-overlay.rs-transition.rs-bg-color {
|
#thermostat .rs-overlay.rs-transition.rs-bg-color {
|
||||||
background-color: var(--paper-card-background-color, white);
|
background-color: var(--paper-card-background-color, white);
|
||||||
}
|
}
|
||||||
#tooltip {
|
#tooltip {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
z-index: 15;
|
z-index: 15;
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
}
|
}
|
||||||
#set-temperature {
|
#set-temperature {
|
||||||
font-size: var(--set-temperature-font-size);
|
font-size: var(--set-temperature-font-size);
|
||||||
padding-bottom: var(--set-temperature-padding-bottom);
|
padding-bottom: var(--set-temperature-padding-bottom);
|
||||||
}
|
}
|
||||||
.title {
|
.title {
|
||||||
font-size: var(--title-font-size);
|
font-size: var(--title-font-size);
|
||||||
margin-top: var(--title-margin-top);
|
margin-top: var(--title-margin-top);
|
||||||
}
|
}
|
||||||
.climate-info {
|
.climate-info {
|
||||||
margin-top: var(--climate-info-margin-top);
|
margin-top: var(--climate-info-margin-top);
|
||||||
}
|
}
|
||||||
.current-mode {
|
.current-mode {
|
||||||
font-size: var(--current-mode-font-size);
|
font-size: var(--current-mode-font-size);
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
.modes {
|
.modes {
|
||||||
margin-top: var(--modes-margin-top);
|
margin-top: var(--modes-margin-top);
|
||||||
}
|
}
|
||||||
.modes ha-icon {
|
.modes ha-icon {
|
||||||
color: var(--disabled-text-color);
|
color: var(--disabled-text-color);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 0 10px;
|
margin: 0 10px;
|
||||||
}
|
}
|
||||||
.modes ha-icon.selected-icon {
|
.modes ha-icon.selected-icon {
|
||||||
color: var(--mode-color);
|
color: var(--mode-color);
|
||||||
}
|
}
|
||||||
.current-temperature {
|
.current-temperature {
|
||||||
margin-top: var(--current-temperature-margin-top);
|
margin-top: var(--current-temperature-margin-top);
|
||||||
font-size: var(--current-temperature-font-size);
|
font-size: var(--current-temperature-font-size);
|
||||||
}
|
}
|
||||||
.current-temperature-text {
|
.current-temperature-text {
|
||||||
padding-left: var(--current-temperature-text-padding-left);
|
padding-left: var(--current-temperature-text-padding-left);
|
||||||
}
|
}
|
||||||
.uom {
|
.uom {
|
||||||
font-size: var(--uom-font-size);
|
font-size: var(--uom-font-size);
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
margin-left: var(--uom-margin-left);
|
margin-left: var(--uom-margin-left);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _dragEvent(e): void {
|
private _dragEvent(e): void {
|
||||||
this.shadowRoot!.querySelector("#set-temperature")!.innerHTML = formatTemp(
|
this.shadowRoot!.querySelector("#set-temperature")!.innerHTML = formatTemp(
|
||||||
String(e.value).split(",")
|
String(e.value).split(",")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setTemperature(e): void {
|
private _setTemperature(e): void {
|
||||||
const stateObj = this.hass!.states[this._config!.entity] as ClimateEntity;
|
const stateObj = this.hass!.states[this._config!.entity] as ClimateEntity;
|
||||||
if (
|
if (
|
||||||
stateObj.attributes.target_temp_low &&
|
stateObj.attributes.target_temp_low &&
|
||||||
stateObj.attributes.target_temp_high
|
stateObj.attributes.target_temp_high
|
||||||
) {
|
) {
|
||||||
if (e.handle.index === 1) {
|
if (e.handle.index === 1) {
|
||||||
this.hass!.callService("climate", "set_temperature", {
|
this.hass!.callService("climate", "set_temperature", {
|
||||||
entity_id: this._config!.entity,
|
entity_id: this._config!.entity,
|
||||||
target_temp_low: e.handle.value,
|
target_temp_low: e.handle.value,
|
||||||
target_temp_high: stateObj.attributes.target_temp_high,
|
target_temp_high: stateObj.attributes.target_temp_high,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.hass!.callService("climate", "set_temperature", {
|
this.hass!.callService("climate", "set_temperature", {
|
||||||
entity_id: this._config!.entity,
|
entity_id: this._config!.entity,
|
||||||
target_temp_low: stateObj.attributes.target_temp_low,
|
target_temp_low: stateObj.attributes.target_temp_low,
|
||||||
target_temp_high: e.handle.value,
|
target_temp_high: e.handle.value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.hass!.callService("climate", "set_temperature", {
|
this.hass!.callService("climate", "set_temperature", {
|
||||||
entity_id: this._config!.entity,
|
entity_id: this._config!.entity,
|
||||||
temperature: e.value,
|
temperature: e.value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _renderIcon(mode: string, currentMode: string): TemplateResult {
|
private _renderIcon(mode: string, currentMode: string): TemplateResult {
|
||||||
if (!modeIcons[mode]) {
|
if (!modeIcons[mode]) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
return html`<ha-icon
|
return html`<ha-icon
|
||||||
class="${classMap({ "selected-icon": currentMode === mode })}"
|
class="${classMap({ "selected-icon": currentMode === mode })}"
|
||||||
.mode="${mode}"
|
.mode="${mode}"
|
||||||
.icon="${modeIcons[mode]}"
|
.icon="${modeIcons[mode]}"
|
||||||
@click="${this._handleModeClick}"
|
@click="${this._handleModeClick}"
|
||||||
></ha-icon>`;
|
></ha-icon>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleModeClick(e: MouseEvent): void {
|
private _handleModeClick(e: MouseEvent): void {
|
||||||
this.hass!.callService("climate", "set_operation_mode", {
|
this.hass!.callService("climate", "set_operation_mode", {
|
||||||
entity_id: this._config!.entity,
|
entity_id: this._config!.entity,
|
||||||
operation_mode: (e.currentTarget as any).mode,
|
operation_mode: (e.currentTarget as any).mode,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-thermostat-card": HuiThermostatCard;
|
"hui-thermostat-card": HuiThermostatCard;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-thermostat-card", HuiThermostatCard);
|
customElements.define("hui-thermostat-card", HuiThermostatCard);
|
||||||
|
|||||||
@@ -1,50 +1,50 @@
|
|||||||
import { html } from "@polymer/lit-element";
|
import { html } from "@polymer/lit-element";
|
||||||
|
|
||||||
import computeCardSize from "../common/compute-card-size";
|
import computeCardSize from "../common/compute-card-size";
|
||||||
|
|
||||||
import { HuiStackCard } from "./hui-stack-card";
|
import { HuiStackCard } from "./hui-stack-card";
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
class HuiVerticalStackCard extends HuiStackCard {
|
class HuiVerticalStackCard extends HuiStackCard {
|
||||||
public getCardSize() {
|
public getCardSize() {
|
||||||
let totalSize = 0;
|
let totalSize = 0;
|
||||||
|
|
||||||
if (!this._cards) {
|
if (!this._cards) {
|
||||||
return totalSize;
|
return totalSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const element of this._cards) {
|
for (const element of this._cards) {
|
||||||
totalSize += computeCardSize(element);
|
totalSize += computeCardSize(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
return totalSize;
|
return totalSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected renderStyle(): TemplateResult {
|
protected renderStyle(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
#root {
|
#root {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
#root > * {
|
#root > * {
|
||||||
margin: 4px 0 4px 0;
|
margin: 4px 0 4px 0;
|
||||||
}
|
}
|
||||||
#root > *:first-child {
|
#root > *:first-child {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
#root > *:last-child {
|
#root > *:last-child {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-vertical-stack-card": HuiVerticalStackCard;
|
"hui-vertical-stack-card": HuiVerticalStackCard;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-vertical-stack-card", HuiVerticalStackCard);
|
customElements.define("hui-vertical-stack-card", HuiVerticalStackCard);
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import "../../../cards/ha-camera-card";
|
import "../../../cards/ha-camera-card";
|
||||||
|
|
||||||
import LegacyWrapperCard from "./hui-legacy-wrapper-card";
|
import LegacyWrapperCard from "./hui-legacy-wrapper-card";
|
||||||
|
|
||||||
class HuiWeatherForecastCard extends LegacyWrapperCard {
|
class HuiWeatherForecastCard extends LegacyWrapperCard {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("ha-weather-card", "weather");
|
super("ha-weather-card", "weather");
|
||||||
}
|
}
|
||||||
|
|
||||||
getCardSize() {
|
getCardSize() {
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-weather-forecast-card", HuiWeatherForecastCard);
|
customElements.define("hui-weather-forecast-card", HuiWeatherForecastCard);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import computeDomain from "../../../common/entity/compute_domain";
|
import computeDomain from "../../../common/entity/compute_domain";
|
||||||
|
|
||||||
export default function computeNotifications(states) {
|
export default function computeNotifications(states) {
|
||||||
return Object.keys(states)
|
return Object.keys(states)
|
||||||
.filter((entityId) => computeDomain(entityId) === "configurator")
|
.filter((entityId) => computeDomain(entityId) === "configurator")
|
||||||
.map((entityId) => states[entityId]);
|
.map((entityId) => states[entityId]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +1,38 @@
|
|||||||
import computeStateName from "../../../common/entity/compute_state_name";
|
import computeStateName from "../../../common/entity/compute_state_name";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { LovelaceElementConfig } from "../elements/types";
|
import { LovelaceElementConfig } from "../elements/types";
|
||||||
|
|
||||||
export const computeTooltip = (
|
export const computeTooltip = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config: LovelaceElementConfig
|
config: LovelaceElementConfig
|
||||||
): string => {
|
): string => {
|
||||||
if (config.title) {
|
if (config.title) {
|
||||||
return config.title;
|
return config.title;
|
||||||
}
|
}
|
||||||
|
|
||||||
let stateName = "";
|
let stateName = "";
|
||||||
let tooltip: string;
|
let tooltip: string;
|
||||||
|
|
||||||
if (config.entity) {
|
if (config.entity) {
|
||||||
stateName =
|
stateName =
|
||||||
config.entity in hass.states
|
config.entity in hass.states
|
||||||
? computeStateName(hass.states[config.entity])
|
? computeStateName(hass.states[config.entity])
|
||||||
: config.entity;
|
: config.entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (config.tap_action) {
|
switch (config.tap_action) {
|
||||||
case "navigate":
|
case "navigate":
|
||||||
tooltip = `Navigate to ${config.navigation_path}`;
|
tooltip = `Navigate to ${config.navigation_path}`;
|
||||||
break;
|
break;
|
||||||
case "toggle":
|
case "toggle":
|
||||||
tooltip = `Toggle ${stateName}`;
|
tooltip = `Toggle ${stateName}`;
|
||||||
break;
|
break;
|
||||||
case "call-service":
|
case "call-service":
|
||||||
tooltip = `Call service ${config.service}`;
|
tooltip = `Call service ${config.service}`;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
tooltip = `Show more-info: ${stateName}`;
|
tooltip = `Show more-info: ${stateName}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return tooltip;
|
return tooltip;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,112 +1,112 @@
|
|||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
|
|
||||||
import "../cards/hui-alarm-panel-card";
|
import "../cards/hui-alarm-panel-card";
|
||||||
import "../cards/hui-conditional-card.ts";
|
import "../cards/hui-conditional-card.ts";
|
||||||
import "../cards/hui-entities-card.ts";
|
import "../cards/hui-entities-card.ts";
|
||||||
import "../cards/hui-entity-button-card.ts";
|
import "../cards/hui-entity-button-card.ts";
|
||||||
import "../cards/hui-entity-filter-card";
|
import "../cards/hui-entity-filter-card";
|
||||||
import "../cards/hui-error-card.ts";
|
import "../cards/hui-error-card.ts";
|
||||||
import "../cards/hui-glance-card.ts";
|
import "../cards/hui-glance-card.ts";
|
||||||
import "../cards/hui-history-graph-card";
|
import "../cards/hui-history-graph-card";
|
||||||
import "../cards/hui-horizontal-stack-card.ts";
|
import "../cards/hui-horizontal-stack-card.ts";
|
||||||
import "../cards/hui-iframe-card.ts";
|
import "../cards/hui-iframe-card.ts";
|
||||||
import "../cards/hui-light-card";
|
import "../cards/hui-light-card";
|
||||||
import "../cards/hui-map-card";
|
import "../cards/hui-map-card";
|
||||||
import "../cards/hui-markdown-card.ts";
|
import "../cards/hui-markdown-card.ts";
|
||||||
import "../cards/hui-media-control-card";
|
import "../cards/hui-media-control-card";
|
||||||
import "../cards/hui-picture-card";
|
import "../cards/hui-picture-card";
|
||||||
import "../cards/hui-picture-elements-card";
|
import "../cards/hui-picture-elements-card";
|
||||||
import "../cards/hui-picture-entity-card";
|
import "../cards/hui-picture-entity-card";
|
||||||
import "../cards/hui-picture-glance-card";
|
import "../cards/hui-picture-glance-card";
|
||||||
import "../cards/hui-plant-status-card";
|
import "../cards/hui-plant-status-card";
|
||||||
import "../cards/hui-sensor-card";
|
import "../cards/hui-sensor-card";
|
||||||
import "../cards/hui-vertical-stack-card.ts";
|
import "../cards/hui-vertical-stack-card.ts";
|
||||||
import "../cards/hui-thermostat-card.ts";
|
import "../cards/hui-thermostat-card.ts";
|
||||||
import "../cards/hui-weather-forecast-card";
|
import "../cards/hui-weather-forecast-card";
|
||||||
import "../cards/hui-gauge-card";
|
import "../cards/hui-gauge-card";
|
||||||
|
|
||||||
import createErrorCardConfig from "./create-error-card-config";
|
import createErrorCardConfig from "./create-error-card-config";
|
||||||
|
|
||||||
const CARD_TYPES = new Set([
|
const CARD_TYPES = new Set([
|
||||||
"alarm-panel",
|
"alarm-panel",
|
||||||
"conditional",
|
"conditional",
|
||||||
"entities",
|
"entities",
|
||||||
"entity-button",
|
"entity-button",
|
||||||
"entity-filter",
|
"entity-filter",
|
||||||
"error",
|
"error",
|
||||||
"gauge",
|
"gauge",
|
||||||
"glance",
|
"glance",
|
||||||
"history-graph",
|
"history-graph",
|
||||||
"horizontal-stack",
|
"horizontal-stack",
|
||||||
"iframe",
|
"iframe",
|
||||||
"light",
|
"light",
|
||||||
"map",
|
"map",
|
||||||
"markdown",
|
"markdown",
|
||||||
"media-control",
|
"media-control",
|
||||||
"picture",
|
"picture",
|
||||||
"picture-elements",
|
"picture-elements",
|
||||||
"picture-entity",
|
"picture-entity",
|
||||||
"picture-glance",
|
"picture-glance",
|
||||||
"plant-status",
|
"plant-status",
|
||||||
"sensor",
|
"sensor",
|
||||||
"thermostat",
|
"thermostat",
|
||||||
"vertical-stack",
|
"vertical-stack",
|
||||||
"weather-forecast",
|
"weather-forecast",
|
||||||
]);
|
]);
|
||||||
const CUSTOM_TYPE_PREFIX = "custom:";
|
const CUSTOM_TYPE_PREFIX = "custom:";
|
||||||
const TIMEOUT = 2000;
|
const TIMEOUT = 2000;
|
||||||
|
|
||||||
function _createElement(tag, config) {
|
function _createElement(tag, config) {
|
||||||
const element = document.createElement(tag);
|
const element = document.createElement(tag);
|
||||||
try {
|
try {
|
||||||
element.setConfig(config);
|
element.setConfig(config);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
console.error(tag, err);
|
console.error(tag, err);
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
return _createErrorElement(err.message, config);
|
return _createErrorElement(err.message, config);
|
||||||
}
|
}
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _createErrorElement(error, config) {
|
function _createErrorElement(error, config) {
|
||||||
return _createElement("hui-error-card", createErrorCardConfig(error, config));
|
return _createElement("hui-error-card", createErrorCardConfig(error, config));
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function createCardElement(config) {
|
export default function createCardElement(config) {
|
||||||
if (!config || typeof config !== "object" || !config.type) {
|
if (!config || typeof config !== "object" || !config.type) {
|
||||||
return _createErrorElement("No card type configured.", config);
|
return _createErrorElement("No card type configured.", config);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.type.startsWith(CUSTOM_TYPE_PREFIX)) {
|
if (config.type.startsWith(CUSTOM_TYPE_PREFIX)) {
|
||||||
const tag = config.type.substr(CUSTOM_TYPE_PREFIX.length);
|
const tag = config.type.substr(CUSTOM_TYPE_PREFIX.length);
|
||||||
|
|
||||||
if (customElements.get(tag)) {
|
if (customElements.get(tag)) {
|
||||||
return _createElement(tag, config);
|
return _createElement(tag, config);
|
||||||
}
|
}
|
||||||
const element = _createErrorElement(
|
const element = _createErrorElement(
|
||||||
`Custom element doesn't exist: ${tag}.`,
|
`Custom element doesn't exist: ${tag}.`,
|
||||||
config
|
config
|
||||||
);
|
);
|
||||||
element.style.display = "None";
|
element.style.display = "None";
|
||||||
const timer = window.setTimeout(() => {
|
const timer = window.setTimeout(() => {
|
||||||
element.style.display = "";
|
element.style.display = "";
|
||||||
}, TIMEOUT);
|
}, TIMEOUT);
|
||||||
|
|
||||||
customElements.whenDefined(tag).then(() => {
|
customElements.whenDefined(tag).then(() => {
|
||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
fireEvent(element, "rebuild-view");
|
fireEvent(element, "rebuild-view");
|
||||||
});
|
});
|
||||||
|
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CARD_TYPES.has(config.type)) {
|
if (!CARD_TYPES.has(config.type)) {
|
||||||
return _createErrorElement(
|
return _createErrorElement(
|
||||||
`Unknown card type encountered: ${config.type}.`,
|
`Unknown card type encountered: ${config.type}.`,
|
||||||
config
|
config
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _createElement(`hui-${config.type}-card`, config);
|
return _createElement(`hui-${config.type}-card`, config);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,79 +1,79 @@
|
|||||||
import "../elements/hui-icon-element";
|
import "../elements/hui-icon-element";
|
||||||
import "../elements/hui-image-element";
|
import "../elements/hui-image-element";
|
||||||
import "../elements/hui-service-button-element";
|
import "../elements/hui-service-button-element";
|
||||||
import "../elements/hui-state-badge-element";
|
import "../elements/hui-state-badge-element";
|
||||||
import "../elements/hui-state-icon-element";
|
import "../elements/hui-state-icon-element";
|
||||||
import "../elements/hui-state-label-element";
|
import "../elements/hui-state-label-element";
|
||||||
|
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import createErrorCardConfig from "./create-error-card-config";
|
import createErrorCardConfig from "./create-error-card-config";
|
||||||
|
|
||||||
const CUSTOM_TYPE_PREFIX = "custom:";
|
const CUSTOM_TYPE_PREFIX = "custom:";
|
||||||
const ELEMENT_TYPES = new Set([
|
const ELEMENT_TYPES = new Set([
|
||||||
"icon",
|
"icon",
|
||||||
"image",
|
"image",
|
||||||
"service-button",
|
"service-button",
|
||||||
"state-badge",
|
"state-badge",
|
||||||
"state-icon",
|
"state-icon",
|
||||||
"state-label",
|
"state-label",
|
||||||
]);
|
]);
|
||||||
const TIMEOUT = 2000;
|
const TIMEOUT = 2000;
|
||||||
|
|
||||||
function _createElement(tag, config) {
|
function _createElement(tag, config) {
|
||||||
const element = document.createElement(tag);
|
const element = document.createElement(tag);
|
||||||
try {
|
try {
|
||||||
element.setConfig(config);
|
element.setConfig(config);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
console.error(tag, err);
|
console.error(tag, err);
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
return _createErrorElement(err.message, config);
|
return _createErrorElement(err.message, config);
|
||||||
}
|
}
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _createErrorElement(error, config) {
|
function _createErrorElement(error, config) {
|
||||||
return _createElement("hui-error-card", createErrorCardConfig(error, config));
|
return _createElement("hui-error-card", createErrorCardConfig(error, config));
|
||||||
}
|
}
|
||||||
|
|
||||||
function _hideErrorElement(element) {
|
function _hideErrorElement(element) {
|
||||||
element.style.display = "None";
|
element.style.display = "None";
|
||||||
return window.setTimeout(() => {
|
return window.setTimeout(() => {
|
||||||
element.style.display = "";
|
element.style.display = "";
|
||||||
}, TIMEOUT);
|
}, TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function createHuiElement(config) {
|
export default function createHuiElement(config) {
|
||||||
if (!config || typeof config !== "object" || !config.type) {
|
if (!config || typeof config !== "object" || !config.type) {
|
||||||
return _createErrorElement("No element type configured.", config);
|
return _createErrorElement("No element type configured.", config);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.type.startsWith(CUSTOM_TYPE_PREFIX)) {
|
if (config.type.startsWith(CUSTOM_TYPE_PREFIX)) {
|
||||||
const tag = config.type.substr(CUSTOM_TYPE_PREFIX.length);
|
const tag = config.type.substr(CUSTOM_TYPE_PREFIX.length);
|
||||||
|
|
||||||
if (customElements.get(tag)) {
|
if (customElements.get(tag)) {
|
||||||
return _createElement(tag, config);
|
return _createElement(tag, config);
|
||||||
}
|
}
|
||||||
const element = _createErrorElement(
|
const element = _createErrorElement(
|
||||||
`Custom element doesn't exist: ${tag}.`,
|
`Custom element doesn't exist: ${tag}.`,
|
||||||
config
|
config
|
||||||
);
|
);
|
||||||
const timer = _hideErrorElement(element);
|
const timer = _hideErrorElement(element);
|
||||||
|
|
||||||
customElements.whenDefined(tag).then(() => {
|
customElements.whenDefined(tag).then(() => {
|
||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
fireEvent(element, "rebuild-view");
|
fireEvent(element, "rebuild-view");
|
||||||
});
|
});
|
||||||
|
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ELEMENT_TYPES.has(config.type)) {
|
if (!ELEMENT_TYPES.has(config.type)) {
|
||||||
return _createErrorElement(
|
return _createErrorElement(
|
||||||
`Unknown element type encountered: ${config.type}.`,
|
`Unknown element type encountered: ${config.type}.`,
|
||||||
config
|
config
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _createElement(`hui-${config.type}-element`, config);
|
return _createElement(`hui-${config.type}-element`, config);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,117 +1,117 @@
|
|||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
|
|
||||||
import "../entity-rows/hui-climate-entity-row";
|
import "../entity-rows/hui-climate-entity-row";
|
||||||
import "../entity-rows/hui-cover-entity-row";
|
import "../entity-rows/hui-cover-entity-row";
|
||||||
import "../entity-rows/hui-group-entity-row";
|
import "../entity-rows/hui-group-entity-row";
|
||||||
import "../entity-rows/hui-input-number-entity-row";
|
import "../entity-rows/hui-input-number-entity-row";
|
||||||
import "../entity-rows/hui-input-select-entity-row";
|
import "../entity-rows/hui-input-select-entity-row";
|
||||||
import "../entity-rows/hui-input-text-entity-row";
|
import "../entity-rows/hui-input-text-entity-row";
|
||||||
import "../entity-rows/hui-lock-entity-row";
|
import "../entity-rows/hui-lock-entity-row";
|
||||||
import "../entity-rows/hui-media-player-entity-row";
|
import "../entity-rows/hui-media-player-entity-row";
|
||||||
import "../entity-rows/hui-scene-entity-row";
|
import "../entity-rows/hui-scene-entity-row";
|
||||||
import "../entity-rows/hui-script-entity-row";
|
import "../entity-rows/hui-script-entity-row";
|
||||||
import "../entity-rows/hui-text-entity-row";
|
import "../entity-rows/hui-text-entity-row";
|
||||||
import "../entity-rows/hui-timer-entity-row";
|
import "../entity-rows/hui-timer-entity-row";
|
||||||
import "../entity-rows/hui-toggle-entity-row";
|
import "../entity-rows/hui-toggle-entity-row";
|
||||||
|
|
||||||
import "../special-rows/hui-call-service-row";
|
import "../special-rows/hui-call-service-row";
|
||||||
import "../special-rows/hui-divider-row";
|
import "../special-rows/hui-divider-row";
|
||||||
import "../special-rows/hui-section-row";
|
import "../special-rows/hui-section-row";
|
||||||
import "../special-rows/hui-weblink-row";
|
import "../special-rows/hui-weblink-row";
|
||||||
|
|
||||||
import createErrorCardConfig from "./create-error-card-config";
|
import createErrorCardConfig from "./create-error-card-config";
|
||||||
|
|
||||||
const CUSTOM_TYPE_PREFIX = "custom:";
|
const CUSTOM_TYPE_PREFIX = "custom:";
|
||||||
const SPECIAL_TYPES = new Set([
|
const SPECIAL_TYPES = new Set([
|
||||||
"call-service",
|
"call-service",
|
||||||
"divider",
|
"divider",
|
||||||
"section",
|
"section",
|
||||||
"weblink",
|
"weblink",
|
||||||
]);
|
]);
|
||||||
const DOMAIN_TO_ELEMENT_TYPE = {
|
const DOMAIN_TO_ELEMENT_TYPE = {
|
||||||
automation: "toggle",
|
automation: "toggle",
|
||||||
climate: "climate",
|
climate: "climate",
|
||||||
cover: "cover",
|
cover: "cover",
|
||||||
fan: "toggle",
|
fan: "toggle",
|
||||||
group: "group",
|
group: "group",
|
||||||
input_boolean: "toggle",
|
input_boolean: "toggle",
|
||||||
input_number: "input-number",
|
input_number: "input-number",
|
||||||
input_select: "input-select",
|
input_select: "input-select",
|
||||||
input_text: "input-text",
|
input_text: "input-text",
|
||||||
light: "toggle",
|
light: "toggle",
|
||||||
media_player: "media-player",
|
media_player: "media-player",
|
||||||
lock: "lock",
|
lock: "lock",
|
||||||
scene: "scene",
|
scene: "scene",
|
||||||
script: "script",
|
script: "script",
|
||||||
timer: "timer",
|
timer: "timer",
|
||||||
switch: "toggle",
|
switch: "toggle",
|
||||||
vacuum: "toggle",
|
vacuum: "toggle",
|
||||||
};
|
};
|
||||||
const TIMEOUT = 2000;
|
const TIMEOUT = 2000;
|
||||||
|
|
||||||
function _createElement(tag, config) {
|
function _createElement(tag, config) {
|
||||||
const element = document.createElement(tag);
|
const element = document.createElement(tag);
|
||||||
try {
|
try {
|
||||||
if ("setConfig" in element) element.setConfig(config);
|
if ("setConfig" in element) element.setConfig(config);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
console.error(tag, err);
|
console.error(tag, err);
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
return _createErrorElement(err.message, config);
|
return _createErrorElement(err.message, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _createErrorElement(error, config) {
|
function _createErrorElement(error, config) {
|
||||||
return _createElement("hui-error-card", createErrorCardConfig(error, config));
|
return _createElement("hui-error-card", createErrorCardConfig(error, config));
|
||||||
}
|
}
|
||||||
|
|
||||||
function _hideErrorElement(element) {
|
function _hideErrorElement(element) {
|
||||||
element.style.display = "None";
|
element.style.display = "None";
|
||||||
return window.setTimeout(() => {
|
return window.setTimeout(() => {
|
||||||
element.style.display = "";
|
element.style.display = "";
|
||||||
}, TIMEOUT);
|
}, TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function createRowElement(config) {
|
export default function createRowElement(config) {
|
||||||
let tag;
|
let tag;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!config ||
|
!config ||
|
||||||
typeof config !== "object" ||
|
typeof config !== "object" ||
|
||||||
(!config.entity && !config.type)
|
(!config.entity && !config.type)
|
||||||
) {
|
) {
|
||||||
return _createErrorElement("Invalid config given.", config);
|
return _createErrorElement("Invalid config given.", config);
|
||||||
}
|
}
|
||||||
|
|
||||||
const type = config.type || "default";
|
const type = config.type || "default";
|
||||||
if (SPECIAL_TYPES.has(type)) {
|
if (SPECIAL_TYPES.has(type)) {
|
||||||
return _createElement(`hui-${type}-row`, config);
|
return _createElement(`hui-${type}-row`, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type.startsWith(CUSTOM_TYPE_PREFIX)) {
|
if (type.startsWith(CUSTOM_TYPE_PREFIX)) {
|
||||||
tag = type.substr(CUSTOM_TYPE_PREFIX.length);
|
tag = type.substr(CUSTOM_TYPE_PREFIX.length);
|
||||||
|
|
||||||
if (customElements.get(tag)) {
|
if (customElements.get(tag)) {
|
||||||
return _createElement(tag, config);
|
return _createElement(tag, config);
|
||||||
}
|
}
|
||||||
const element = _createErrorElement(
|
const element = _createErrorElement(
|
||||||
`Custom element doesn't exist: ${tag}.`,
|
`Custom element doesn't exist: ${tag}.`,
|
||||||
config
|
config
|
||||||
);
|
);
|
||||||
const timer = _hideErrorElement(element);
|
const timer = _hideErrorElement(element);
|
||||||
|
|
||||||
customElements.whenDefined(tag).then(() => {
|
customElements.whenDefined(tag).then(() => {
|
||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
fireEvent(element, "rebuild-view");
|
fireEvent(element, "rebuild-view");
|
||||||
});
|
});
|
||||||
|
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
const domain = config.entity.split(".", 1)[0];
|
const domain = config.entity.split(".", 1)[0];
|
||||||
tag = `hui-${DOMAIN_TO_ELEMENT_TYPE[domain] || "text"}-entity-row`;
|
tag = `hui-${DOMAIN_TO_ELEMENT_TYPE[domain] || "text"}-entity-row`;
|
||||||
|
|
||||||
return _createElement(tag, config);
|
return _createElement(tag, config);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { STATES_OFF } from "../../../../common/const";
|
import { STATES_OFF } from "../../../../common/const";
|
||||||
import turnOnOffEntity from "./turn-on-off-entity";
|
import turnOnOffEntity from "./turn-on-off-entity";
|
||||||
|
|
||||||
export default function toggleEntity(hass, entityId) {
|
export default function toggleEntity(hass, entityId) {
|
||||||
const turnOn = STATES_OFF.includes(hass.states[entityId].state);
|
const turnOn = STATES_OFF.includes(hass.states[entityId].state);
|
||||||
turnOnOffEntity(hass, entityId, turnOn);
|
turnOnOffEntity(hass, entityId, turnOn);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,34 @@
|
|||||||
import { STATES_OFF } from "../../../../common/const";
|
import { STATES_OFF } from "../../../../common/const";
|
||||||
import computeDomain from "../../../../common/entity/compute_domain";
|
import computeDomain from "../../../../common/entity/compute_domain";
|
||||||
|
|
||||||
export default function turnOnOffEntities(hass, entityIds, turnOn = true) {
|
export default function turnOnOffEntities(hass, entityIds, turnOn = true) {
|
||||||
const domainsToCall = {};
|
const domainsToCall = {};
|
||||||
entityIds.forEach((entityId) => {
|
entityIds.forEach((entityId) => {
|
||||||
if (STATES_OFF.includes(hass.states[entityId].state) === turnOn) {
|
if (STATES_OFF.includes(hass.states[entityId].state) === turnOn) {
|
||||||
const stateDomain = computeDomain(entityId);
|
const stateDomain = computeDomain(entityId);
|
||||||
const serviceDomain = ["cover", "lock"].includes(stateDomain)
|
const serviceDomain = ["cover", "lock"].includes(stateDomain)
|
||||||
? stateDomain
|
? stateDomain
|
||||||
: "homeassistant";
|
: "homeassistant";
|
||||||
|
|
||||||
if (!(serviceDomain in domainsToCall)) domainsToCall[serviceDomain] = [];
|
if (!(serviceDomain in domainsToCall)) domainsToCall[serviceDomain] = [];
|
||||||
domainsToCall[serviceDomain].push(entityId);
|
domainsToCall[serviceDomain].push(entityId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.keys(domainsToCall).forEach((domain) => {
|
Object.keys(domainsToCall).forEach((domain) => {
|
||||||
let service;
|
let service;
|
||||||
switch (domain) {
|
switch (domain) {
|
||||||
case "lock":
|
case "lock":
|
||||||
service = turnOn ? "unlock" : "lock";
|
service = turnOn ? "unlock" : "lock";
|
||||||
break;
|
break;
|
||||||
case "cover":
|
case "cover":
|
||||||
service = turnOn ? "open_cover" : "close_cover";
|
service = turnOn ? "open_cover" : "close_cover";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
service = turnOn ? "turn_on" : "turn_off";
|
service = turnOn ? "turn_on" : "turn_off";
|
||||||
}
|
}
|
||||||
|
|
||||||
const entities = domainsToCall[domain];
|
const entities = domainsToCall[domain];
|
||||||
hass.callService(domain, service, { entity_id: entities });
|
hass.callService(domain, service, { entity_id: entities });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
import computeDomain from "../../../../common/entity/compute_domain";
|
import computeDomain from "../../../../common/entity/compute_domain";
|
||||||
|
|
||||||
export default function turnOnOffEntity(hass, entityId, turnOn = true) {
|
export default function turnOnOffEntity(hass, entityId, turnOn = true) {
|
||||||
const stateDomain = computeDomain(entityId);
|
const stateDomain = computeDomain(entityId);
|
||||||
const serviceDomain = stateDomain === "group" ? "homeassistant" : stateDomain;
|
const serviceDomain = stateDomain === "group" ? "homeassistant" : stateDomain;
|
||||||
|
|
||||||
let service;
|
let service;
|
||||||
switch (stateDomain) {
|
switch (stateDomain) {
|
||||||
case "lock":
|
case "lock":
|
||||||
service = turnOn ? "unlock" : "lock";
|
service = turnOn ? "unlock" : "lock";
|
||||||
break;
|
break;
|
||||||
case "cover":
|
case "cover":
|
||||||
service = turnOn ? "open_cover" : "close_cover";
|
service = turnOn ? "open_cover" : "close_cover";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
service = turnOn ? "turn_on" : "turn_off";
|
service = turnOn ? "turn_on" : "turn_off";
|
||||||
}
|
}
|
||||||
|
|
||||||
hass.callService(serviceDomain, service, { entity_id: entityId });
|
hass.callService(serviceDomain, service, { entity_id: entityId });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,44 @@
|
|||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { LovelaceElementConfig } from "../elements/types";
|
import { LovelaceElementConfig } from "../elements/types";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { navigate } from "../../../common/navigate";
|
import { navigate } from "../../../common/navigate";
|
||||||
import toggleEntity from "../../../../src/panels/lovelace/common/entity/toggle-entity";
|
import toggleEntity from "../../../../src/panels/lovelace/common/entity/toggle-entity";
|
||||||
|
|
||||||
export const handleClick = (
|
export const handleClick = (
|
||||||
node: HTMLElement,
|
node: HTMLElement,
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config: LovelaceElementConfig,
|
config: LovelaceElementConfig,
|
||||||
hold: boolean
|
hold: boolean
|
||||||
): void => {
|
): void => {
|
||||||
let action = config.tap_action || "more-info";
|
let action = config.tap_action || "more-info";
|
||||||
|
|
||||||
if (hold && config.hold_action) {
|
if (hold && config.hold_action) {
|
||||||
action = config.hold_action;
|
action = config.hold_action;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action === "none") {
|
if (action === "none") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case "more-info":
|
case "more-info":
|
||||||
fireEvent(node, "hass-more-info", { entityId: config.entity });
|
fireEvent(node, "hass-more-info", { entityId: config.entity });
|
||||||
break;
|
break;
|
||||||
case "navigate":
|
case "navigate":
|
||||||
navigate(node, config.navigation_path ? config.navigation_path : "");
|
navigate(node, config.navigation_path ? config.navigation_path : "");
|
||||||
break;
|
break;
|
||||||
case "toggle":
|
case "toggle":
|
||||||
toggleEntity(hass, config.entity);
|
toggleEntity(hass, config.entity);
|
||||||
break;
|
break;
|
||||||
case "call-service": {
|
case "call-service": {
|
||||||
if (config.service) {
|
if (config.service) {
|
||||||
const [domain, service] = config.service.split(".", 2);
|
const [domain, service] = config.service.split(".", 2);
|
||||||
const serviceData = {
|
const serviceData = {
|
||||||
entity_id: config.entity,
|
entity_id: config.entity,
|
||||||
...config.service_data,
|
...config.service_data,
|
||||||
};
|
};
|
||||||
hass.callService(domain, service, serviceData);
|
hass.callService(domain, service, serviceData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,38 +1,38 @@
|
|||||||
// Parse array of entity objects from config
|
// Parse array of entity objects from config
|
||||||
import isValidEntityId from "../../../common/entity/valid_entity_id";
|
import isValidEntityId from "../../../common/entity/valid_entity_id";
|
||||||
|
|
||||||
export default function processConfigEntities(entities) {
|
export default function processConfigEntities(entities) {
|
||||||
if (!entities || !Array.isArray(entities)) {
|
if (!entities || !Array.isArray(entities)) {
|
||||||
throw new Error("Entities need to be an array");
|
throw new Error("Entities need to be an array");
|
||||||
}
|
}
|
||||||
|
|
||||||
return entities.map((entityConf, index) => {
|
return entities.map((entityConf, index) => {
|
||||||
if (
|
if (
|
||||||
typeof entityConf === "object" &&
|
typeof entityConf === "object" &&
|
||||||
!Array.isArray(entityConf) &&
|
!Array.isArray(entityConf) &&
|
||||||
entityConf.type
|
entityConf.type
|
||||||
) {
|
) {
|
||||||
return entityConf;
|
return entityConf;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof entityConf === "string") {
|
if (typeof entityConf === "string") {
|
||||||
entityConf = { entity: entityConf };
|
entityConf = { entity: entityConf };
|
||||||
} else if (typeof entityConf === "object" && !Array.isArray(entityConf)) {
|
} else if (typeof entityConf === "object" && !Array.isArray(entityConf)) {
|
||||||
if (!entityConf.entity) {
|
if (!entityConf.entity) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Entity object at position ${index} is missing entity field.`
|
`Entity object at position ${index} is missing entity field.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Invalid entity specified at position ${index}.`);
|
throw new Error(`Invalid entity specified at position ${index}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isValidEntityId(entityConf.entity)) {
|
if (!isValidEntityId(entityConf.entity)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Invalid entity ID at position ${index}: ${entityConf.entity}`
|
`Invalid entity ID at position ${index}: ${entityConf.entity}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return entityConf;
|
return entityConf;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +1,68 @@
|
|||||||
import "@polymer/paper-button/paper-button";
|
import "@polymer/paper-button/paper-button";
|
||||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
|
|
||||||
let registeredDialog = false;
|
let registeredDialog = false;
|
||||||
|
|
||||||
export class HuiCardOptions extends LitElement {
|
export class HuiCardOptions extends LitElement {
|
||||||
public cardId?: string;
|
public cardId?: string;
|
||||||
protected hass?: HomeAssistant;
|
protected hass?: HomeAssistant;
|
||||||
|
|
||||||
static get properties(): PropertyDeclarations {
|
static get properties(): PropertyDeclarations {
|
||||||
return {
|
return {
|
||||||
hass: {},
|
hass: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public connectedCallback() {
|
public connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
if (!registeredDialog) {
|
if (!registeredDialog) {
|
||||||
registeredDialog = true;
|
registeredDialog = true;
|
||||||
fireEvent(this, "register-dialog", {
|
fireEvent(this, "register-dialog", {
|
||||||
dialogShowEvent: "show-edit-card",
|
dialogShowEvent: "show-edit-card",
|
||||||
dialogTag: "hui-dialog-edit-card",
|
dialogTag: "hui-dialog-edit-card",
|
||||||
dialogImport: () => import("../editor/hui-dialog-edit-card"),
|
dialogImport: () => import("../editor/hui-dialog-edit-card"),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
div {
|
div {
|
||||||
border-top: 1px solid #e8e8e8;
|
border-top: 1px solid #e8e8e8;
|
||||||
padding: 5px 16px;
|
padding: 5px 16px;
|
||||||
background: var(--paper-card-background-color, white);
|
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.2) 0px 3px 1px -2px;
|
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.2) 0px 3px 1px -2px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
paper-button {
|
paper-button {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
<div>
|
<div>
|
||||||
<paper-button
|
<paper-button
|
||||||
@click="${this._editCard}"
|
@click="${this._editCard}"
|
||||||
>EDIT</paper-button>
|
>EDIT</paper-button>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
private _editCard() {
|
private _editCard() {
|
||||||
fireEvent(this, "show-edit-card", {
|
fireEvent(this, "show-edit-card", {
|
||||||
hass: this.hass,
|
hass: this.hass,
|
||||||
cardId: this.cardId,
|
cardId: this.cardId,
|
||||||
reloadLovelace: () => fireEvent(this, "config-refresh"),
|
reloadLovelace: () => fireEvent(this, "config-refresh"),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-card-options": HuiCardOptions;
|
"hui-card-options": HuiCardOptions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-card-options", HuiCardOptions);
|
customElements.define("hui-card-options", HuiCardOptions);
|
||||||
|
|||||||
@@ -1,57 +1,57 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
import "@polymer/paper-toggle-button/paper-toggle-button";
|
import "@polymer/paper-toggle-button/paper-toggle-button";
|
||||||
|
|
||||||
import { DOMAINS_TOGGLE } from "../../../common/const";
|
import { DOMAINS_TOGGLE } from "../../../common/const";
|
||||||
import turnOnOffEntities from "../common/entity/turn-on-off-entities";
|
import turnOnOffEntities from "../common/entity/turn-on-off-entities";
|
||||||
|
|
||||||
class HuiEntitiesToggle extends PolymerElement {
|
class HuiEntitiesToggle extends PolymerElement {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
:host {
|
:host {
|
||||||
width: 38px;
|
width: 38px;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
paper-toggle-button {
|
paper-toggle-button {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
--paper-toggle-button-label-spacing: 0;
|
--paper-toggle-button-label-spacing: 0;
|
||||||
padding: 13px 5px;
|
padding: 13px 5px;
|
||||||
margin: -4px -5px;
|
margin: -4px -5px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<template is="dom-if" if="[[_toggleEntities.length]]">
|
<template is="dom-if" if="[[_toggleEntities.length]]">
|
||||||
<paper-toggle-button checked="[[_computeIsChecked(hass, _toggleEntities)]]" on-change="_callService"></paper-toggle-button>
|
<paper-toggle-button checked="[[_computeIsChecked(hass, _toggleEntities)]]" on-change="_callService"></paper-toggle-button>
|
||||||
</template>
|
</template>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
entities: Array,
|
entities: Array,
|
||||||
_toggleEntities: {
|
_toggleEntities: {
|
||||||
type: Array,
|
type: Array,
|
||||||
computed: "_computeToggleEntities(hass, entities)",
|
computed: "_computeToggleEntities(hass, entities)",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeToggleEntities(hass, entityIds) {
|
_computeToggleEntities(hass, entityIds) {
|
||||||
return entityIds.filter(
|
return entityIds.filter(
|
||||||
(entityId) =>
|
(entityId) =>
|
||||||
entityId in hass.states && DOMAINS_TOGGLE.has(entityId.split(".", 1)[0])
|
entityId in hass.states && DOMAINS_TOGGLE.has(entityId.split(".", 1)[0])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeIsChecked(hass, entityIds) {
|
_computeIsChecked(hass, entityIds) {
|
||||||
return entityIds.some((entityId) => hass.states[entityId].state === "on");
|
return entityIds.some((entityId) => hass.states[entityId].state === "on");
|
||||||
}
|
}
|
||||||
|
|
||||||
_callService(ev) {
|
_callService(ev) {
|
||||||
const turnOn = ev.target.checked;
|
const turnOn = ev.target.checked;
|
||||||
turnOnOffEntities(this.hass, this._toggleEntities, turnOn);
|
turnOnOffEntities(this.hass, this._toggleEntities, turnOn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-entities-toggle", HuiEntitiesToggle);
|
customElements.define("hui-entities-toggle", HuiEntitiesToggle);
|
||||||
|
|||||||
@@ -1,137 +1,137 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import "../../../components/entity/state-badge";
|
import "../../../components/entity/state-badge";
|
||||||
import "../../../components/ha-relative-time";
|
import "../../../components/ha-relative-time";
|
||||||
import "../../../components/ha-icon";
|
import "../../../components/ha-icon";
|
||||||
|
|
||||||
import computeStateName from "../../../common/entity/compute_state_name";
|
import computeStateName from "../../../common/entity/compute_state_name";
|
||||||
|
|
||||||
class HuiGenericEntityRow extends PolymerElement {
|
class HuiGenericEntityRow extends PolymerElement {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
${this.styleTemplate}
|
${this.styleTemplate}
|
||||||
<template is="dom-if" if="[[_stateObj]]">
|
<template is="dom-if" if="[[_stateObj]]">
|
||||||
${this.stateBadgeTemplate}
|
${this.stateBadgeTemplate}
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
${this.infoTemplate}
|
${this.infoTemplate}
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template is="dom-if" if="[[!_stateObj]]">
|
<template is="dom-if" if="[[!_stateObj]]">
|
||||||
<div class="not-found">
|
<div class="not-found">
|
||||||
Entity not available: [[config.entity]]
|
Entity not available: [[config.entity]]
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styleTemplate() {
|
static get styleTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
:host {
|
:host {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
.flex {
|
.flex {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
margin-left: 16px;
|
margin-left: 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
.info {
|
.info {
|
||||||
flex: 1 0 60px;
|
flex: 1 0 60px;
|
||||||
}
|
}
|
||||||
.info,
|
.info,
|
||||||
.info > * {
|
.info > * {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
.flex ::slotted(*) {
|
.flex ::slotted(*) {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
.flex ::slotted([slot=secondary]) {
|
.flex ::slotted([slot=secondary]) {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
.secondary,
|
.secondary,
|
||||||
ha-relative-time {
|
ha-relative-time {
|
||||||
display: block;
|
display: block;
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
.not-found {
|
.not-found {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background-color: yellow;
|
background-color: yellow;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
state-badge {
|
state-badge {
|
||||||
flex: 0 0 40px;
|
flex: 0 0 40px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get stateBadgeTemplate() {
|
static get stateBadgeTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<state-badge
|
<state-badge
|
||||||
state-obj="[[_stateObj]]"
|
state-obj="[[_stateObj]]"
|
||||||
override-icon="[[config.icon]]"
|
override-icon="[[config.icon]]"
|
||||||
></state-badge>
|
></state-badge>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get infoTemplate() {
|
static get infoTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<div class="info">
|
<div class="info">
|
||||||
[[_computeName(config.name, _stateObj)]]
|
[[_computeName(config.name, _stateObj)]]
|
||||||
<div class="secondary">
|
<div class="secondary">
|
||||||
<template is="dom-if" if="[[showSecondary]]">
|
<template is="dom-if" if="[[showSecondary]]">
|
||||||
<template is="dom-if" if="[[_equals(config.secondary_info, 'entity-id')]]">
|
<template is="dom-if" if="[[_equals(config.secondary_info, 'entity-id')]]">
|
||||||
[[_stateObj.entity_id]]
|
[[_stateObj.entity_id]]
|
||||||
</template>
|
</template>
|
||||||
<template is="dom-if" if="[[_equals(config.secondary_info, 'last-changed')]]">
|
<template is="dom-if" if="[[_equals(config.secondary_info, 'last-changed')]]">
|
||||||
<ha-relative-time
|
<ha-relative-time
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
datetime="[[_stateObj.last_changed]]"
|
datetime="[[_stateObj.last_changed]]"
|
||||||
></ha-relative-time>
|
></ha-relative-time>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<template is="dom-if" if="[[!showSecondary]">
|
<template is="dom-if" if="[[!showSecondary]">
|
||||||
<slot name="secondary"></slot>
|
<slot name="secondary"></slot>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
config: Object,
|
config: Object,
|
||||||
_stateObj: {
|
_stateObj: {
|
||||||
type: Object,
|
type: Object,
|
||||||
computed: "_computeStateObj(hass.states, config.entity)",
|
computed: "_computeStateObj(hass.states, config.entity)",
|
||||||
},
|
},
|
||||||
showSecondary: {
|
showSecondary: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: true,
|
value: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_equals(a, b) {
|
_equals(a, b) {
|
||||||
return a === b;
|
return a === b;
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeStateObj(states, entityId) {
|
_computeStateObj(states, entityId) {
|
||||||
return states && entityId in states ? states[entityId] : null;
|
return states && entityId in states ? states[entityId] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeName(name, stateObj) {
|
_computeName(name, stateObj) {
|
||||||
return name || computeStateName(stateObj);
|
return name || computeStateName(stateObj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define("hui-generic-entity-row", HuiGenericEntityRow);
|
customElements.define("hui-generic-entity-row", HuiGenericEntityRow);
|
||||||
|
|||||||
@@ -1,198 +1,198 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
import "@polymer/paper-toggle-button/paper-toggle-button";
|
import "@polymer/paper-toggle-button/paper-toggle-button";
|
||||||
|
|
||||||
import { STATES_OFF } from "../../../common/const";
|
import { STATES_OFF } from "../../../common/const";
|
||||||
import LocalizeMixin from "../../../mixins/localize-mixin";
|
import LocalizeMixin from "../../../mixins/localize-mixin";
|
||||||
|
|
||||||
import parseAspectRatio from "../../../common/util/parse-aspect-ratio";
|
import parseAspectRatio from "../../../common/util/parse-aspect-ratio";
|
||||||
|
|
||||||
const UPDATE_INTERVAL = 10000;
|
const UPDATE_INTERVAL = 10000;
|
||||||
const DEFAULT_FILTER = "grayscale(100%)";
|
const DEFAULT_FILTER = "grayscale(100%)";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin LocalizeMixin
|
* @appliesMixin LocalizeMixin
|
||||||
*/
|
*/
|
||||||
class HuiImage extends LocalizeMixin(PolymerElement) {
|
class HuiImage extends LocalizeMixin(PolymerElement) {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
${this.styleTemplate}
|
${this.styleTemplate}
|
||||||
<div id="wrapper">
|
<div id="wrapper">
|
||||||
<img
|
<img
|
||||||
id="image"
|
id="image"
|
||||||
src="[[_imageSrc]]"
|
src="[[_imageSrc]]"
|
||||||
on-error="_onImageError"
|
on-error="_onImageError"
|
||||||
on-load="_onImageLoad" />
|
on-load="_onImageLoad" />
|
||||||
<div id="brokenImage"></div>
|
<div id="brokenImage"></div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styleTemplate() {
|
static get styleTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
|
|
||||||
img {
|
img {
|
||||||
display: block;
|
display: block;
|
||||||
height: auto;
|
height: auto;
|
||||||
transition: filter .2s linear;
|
transition: filter .2s linear;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hidden {
|
.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ratio {
|
.ratio {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 0
|
height: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
.ratio img, .ratio div {
|
.ratio img, .ratio div {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#brokenImage {
|
#brokenImage {
|
||||||
background: grey url('/static/images/image-broken.svg') center/36px no-repeat;
|
background: grey url('/static/images/image-broken.svg') center/36px no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
observer: "_hassChanged",
|
observer: "_hassChanged",
|
||||||
},
|
},
|
||||||
entity: String,
|
entity: String,
|
||||||
image: String,
|
image: String,
|
||||||
stateImage: Object,
|
stateImage: Object,
|
||||||
cameraImage: String,
|
cameraImage: String,
|
||||||
aspectRatio: String,
|
aspectRatio: String,
|
||||||
filter: String,
|
filter: String,
|
||||||
stateFilter: Object,
|
stateFilter: Object,
|
||||||
_imageSrc: String,
|
_imageSrc: String,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static get observers() {
|
static get observers() {
|
||||||
return ["_configChanged(image, stateImage, cameraImage, aspectRatio)"];
|
return ["_configChanged(image, stateImage, cameraImage, aspectRatio)"];
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
if (this.cameraImage) {
|
if (this.cameraImage) {
|
||||||
this.timer = setInterval(
|
this.timer = setInterval(
|
||||||
() => this._updateCameraImageSrc(),
|
() => this._updateCameraImageSrc(),
|
||||||
UPDATE_INTERVAL
|
UPDATE_INTERVAL
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
clearInterval(this.timer);
|
clearInterval(this.timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
_configChanged(image, stateImage, cameraImage, aspectRatio) {
|
_configChanged(image, stateImage, cameraImage, aspectRatio) {
|
||||||
const ratio = parseAspectRatio(aspectRatio);
|
const ratio = parseAspectRatio(aspectRatio);
|
||||||
|
|
||||||
if (ratio && ratio.w > 0 && ratio.h > 0) {
|
if (ratio && ratio.w > 0 && ratio.h > 0) {
|
||||||
this.$.wrapper.style.paddingBottom = `${(
|
this.$.wrapper.style.paddingBottom = `${(
|
||||||
(100 * ratio.h) /
|
(100 * ratio.h) /
|
||||||
ratio.w
|
ratio.w
|
||||||
).toFixed(2)}%`;
|
).toFixed(2)}%`;
|
||||||
this.$.wrapper.classList.add("ratio");
|
this.$.wrapper.classList.add("ratio");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cameraImage) {
|
if (cameraImage) {
|
||||||
this._updateCameraImageSrc();
|
this._updateCameraImageSrc();
|
||||||
} else if (image && !stateImage) {
|
} else if (image && !stateImage) {
|
||||||
this._imageSrc = image;
|
this._imageSrc = image;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onImageError() {
|
_onImageError() {
|
||||||
this._imageSrc = null;
|
this._imageSrc = null;
|
||||||
this.$.image.classList.add("hidden");
|
this.$.image.classList.add("hidden");
|
||||||
if (!this.$.wrapper.classList.contains("ratio")) {
|
if (!this.$.wrapper.classList.contains("ratio")) {
|
||||||
this.$.brokenImage.style.setProperty(
|
this.$.brokenImage.style.setProperty(
|
||||||
"height",
|
"height",
|
||||||
`${this._lastImageHeight || "100"}px`
|
`${this._lastImageHeight || "100"}px`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.$.brokenImage.classList.remove("hidden");
|
this.$.brokenImage.classList.remove("hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
_onImageLoad() {
|
_onImageLoad() {
|
||||||
this.$.image.classList.remove("hidden");
|
this.$.image.classList.remove("hidden");
|
||||||
this.$.brokenImage.classList.add("hidden");
|
this.$.brokenImage.classList.add("hidden");
|
||||||
if (!this.$.wrapper.classList.contains("ratio")) {
|
if (!this.$.wrapper.classList.contains("ratio")) {
|
||||||
this._lastImageHeight = this.$.image.offsetHeight;
|
this._lastImageHeight = this.$.image.offsetHeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_hassChanged(hass) {
|
_hassChanged(hass) {
|
||||||
if (this.cameraImage || !this.entity) {
|
if (this.cameraImage || !this.entity) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const stateObj = hass.states[this.entity];
|
const stateObj = hass.states[this.entity];
|
||||||
const newState = !stateObj ? "unavailable" : stateObj.state;
|
const newState = !stateObj ? "unavailable" : stateObj.state;
|
||||||
|
|
||||||
if (newState === this._currentState) return;
|
if (newState === this._currentState) return;
|
||||||
this._currentState = newState;
|
this._currentState = newState;
|
||||||
|
|
||||||
this._updateStateImage();
|
this._updateStateImage();
|
||||||
this._updateStateFilter(stateObj);
|
this._updateStateFilter(stateObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateStateImage() {
|
_updateStateImage() {
|
||||||
if (!this.stateImage) {
|
if (!this.stateImage) {
|
||||||
this._imageFallback = true;
|
this._imageFallback = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const stateImg = this.stateImage[this._currentState];
|
const stateImg = this.stateImage[this._currentState];
|
||||||
this._imageSrc = stateImg || this.image;
|
this._imageSrc = stateImg || this.image;
|
||||||
this._imageFallback = !stateImg;
|
this._imageFallback = !stateImg;
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateStateFilter(stateObj) {
|
_updateStateFilter(stateObj) {
|
||||||
let filter;
|
let filter;
|
||||||
if (!this.stateFilter) {
|
if (!this.stateFilter) {
|
||||||
filter = this.filter;
|
filter = this.filter;
|
||||||
} else {
|
} else {
|
||||||
filter = this.stateFilter[this._currentState] || this.filter;
|
filter = this.stateFilter[this._currentState] || this.filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isOff = !stateObj || STATES_OFF.includes(stateObj.state);
|
const isOff = !stateObj || STATES_OFF.includes(stateObj.state);
|
||||||
this.$.image.style.filter =
|
this.$.image.style.filter =
|
||||||
filter || (isOff && this._imageFallback && DEFAULT_FILTER) || "";
|
filter || (isOff && this._imageFallback && DEFAULT_FILTER) || "";
|
||||||
}
|
}
|
||||||
|
|
||||||
async _updateCameraImageSrc() {
|
async _updateCameraImageSrc() {
|
||||||
try {
|
try {
|
||||||
const { content_type: contentType, content } = await this.hass.callWS({
|
const { content_type: contentType, content } = await this.hass.callWS({
|
||||||
type: "camera_thumbnail",
|
type: "camera_thumbnail",
|
||||||
entity_id: this.cameraImage,
|
entity_id: this.cameraImage,
|
||||||
});
|
});
|
||||||
this._imageSrc = `data:${contentType};base64, ${content}`;
|
this._imageSrc = `data:${contentType};base64, ${content}`;
|
||||||
this._onImageLoad();
|
this._onImageLoad();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this._onImageError();
|
this._onImageError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-image", HuiImage);
|
customElements.define("hui-image", HuiImage);
|
||||||
|
|||||||
@@ -1,62 +1,62 @@
|
|||||||
import "@polymer/paper-button/paper-button";
|
import "@polymer/paper-button/paper-button";
|
||||||
import "@polymer/paper-icon-button/paper-icon-button";
|
import "@polymer/paper-icon-button/paper-icon-button";
|
||||||
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import "./hui-notification-item-template";
|
import "./hui-notification-item-template";
|
||||||
|
|
||||||
import EventsMixin from "../../../../mixins/events-mixin";
|
import EventsMixin from "../../../../mixins/events-mixin";
|
||||||
import LocalizeMixin from "../../../../mixins/localize-mixin";
|
import LocalizeMixin from "../../../../mixins/localize-mixin";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin EventsMixin
|
* @appliesMixin EventsMixin
|
||||||
* @appliesMixin LocalizeMixin
|
* @appliesMixin LocalizeMixin
|
||||||
*/
|
*/
|
||||||
export class HuiConfiguratorNotificationItem extends EventsMixin(
|
export class HuiConfiguratorNotificationItem extends EventsMixin(
|
||||||
LocalizeMixin(PolymerElement)
|
LocalizeMixin(PolymerElement)
|
||||||
) {
|
) {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<hui-notification-item-template>
|
<hui-notification-item-template>
|
||||||
<span slot="header">[[localize('domain.configurator')]]</span>
|
<span slot="header">[[localize('domain.configurator')]]</span>
|
||||||
|
|
||||||
<div>[[_getMessage(notification)]]</div>
|
<div>[[_getMessage(notification)]]</div>
|
||||||
|
|
||||||
<paper-button
|
<paper-button
|
||||||
slot="actions"
|
slot="actions"
|
||||||
class="primary"
|
class="primary"
|
||||||
on-click="_handleClick"
|
on-click="_handleClick"
|
||||||
>[[_localizeState(notification.state)]]</paper-button>
|
>[[_localizeState(notification.state)]]</paper-button>
|
||||||
</hui-notification-item-template>
|
</hui-notification-item-template>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
notification: Object,
|
notification: Object,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleClick() {
|
_handleClick() {
|
||||||
this.fire("hass-more-info", { entityId: this.notification.entity_id });
|
this.fire("hass-more-info", { entityId: this.notification.entity_id });
|
||||||
}
|
}
|
||||||
|
|
||||||
_localizeState(state) {
|
_localizeState(state) {
|
||||||
return this.localize(`state.configurator.${state}`);
|
return this.localize(`state.configurator.${state}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
_getMessage(notification) {
|
_getMessage(notification) {
|
||||||
const friendlyName = notification.attributes.friendly_name;
|
const friendlyName = notification.attributes.friendly_name;
|
||||||
return this.localize(
|
return this.localize(
|
||||||
"ui.notification_drawer.click_to_configure",
|
"ui.notification_drawer.click_to_configure",
|
||||||
"entity",
|
"entity",
|
||||||
friendlyName
|
friendlyName
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define(
|
customElements.define(
|
||||||
"hui-configurator-notification-item",
|
"hui-configurator-notification-item",
|
||||||
HuiConfiguratorNotificationItem
|
HuiConfiguratorNotificationItem
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,175 +1,175 @@
|
|||||||
import "@polymer/paper-button/paper-button";
|
import "@polymer/paper-button/paper-button";
|
||||||
import "@polymer/paper-icon-button/paper-icon-button";
|
import "@polymer/paper-icon-button/paper-icon-button";
|
||||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||||
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import "./hui-notification-item";
|
import "./hui-notification-item";
|
||||||
|
|
||||||
import EventsMixin from "../../../../mixins/events-mixin";
|
import EventsMixin from "../../../../mixins/events-mixin";
|
||||||
import LocalizeMixin from "../../../../mixins/localize-mixin";
|
import LocalizeMixin from "../../../../mixins/localize-mixin";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin EventsMixin
|
* @appliesMixin EventsMixin
|
||||||
* @appliesMixin LocalizeMixin
|
* @appliesMixin LocalizeMixin
|
||||||
*/
|
*/
|
||||||
export class HuiNotificationDrawer extends EventsMixin(
|
export class HuiNotificationDrawer extends EventsMixin(
|
||||||
LocalizeMixin(PolymerElement)
|
LocalizeMixin(PolymerElement)
|
||||||
) {
|
) {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<style include="paper-material-styles">
|
<style include="paper-material-styles">
|
||||||
:host {
|
:host {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host([hidden]) {
|
:host([hidden]) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
background: var(--sidebar-background-color, var(--primary-background-color));
|
background: var(--sidebar-background-color, var(--primary-background-color));
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
box-shadow: var(--paper-material-elevation-1_-_box-shadow);
|
box-shadow: var(--paper-material-elevation-1_-_box-shadow);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
transition: right .2s ease-in;
|
transition: right .2s ease-in;
|
||||||
width: 500px;
|
width: 500px;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host(:not(narrow)) .container {
|
:host(:not(narrow)) .container {
|
||||||
right: -500px;
|
right: -500px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host([narrow]) .container {
|
:host([narrow]) .container {
|
||||||
right: -100%;
|
right: -100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host(.open) .container,
|
:host(.open) .container,
|
||||||
:host(.open[narrow]) .container {
|
:host(.open[narrow]) .container {
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
app-toolbar {
|
app-toolbar {
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
border-bottom: 1px solid var(--divider-color);
|
border-bottom: 1px solid var(--divider-color);
|
||||||
background-color: var(--primary-background-color);
|
background-color: var(--primary-background-color);
|
||||||
min-height: 64px;
|
min-height: 64px;
|
||||||
width: calc(100% - 32px);
|
width: calc(100% - 32px);
|
||||||
z-index: 11;
|
z-index: 11;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay {
|
.overlay {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host(.open) .overlay {
|
:host(.open) .overlay {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
display: block;
|
display: block;
|
||||||
left: 0;
|
left: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notifications {
|
.notifications {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding-top: 16px;
|
padding-top: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification {
|
.notification {
|
||||||
padding: 0 16px 16px;
|
padding: 0 16px 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty {
|
.empty {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<div class="overlay" on-click="_closeDrawer"></div>
|
<div class="overlay" on-click="_closeDrawer"></div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<div main-title>[[localize('ui.notification_drawer.title')]]</div>
|
<div main-title>[[localize('ui.notification_drawer.title')]]</div>
|
||||||
<paper-icon-button icon="hass:chevron-right" on-click="_closeDrawer"></paper-icon-button>
|
<paper-icon-button icon="hass:chevron-right" on-click="_closeDrawer"></paper-icon-button>
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
<div class="notifications">
|
<div class="notifications">
|
||||||
<template is="dom-if" if="[[!_empty(notifications)]]">
|
<template is="dom-if" if="[[!_empty(notifications)]]">
|
||||||
<dom-repeat items="[[notifications]]">
|
<dom-repeat items="[[notifications]]">
|
||||||
<template>
|
<template>
|
||||||
<div class="notification">
|
<div class="notification">
|
||||||
<hui-notification-item hass="[[hass]]" notification="[[item]]"></hui-notification-item>
|
<hui-notification-item hass="[[hass]]" notification="[[item]]"></hui-notification-item>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</dom-repeat>
|
</dom-repeat>
|
||||||
</template>
|
</template>
|
||||||
<template is="dom-if" if="[[_empty(notifications)]]">
|
<template is="dom-if" if="[[_empty(notifications)]]">
|
||||||
<div class="empty">[[localize('ui.notification_drawer.empty')]]<div>
|
<div class="empty">[[localize('ui.notification_drawer.empty')]]<div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
narrow: {
|
narrow: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
reflectToAttribute: true,
|
reflectToAttribute: true,
|
||||||
},
|
},
|
||||||
open: {
|
open: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
notify: true,
|
notify: true,
|
||||||
observer: "_openChanged",
|
observer: "_openChanged",
|
||||||
},
|
},
|
||||||
hidden: {
|
hidden: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: true,
|
value: true,
|
||||||
reflectToAttribute: true,
|
reflectToAttribute: true,
|
||||||
},
|
},
|
||||||
notifications: {
|
notifications: {
|
||||||
type: Array,
|
type: Array,
|
||||||
value: [],
|
value: [],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_closeDrawer(ev) {
|
_closeDrawer(ev) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.open = false;
|
this.open = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_empty(notifications) {
|
_empty(notifications) {
|
||||||
return notifications.length === 0;
|
return notifications.length === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_openChanged(open) {
|
_openChanged(open) {
|
||||||
clearTimeout(this._openTimer);
|
clearTimeout(this._openTimer);
|
||||||
if (open) {
|
if (open) {
|
||||||
// Render closed then animate open
|
// Render closed then animate open
|
||||||
this.hidden = false;
|
this.hidden = false;
|
||||||
this._openTimer = setTimeout(() => {
|
this._openTimer = setTimeout(() => {
|
||||||
this.classList.add("open");
|
this.classList.add("open");
|
||||||
}, 50);
|
}, 50);
|
||||||
} else {
|
} else {
|
||||||
// Animate closed then hide
|
// Animate closed then hide
|
||||||
this.classList.remove("open");
|
this.classList.remove("open");
|
||||||
this._openTimer = setTimeout(() => {
|
this._openTimer = setTimeout(() => {
|
||||||
this.hidden = true;
|
this.hidden = true;
|
||||||
}, 250);
|
}, 250);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define("hui-notification-drawer", HuiNotificationDrawer);
|
customElements.define("hui-notification-drawer", HuiNotificationDrawer);
|
||||||
|
|||||||
@@ -1,49 +1,49 @@
|
|||||||
import "@polymer/paper-button/paper-button";
|
import "@polymer/paper-button/paper-button";
|
||||||
import "@polymer/paper-icon-button/paper-icon-button";
|
import "@polymer/paper-icon-button/paper-icon-button";
|
||||||
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
|
|
||||||
export class HuiNotificationItemTemplate extends PolymerElement {
|
export class HuiNotificationItemTemplate extends PolymerElement {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
.contents {
|
.contents {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-card .header {
|
ha-card .header {
|
||||||
@apply --paper-font-headline;
|
@apply --paper-font-headline;
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
padding: 16px 16px 0;
|
padding: 16px 16px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
border-top: 1px solid #e8e8e8;
|
border-top: 1px solid #e8e8e8;
|
||||||
padding: 5px 16px;
|
padding: 5px 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
::slotted(.primary) {
|
::slotted(.primary) {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<ha-card>
|
<ha-card>
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<slot name="header"></slot>
|
<slot name="header"></slot>
|
||||||
</div>
|
</div>
|
||||||
<div class="contents">
|
<div class="contents">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<slot name="actions"></slot>
|
<slot name="actions"></slot>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define(
|
customElements.define(
|
||||||
"hui-notification-item-template",
|
"hui-notification-item-template",
|
||||||
HuiNotificationItemTemplate
|
HuiNotificationItemTemplate
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,35 +1,35 @@
|
|||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
import computeDomain from "../../../../common/entity/compute_domain";
|
import computeDomain from "../../../../common/entity/compute_domain";
|
||||||
|
|
||||||
import "./hui-configurator-notification-item";
|
import "./hui-configurator-notification-item";
|
||||||
import "./hui-persistent-notification-item";
|
import "./hui-persistent-notification-item";
|
||||||
|
|
||||||
export class HuiNotificationItem extends PolymerElement {
|
export class HuiNotificationItem extends PolymerElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
notification: {
|
notification: {
|
||||||
type: Object,
|
type: Object,
|
||||||
observer: "_stateChanged",
|
observer: "_stateChanged",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_stateChanged(notification) {
|
_stateChanged(notification) {
|
||||||
if (this.lastChild) {
|
if (this.lastChild) {
|
||||||
this.removeChild(this.lastChild);
|
this.removeChild(this.lastChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!notification) return;
|
if (!notification) return;
|
||||||
|
|
||||||
const domain = notification.entity_id
|
const domain = notification.entity_id
|
||||||
? computeDomain(notification.entity_id)
|
? computeDomain(notification.entity_id)
|
||||||
: "persistent_notification";
|
: "persistent_notification";
|
||||||
const tag = `hui-${domain}-notification-item`;
|
const tag = `hui-${domain}-notification-item`;
|
||||||
const el = document.createElement(tag);
|
const el = document.createElement(tag);
|
||||||
el.hass = this.hass;
|
el.hass = this.hass;
|
||||||
el.notification = notification;
|
el.notification = notification;
|
||||||
this.appendChild(el);
|
this.appendChild(el);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define("hui-notification-item", HuiNotificationItem);
|
customElements.define("hui-notification-item", HuiNotificationItem);
|
||||||
|
|||||||
@@ -1,62 +1,62 @@
|
|||||||
import "@polymer/paper-button/paper-button";
|
import "@polymer/paper-button/paper-button";
|
||||||
import "@polymer/paper-icon-button/paper-icon-button";
|
import "@polymer/paper-icon-button/paper-icon-button";
|
||||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||||
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import EventsMixin from "../../../../mixins/events-mixin";
|
import EventsMixin from "../../../../mixins/events-mixin";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin EventsMixin
|
* @appliesMixin EventsMixin
|
||||||
*/
|
*/
|
||||||
export class HuiNotificationsButton extends EventsMixin(PolymerElement) {
|
export class HuiNotificationsButton extends EventsMixin(PolymerElement) {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
:host {
|
:host {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.indicator {
|
.indicator {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
width: 10px;
|
width: 10px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: var(--accent-color);
|
background: var(--accent-color);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.indicator[hidden] {
|
.indicator[hidden] {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<paper-icon-button icon="hass:bell" on-click="_clicked"></paper-icon-button>
|
<paper-icon-button icon="hass:bell" on-click="_clicked"></paper-icon-button>
|
||||||
<span class="indicator" hidden$="[[!_hasNotifications(notifications)]]"></span>
|
<span class="indicator" hidden$="[[!_hasNotifications(notifications)]]"></span>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
notificationsOpen: {
|
notificationsOpen: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
notify: true,
|
notify: true,
|
||||||
},
|
},
|
||||||
notifications: {
|
notifications: {
|
||||||
type: Array,
|
type: Array,
|
||||||
value: [],
|
value: [],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_clicked() {
|
_clicked() {
|
||||||
this.notificationsOpen = true;
|
this.notificationsOpen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_hasNotifications(notifications) {
|
_hasNotifications(notifications) {
|
||||||
return notifications.length > 0;
|
return notifications.length > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define("hui-notifications-button", HuiNotificationsButton);
|
customElements.define("hui-notifications-button", HuiNotificationsButton);
|
||||||
|
|||||||
@@ -1,89 +1,89 @@
|
|||||||
import "@polymer/paper-button/paper-button";
|
import "@polymer/paper-button/paper-button";
|
||||||
import "@polymer/paper-icon-button/paper-icon-button";
|
import "@polymer/paper-icon-button/paper-icon-button";
|
||||||
import "@polymer/paper-tooltip/paper-tooltip";
|
import "@polymer/paper-tooltip/paper-tooltip";
|
||||||
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import "../../../../components/ha-relative-time";
|
import "../../../../components/ha-relative-time";
|
||||||
import "../../../../components/ha-markdown";
|
import "../../../../components/ha-markdown";
|
||||||
import "./hui-notification-item-template";
|
import "./hui-notification-item-template";
|
||||||
|
|
||||||
import LocalizeMixin from "../../../../mixins/localize-mixin";
|
import LocalizeMixin from "../../../../mixins/localize-mixin";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin LocalizeMixin
|
* @appliesMixin LocalizeMixin
|
||||||
*/
|
*/
|
||||||
export class HuiPersistentNotificationItem extends LocalizeMixin(
|
export class HuiPersistentNotificationItem extends LocalizeMixin(
|
||||||
PolymerElement
|
PolymerElement
|
||||||
) {
|
) {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
.time {
|
.time {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
}
|
}
|
||||||
ha-relative-time {
|
ha-relative-time {
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<hui-notification-item-template>
|
<hui-notification-item-template>
|
||||||
<span slot="header">[[_computeTitle(notification)]]</span>
|
<span slot="header">[[_computeTitle(notification)]]</span>
|
||||||
|
|
||||||
<ha-markdown content="[[notification.message]]"></ha-markdown>
|
<ha-markdown content="[[notification.message]]"></ha-markdown>
|
||||||
|
|
||||||
<div class="time">
|
<div class="time">
|
||||||
<span>
|
<span>
|
||||||
<ha-relative-time
|
<ha-relative-time
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
datetime="[[notification.created_at]]"
|
datetime="[[notification.created_at]]"
|
||||||
></ha-relative-time>
|
></ha-relative-time>
|
||||||
<paper-tooltip>[[_computeTooltip(hass, notification)]]</paper-tooltip>
|
<paper-tooltip>[[_computeTooltip(hass, notification)]]</paper-tooltip>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<paper-button
|
<paper-button
|
||||||
slot="actions"
|
slot="actions"
|
||||||
class="primary"
|
class="primary"
|
||||||
on-click="_handleDismiss"
|
on-click="_handleDismiss"
|
||||||
>[[localize('ui.card.persistent_notification.dismiss')]]</paper-button>
|
>[[localize('ui.card.persistent_notification.dismiss')]]</paper-button>
|
||||||
</hui-notification-item-template>
|
</hui-notification-item-template>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
notification: Object,
|
notification: Object,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleDismiss() {
|
_handleDismiss() {
|
||||||
this.hass.callService("persistent_notification", "dismiss", {
|
this.hass.callService("persistent_notification", "dismiss", {
|
||||||
notification_id: this.notification.notification_id,
|
notification_id: this.notification.notification_id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeTitle(notification) {
|
_computeTitle(notification) {
|
||||||
return notification.title || notification.notification_id;
|
return notification.title || notification.notification_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeTooltip(hass, notification) {
|
_computeTooltip(hass, notification) {
|
||||||
if (!hass || !notification) return null;
|
if (!hass || !notification) return null;
|
||||||
|
|
||||||
const d = new Date(notification.created_at);
|
const d = new Date(notification.created_at);
|
||||||
return d.toLocaleDateString(hass.language, {
|
return d.toLocaleDateString(hass.language, {
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
month: "short",
|
month: "short",
|
||||||
day: "numeric",
|
day: "numeric",
|
||||||
minute: "numeric",
|
minute: "numeric",
|
||||||
hour: "numeric",
|
hour: "numeric",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define(
|
customElements.define(
|
||||||
"hui-persistent_notification-notification-item",
|
"hui-persistent_notification-notification-item",
|
||||||
HuiPersistentNotificationItem
|
HuiPersistentNotificationItem
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,122 +1,122 @@
|
|||||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
|
|
||||||
import "@polymer/paper-button/paper-button";
|
import "@polymer/paper-button/paper-button";
|
||||||
import "@polymer/paper-input/paper-textarea";
|
import "@polymer/paper-input/paper-textarea";
|
||||||
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
||||||
import "@polymer/paper-dialog/paper-dialog";
|
import "@polymer/paper-dialog/paper-dialog";
|
||||||
// This is not a duplicate import, one is for types, one is for element.
|
// This is not a duplicate import, one is for types, one is for element.
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
import { PaperDialogElement } from "@polymer/paper-dialog/paper-dialog";
|
import { PaperDialogElement } from "@polymer/paper-dialog/paper-dialog";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { getCardConfig, updateCardConfig } from "../common/data";
|
import { getCardConfig, updateCardConfig } from "../common/data";
|
||||||
|
|
||||||
import "./hui-yaml-editor";
|
import "./hui-yaml-editor";
|
||||||
import "./hui-yaml-card-preview";
|
import "./hui-yaml-card-preview";
|
||||||
// This is not a duplicate import, one is for types, one is for element.
|
// This is not a duplicate import, one is for types, one is for element.
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
import { HuiYAMLCardPreview } from "./hui-yaml-card-preview";
|
import { HuiYAMLCardPreview } from "./hui-yaml-card-preview";
|
||||||
|
|
||||||
export class HuiDialogEditCard extends LitElement {
|
export class HuiDialogEditCard extends LitElement {
|
||||||
protected hass?: HomeAssistant;
|
protected hass?: HomeAssistant;
|
||||||
private _cardId?: string;
|
private _cardId?: string;
|
||||||
private _cardConfig?: string;
|
private _cardConfig?: string;
|
||||||
private _reloadLovelace?: () => void;
|
private _reloadLovelace?: () => void;
|
||||||
|
|
||||||
static get properties(): PropertyDeclarations {
|
static get properties(): PropertyDeclarations {
|
||||||
return {
|
return {
|
||||||
hass: {},
|
hass: {},
|
||||||
cardId: {
|
cardId: {
|
||||||
type: Number,
|
type: Number,
|
||||||
},
|
},
|
||||||
_cardConfig: {},
|
_cardConfig: {},
|
||||||
_dialogClosedCallback: {},
|
_dialogClosedCallback: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async showDialog({ hass, cardId, reloadLovelace }) {
|
public async showDialog({ hass, cardId, reloadLovelace }) {
|
||||||
this.hass = hass;
|
this.hass = hass;
|
||||||
this._cardId = cardId;
|
this._cardId = cardId;
|
||||||
this._reloadLovelace = reloadLovelace;
|
this._reloadLovelace = reloadLovelace;
|
||||||
this._cardConfig = "";
|
this._cardConfig = "";
|
||||||
this._loadConfig();
|
this._loadConfig();
|
||||||
// Wait till dialog is rendered.
|
// Wait till dialog is rendered.
|
||||||
await this.updateComplete;
|
await this.updateComplete;
|
||||||
this._dialog.open();
|
this._dialog.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
private get _dialog(): PaperDialogElement {
|
private get _dialog(): PaperDialogElement {
|
||||||
return this.shadowRoot!.querySelector("paper-dialog")!;
|
return this.shadowRoot!.querySelector("paper-dialog")!;
|
||||||
}
|
}
|
||||||
|
|
||||||
private get _previewEl(): HuiYAMLCardPreview {
|
private get _previewEl(): HuiYAMLCardPreview {
|
||||||
return this.shadowRoot!.querySelector("hui-yaml-card-preview")!;
|
return this.shadowRoot!.querySelector("hui-yaml-card-preview")!;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
paper-dialog {
|
paper-dialog {
|
||||||
width: 650px;
|
width: 650px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<paper-dialog with-backdrop>
|
<paper-dialog with-backdrop>
|
||||||
<h2>Card Configuration</h2>
|
<h2>Card Configuration</h2>
|
||||||
<paper-dialog-scrollable>
|
<paper-dialog-scrollable>
|
||||||
<hui-yaml-editor
|
<hui-yaml-editor
|
||||||
.yaml="${this._cardConfig}"
|
.yaml="${this._cardConfig}"
|
||||||
@yaml-changed="${this._handleYamlChanged}"
|
@yaml-changed="${this._handleYamlChanged}"
|
||||||
></hui-yaml-editor>
|
></hui-yaml-editor>
|
||||||
<hui-yaml-card-preview
|
<hui-yaml-card-preview
|
||||||
.hass="${this.hass}"
|
.hass="${this.hass}"
|
||||||
.yaml="${this._cardConfig}"
|
.yaml="${this._cardConfig}"
|
||||||
></hui-yaml-card-preview>
|
></hui-yaml-card-preview>
|
||||||
</paper-dialog-scrollable>
|
</paper-dialog-scrollable>
|
||||||
<div class="paper-dialog-buttons">
|
<div class="paper-dialog-buttons">
|
||||||
<paper-button @click="${this._closeDialog}">Cancel</paper-button>
|
<paper-button @click="${this._closeDialog}">Cancel</paper-button>
|
||||||
<paper-button @click="${this._updateConfig}">Save</paper-button>
|
<paper-button @click="${this._updateConfig}">Save</paper-button>
|
||||||
</div>
|
</div>
|
||||||
</paper-dialog>
|
</paper-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleYamlChanged(ev) {
|
private _handleYamlChanged(ev) {
|
||||||
this._previewEl.yaml = ev.detail.yaml;
|
this._previewEl.yaml = ev.detail.yaml;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _closeDialog() {
|
private _closeDialog() {
|
||||||
this._dialog.close();
|
this._dialog.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _loadConfig() {
|
private async _loadConfig() {
|
||||||
this._cardConfig = await getCardConfig(this.hass!, this._cardId!);
|
this._cardConfig = await getCardConfig(this.hass!, this._cardId!);
|
||||||
await this.updateComplete;
|
await this.updateComplete;
|
||||||
// This will center the dialog with the updated config
|
// This will center the dialog with the updated config
|
||||||
fireEvent(this._dialog, "iron-resize");
|
fireEvent(this._dialog, "iron-resize");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _updateConfig() {
|
private async _updateConfig() {
|
||||||
const newCardConfig = this.shadowRoot!.querySelector("hui-yaml-editor")!
|
const newCardConfig = this.shadowRoot!.querySelector("hui-yaml-editor")!
|
||||||
.yaml;
|
.yaml;
|
||||||
|
|
||||||
if (this._cardConfig === newCardConfig) {
|
if (this._cardConfig === newCardConfig) {
|
||||||
this._dialog.close();
|
this._dialog.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await updateCardConfig(this.hass!, this._cardId!, newCardConfig);
|
await updateCardConfig(this.hass!, this._cardId!, newCardConfig);
|
||||||
this._dialog.close();
|
this._dialog.close();
|
||||||
this._reloadLovelace!();
|
this._reloadLovelace!();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert(`Saving failed: ${err.reason}`);
|
alert(`Saving failed: ${err.reason}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-dialog-edit-card": HuiDialogEditCard;
|
"hui-dialog-edit-card": HuiDialogEditCard;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-dialog-edit-card", HuiDialogEditCard);
|
customElements.define("hui-dialog-edit-card", HuiDialogEditCard);
|
||||||
|
|||||||
@@ -1,52 +1,52 @@
|
|||||||
import yaml from "js-yaml";
|
import yaml from "js-yaml";
|
||||||
|
|
||||||
import "@polymer/paper-input/paper-textarea";
|
import "@polymer/paper-input/paper-textarea";
|
||||||
|
|
||||||
import createCardElement from "../common/create-card-element";
|
import createCardElement from "../common/create-card-element";
|
||||||
import createErrorCardConfig from "../common/create-error-card-config";
|
import createErrorCardConfig from "../common/create-error-card-config";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { LovelaceCard } from "../types";
|
import { LovelaceCard } from "../types";
|
||||||
|
|
||||||
export class HuiYAMLCardPreview extends HTMLElement {
|
export class HuiYAMLCardPreview extends HTMLElement {
|
||||||
private _hass?: HomeAssistant;
|
private _hass?: HomeAssistant;
|
||||||
|
|
||||||
set hass(value: HomeAssistant) {
|
set hass(value: HomeAssistant) {
|
||||||
this._hass = value;
|
this._hass = value;
|
||||||
if (this.lastChild) {
|
if (this.lastChild) {
|
||||||
(this.lastChild as LovelaceCard).hass = value;
|
(this.lastChild as LovelaceCard).hass = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
set yaml(value: string) {
|
set yaml(value: string) {
|
||||||
if (this.lastChild) {
|
if (this.lastChild) {
|
||||||
this.removeChild(this.lastChild);
|
this.removeChild(this.lastChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value === "") {
|
if (value === "") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let conf;
|
let conf;
|
||||||
try {
|
try {
|
||||||
conf = yaml.safeLoad(value);
|
conf = yaml.safeLoad(value);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
conf = createErrorCardConfig(`Invalid YAML: ${err.message}`, undefined);
|
conf = createErrorCardConfig(`Invalid YAML: ${err.message}`, undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
const element = createCardElement(conf);
|
const element = createCardElement(conf);
|
||||||
|
|
||||||
if (this._hass) {
|
if (this._hass) {
|
||||||
element.hass = this._hass;
|
element.hass = this._hass;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.appendChild(element);
|
this.appendChild(element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-yaml-card-preview": HuiYAMLCardPreview;
|
"hui-yaml-card-preview": HuiYAMLCardPreview;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-yaml-card-preview", HuiYAMLCardPreview);
|
customElements.define("hui-yaml-card-preview", HuiYAMLCardPreview);
|
||||||
|
|||||||
@@ -1,41 +1,41 @@
|
|||||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
|
|
||||||
import "@polymer/paper-input/paper-textarea";
|
import "@polymer/paper-input/paper-textarea";
|
||||||
|
|
||||||
export class HuiYAMLEditor extends LitElement {
|
export class HuiYAMLEditor extends LitElement {
|
||||||
public yaml?: string;
|
public yaml?: string;
|
||||||
|
|
||||||
static get properties(): PropertyDeclarations {
|
static get properties(): PropertyDeclarations {
|
||||||
return {
|
return {
|
||||||
yaml: {},
|
yaml: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
paper-textarea {
|
paper-textarea {
|
||||||
--paper-input-container-shared-input-style_-_font-family: monospace;
|
--paper-input-container-shared-input-style_-_font-family: monospace;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<paper-textarea
|
<paper-textarea
|
||||||
value="${this.yaml}"
|
value="${this.yaml}"
|
||||||
@value-changed="${this._valueChanged}"
|
@value-changed="${this._valueChanged}"
|
||||||
></paper-textarea>
|
></paper-textarea>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _valueChanged(ev) {
|
private _valueChanged(ev) {
|
||||||
this.yaml = ev.target.value;
|
this.yaml = ev.target.value;
|
||||||
fireEvent(this, "yaml-changed", { yaml: ev.target.value });
|
fireEvent(this, "yaml-changed", { yaml: ev.target.value });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-yaml-editor": HuiYAMLEditor;
|
"hui-yaml-editor": HuiYAMLEditor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-yaml-editor", HuiYAMLEditor);
|
customElements.define("hui-yaml-editor", HuiYAMLEditor);
|
||||||
|
|||||||
@@ -1,68 +1,68 @@
|
|||||||
import { html, LitElement } from "@polymer/lit-element";
|
import { html, LitElement } from "@polymer/lit-element";
|
||||||
|
|
||||||
import "../../../components/ha-icon";
|
import "../../../components/ha-icon";
|
||||||
|
|
||||||
import { computeTooltip } from "../common/compute-tooltip";
|
import { computeTooltip } from "../common/compute-tooltip";
|
||||||
import { handleClick } from "../common/handle-click";
|
import { handleClick } from "../common/handle-click";
|
||||||
import { longPress } from "../common/directives/long-press-directive";
|
import { longPress } from "../common/directives/long-press-directive";
|
||||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
||||||
import { LovelaceElement, LovelaceElementConfig } from "./types";
|
import { LovelaceElement, LovelaceElementConfig } from "./types";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
interface Config extends LovelaceElementConfig {
|
interface Config extends LovelaceElementConfig {
|
||||||
icon: string;
|
icon: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class HuiIconElement extends hassLocalizeLitMixin(LitElement)
|
export class HuiIconElement extends hassLocalizeLitMixin(LitElement)
|
||||||
implements LovelaceElement {
|
implements LovelaceElement {
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
private _config?: Config;
|
private _config?: Config;
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return { hass: {}, _config: {} };
|
return { hass: {}, _config: {} };
|
||||||
}
|
}
|
||||||
|
|
||||||
public setConfig(config: Config): void {
|
public setConfig(config: Config): void {
|
||||||
if (!config.icon) {
|
if (!config.icon) {
|
||||||
throw Error("Invalid Configuration: 'icon' required");
|
throw Error("Invalid Configuration: 'icon' required");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this._config) {
|
if (!this._config) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${this.renderStyle()}
|
${this.renderStyle()}
|
||||||
<ha-icon
|
<ha-icon
|
||||||
.icon="${this._config.icon}"
|
.icon="${this._config.icon}"
|
||||||
.title="${computeTooltip(this.hass!, this._config)}"
|
.title="${computeTooltip(this.hass!, this._config)}"
|
||||||
@ha-click="${() => handleClick(this, this.hass!, this._config!, false)}"
|
@ha-click="${() => handleClick(this, this.hass!, this._config!, false)}"
|
||||||
@ha-hold="${() => handleClick(this, this.hass!, this._config!, true)}"
|
@ha-hold="${() => handleClick(this, this.hass!, this._config!, true)}"
|
||||||
.longPress="${longPress()}"
|
.longPress="${longPress()}"
|
||||||
></ha-icon>
|
></ha-icon>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderStyle(): TemplateResult {
|
private renderStyle(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
:host {
|
:host {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-icon-element": HuiIconElement;
|
"hui-icon-element": HuiIconElement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-icon-element", HuiIconElement);
|
customElements.define("hui-icon-element", HuiIconElement);
|
||||||
|
|||||||
@@ -1,94 +1,94 @@
|
|||||||
import { html, LitElement } from "@polymer/lit-element";
|
import { html, LitElement } from "@polymer/lit-element";
|
||||||
|
|
||||||
import "../components/hui-image";
|
import "../components/hui-image";
|
||||||
|
|
||||||
import { computeTooltip } from "../common/compute-tooltip";
|
import { computeTooltip } from "../common/compute-tooltip";
|
||||||
import { handleClick } from "../common/handle-click";
|
import { handleClick } from "../common/handle-click";
|
||||||
import { longPress } from "../common/directives/long-press-directive";
|
import { longPress } from "../common/directives/long-press-directive";
|
||||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
||||||
import { LovelaceElement, LovelaceElementConfig } from "./types";
|
import { LovelaceElement, LovelaceElementConfig } from "./types";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
interface Config extends LovelaceElementConfig {
|
interface Config extends LovelaceElementConfig {
|
||||||
image?: string;
|
image?: string;
|
||||||
state_image?: string;
|
state_image?: string;
|
||||||
camera_image?: string;
|
camera_image?: string;
|
||||||
filter?: string;
|
filter?: string;
|
||||||
state_filter?: string;
|
state_filter?: string;
|
||||||
aspect_ratio?: string;
|
aspect_ratio?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class HuiImageElement extends hassLocalizeLitMixin(LitElement)
|
export class HuiImageElement extends hassLocalizeLitMixin(LitElement)
|
||||||
implements LovelaceElement {
|
implements LovelaceElement {
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
private _config?: Config;
|
private _config?: Config;
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return { hass: {}, _config: {} };
|
return { hass: {}, _config: {} };
|
||||||
}
|
}
|
||||||
|
|
||||||
public setConfig(config: Config): void {
|
public setConfig(config: Config): void {
|
||||||
if (!config) {
|
if (!config) {
|
||||||
throw Error("Error in element configuration");
|
throw Error("Error in element configuration");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.classList.toggle("clickable", config.tap_action !== "none");
|
this.classList.toggle("clickable", config.tap_action !== "none");
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this._config) {
|
if (!this._config) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${this.renderStyle()}
|
${this.renderStyle()}
|
||||||
<hui-image
|
<hui-image
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.entity="${this._config.entity}"
|
.entity="${this._config.entity}"
|
||||||
.image="${this._config.image}"
|
.image="${this._config.image}"
|
||||||
.stateImage=${this._config.state_image}
|
.stateImage=${this._config.state_image}
|
||||||
.cameraImage=${this._config.camera_image}
|
.cameraImage=${this._config.camera_image}
|
||||||
.filter=${this._config.filter}
|
.filter=${this._config.filter}
|
||||||
.stateFilter=${this._config.state_filter}
|
.stateFilter=${this._config.state_filter}
|
||||||
.title="${computeTooltip(this.hass!, this._config)}"
|
.title="${computeTooltip(this.hass!, this._config)}"
|
||||||
.aspectRatio="${this._config.aspect_ratio}"
|
.aspectRatio="${this._config.aspect_ratio}"
|
||||||
@ha-click="${this._handleClick}"
|
@ha-click="${this._handleClick}"
|
||||||
@ha-hold="${this._handleHold}"
|
@ha-hold="${this._handleHold}"
|
||||||
.longPress="${longPress()}"
|
.longPress="${longPress()}"
|
||||||
></hui-image>
|
></hui-image>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderStyle(): TemplateResult {
|
private renderStyle(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
:host(.clickable) {
|
:host(.clickable) {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
-webkit-touch-callout: none !important;
|
-webkit-touch-callout: none !important;
|
||||||
}
|
}
|
||||||
hui-image {
|
hui-image {
|
||||||
-webkit-user-select: none !important;
|
-webkit-user-select: none !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleClick() {
|
private _handleClick() {
|
||||||
handleClick(this, this.hass!, this._config!, false);
|
handleClick(this, this.hass!, this._config!, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleHold() {
|
private _handleHold() {
|
||||||
handleClick(this, this.hass!, this._config!, true);
|
handleClick(this, this.hass!, this._config!, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-image-element": HuiImageElement;
|
"hui-image-element": HuiImageElement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-image-element", HuiImageElement);
|
customElements.define("hui-image-element", HuiImageElement);
|
||||||
|
|||||||
@@ -1,74 +1,74 @@
|
|||||||
import { html, LitElement } from "@polymer/lit-element";
|
import { html, LitElement } from "@polymer/lit-element";
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
import "../../../components/buttons/ha-call-service-button";
|
import "../../../components/buttons/ha-call-service-button";
|
||||||
|
|
||||||
import { LovelaceElement, LovelaceElementConfig } from "./types";
|
import { LovelaceElement, LovelaceElementConfig } from "./types";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
|
|
||||||
export class HuiServiceButtonElement extends LitElement
|
export class HuiServiceButtonElement extends LitElement
|
||||||
implements LovelaceElement {
|
implements LovelaceElement {
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
private _config?: LovelaceElementConfig;
|
private _config?: LovelaceElementConfig;
|
||||||
private _domain?: string;
|
private _domain?: string;
|
||||||
private _service?: string;
|
private _service?: string;
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return { _config: {} };
|
return { _config: {} };
|
||||||
}
|
}
|
||||||
|
|
||||||
public setConfig(config: LovelaceElementConfig): void {
|
public setConfig(config: LovelaceElementConfig): void {
|
||||||
if (!config || !config.service) {
|
if (!config || !config.service) {
|
||||||
throw Error("Invalid Configuration: 'service' required");
|
throw Error("Invalid Configuration: 'service' required");
|
||||||
}
|
}
|
||||||
|
|
||||||
[this._domain, this._service] = config.service.split(".", 2);
|
[this._domain, this._service] = config.service.split(".", 2);
|
||||||
|
|
||||||
if (!this._domain) {
|
if (!this._domain) {
|
||||||
throw Error("Invalid Configuration: 'service' does not have a domain");
|
throw Error("Invalid Configuration: 'service' does not have a domain");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._service) {
|
if (!this._service) {
|
||||||
throw Error(
|
throw Error(
|
||||||
"Invalid Configuration: 'service' does not have a service name"
|
"Invalid Configuration: 'service' does not have a service name"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this._config) {
|
if (!this._config) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${this.renderStyle()}
|
${this.renderStyle()}
|
||||||
<ha-call-service-button
|
<ha-call-service-button
|
||||||
.hass="${this.hass}"
|
.hass="${this.hass}"
|
||||||
.domain="${this._domain}"
|
.domain="${this._domain}"
|
||||||
.service="${this._service}"
|
.service="${this._service}"
|
||||||
.service-data="${this._config.service_data}"
|
.service-data="${this._config.service_data}"
|
||||||
>${this._config.title}</ha-call-service-button>
|
>${this._config.title}</ha-call-service-button>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderStyle(): TemplateResult {
|
private renderStyle(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
ha-call-service-button {
|
ha-call-service-button {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-service-button-element": HuiServiceButtonElement;
|
"hui-service-button-element": HuiServiceButtonElement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-service-button-element", HuiServiceButtonElement);
|
customElements.define("hui-service-button-element", HuiServiceButtonElement);
|
||||||
|
|||||||
@@ -1,53 +1,53 @@
|
|||||||
import { html, LitElement } from "@polymer/lit-element";
|
import { html, LitElement } from "@polymer/lit-element";
|
||||||
|
|
||||||
import "../../../components/entity/ha-state-label-badge";
|
import "../../../components/entity/ha-state-label-badge";
|
||||||
|
|
||||||
import computeStateName from "../../../common/entity/compute_state_name";
|
import computeStateName from "../../../common/entity/compute_state_name";
|
||||||
import { LovelaceElement, LovelaceElementConfig } from "./types";
|
import { LovelaceElement, LovelaceElementConfig } from "./types";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
export class HuiStateBadgeElement extends LitElement
|
export class HuiStateBadgeElement extends LitElement
|
||||||
implements LovelaceElement {
|
implements LovelaceElement {
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
private _config?: LovelaceElementConfig;
|
private _config?: LovelaceElementConfig;
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return { hass: {}, _config: {} };
|
return { hass: {}, _config: {} };
|
||||||
}
|
}
|
||||||
|
|
||||||
public setConfig(config: LovelaceElementConfig): void {
|
public setConfig(config: LovelaceElementConfig): void {
|
||||||
if (!config.entity) {
|
if (!config.entity) {
|
||||||
throw Error("Invalid Configuration: 'entity' required");
|
throw Error("Invalid Configuration: 'entity' required");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (
|
if (
|
||||||
!this._config ||
|
!this._config ||
|
||||||
!this.hass ||
|
!this.hass ||
|
||||||
!this.hass.states[this._config.entity!]
|
!this.hass.states[this._config.entity!]
|
||||||
) {
|
) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = this.hass.states[this._config.entity!];
|
const state = this.hass.states[this._config.entity!];
|
||||||
return html`
|
return html`
|
||||||
<ha-state-label-badge
|
<ha-state-label-badge
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.state=${state}
|
.state=${state}
|
||||||
.title="${computeStateName(state)}"
|
.title="${computeStateName(state)}"
|
||||||
></ha-state-label-badge>
|
></ha-state-label-badge>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-state-badge-element": HuiStateBadgeElement;
|
"hui-state-badge-element": HuiStateBadgeElement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-state-badge-element", HuiStateBadgeElement);
|
customElements.define("hui-state-badge-element", HuiStateBadgeElement);
|
||||||
|
|||||||
@@ -1,77 +1,77 @@
|
|||||||
import { html, LitElement } from "@polymer/lit-element";
|
import { html, LitElement } from "@polymer/lit-element";
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
import "../../../components/entity/state-badge";
|
import "../../../components/entity/state-badge";
|
||||||
|
|
||||||
import { computeTooltip } from "../common/compute-tooltip";
|
import { computeTooltip } from "../common/compute-tooltip";
|
||||||
import { handleClick } from "../common/handle-click";
|
import { handleClick } from "../common/handle-click";
|
||||||
import { longPress } from "../common/directives/long-press-directive";
|
import { longPress } from "../common/directives/long-press-directive";
|
||||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
||||||
import { LovelaceElement, LovelaceElementConfig } from "./types";
|
import { LovelaceElement, LovelaceElementConfig } from "./types";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
|
|
||||||
export class HuiStateIconElement extends hassLocalizeLitMixin(LitElement)
|
export class HuiStateIconElement extends hassLocalizeLitMixin(LitElement)
|
||||||
implements LovelaceElement {
|
implements LovelaceElement {
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
private _config?: LovelaceElementConfig;
|
private _config?: LovelaceElementConfig;
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return { hass: {}, _config: {} };
|
return { hass: {}, _config: {} };
|
||||||
}
|
}
|
||||||
|
|
||||||
public setConfig(config: LovelaceElementConfig): void {
|
public setConfig(config: LovelaceElementConfig): void {
|
||||||
if (!config.entity) {
|
if (!config.entity) {
|
||||||
throw Error("Invalid Configuration: 'entity' required");
|
throw Error("Invalid Configuration: 'entity' required");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (
|
if (
|
||||||
!this._config ||
|
!this._config ||
|
||||||
!this.hass ||
|
!this.hass ||
|
||||||
!this.hass.states[this._config.entity!]
|
!this.hass.states[this._config.entity!]
|
||||||
) {
|
) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = this.hass!.states[this._config.entity!];
|
const state = this.hass!.states[this._config.entity!];
|
||||||
return html`
|
return html`
|
||||||
${this.renderStyle()}
|
${this.renderStyle()}
|
||||||
<state-badge
|
<state-badge
|
||||||
.stateObj=${state}
|
.stateObj=${state}
|
||||||
.title="${computeTooltip(this.hass!, this._config)}"
|
.title="${computeTooltip(this.hass!, this._config)}"
|
||||||
@ha-click="${this._handleClick}"
|
@ha-click="${this._handleClick}"
|
||||||
@ha-hold="${this._handleHold}"
|
@ha-hold="${this._handleHold}"
|
||||||
.longPress="${longPress()}"
|
.longPress="${longPress()}"
|
||||||
></state-badge>
|
></state-badge>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderStyle(): TemplateResult {
|
private renderStyle(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
:host {
|
:host {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleClick() {
|
private _handleClick() {
|
||||||
handleClick(this, this.hass!, this._config!, false);
|
handleClick(this, this.hass!, this._config!, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleHold() {
|
private _handleHold() {
|
||||||
handleClick(this, this.hass!, this._config!, true);
|
handleClick(this, this.hass!, this._config!, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-state-icon-element": HuiStateIconElement;
|
"hui-state-icon-element": HuiStateIconElement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-state-icon-element", HuiStateIconElement);
|
customElements.define("hui-state-icon-element", HuiStateIconElement);
|
||||||
|
|||||||
@@ -1,78 +1,78 @@
|
|||||||
import { html, LitElement } from "@polymer/lit-element";
|
import { html, LitElement } from "@polymer/lit-element";
|
||||||
|
|
||||||
import "../../../components/entity/ha-state-label-badge";
|
import "../../../components/entity/ha-state-label-badge";
|
||||||
|
|
||||||
import computeStateDisplay from "../../../common/entity/compute_state_display";
|
import computeStateDisplay from "../../../common/entity/compute_state_display";
|
||||||
import { computeTooltip } from "../common/compute-tooltip";
|
import { computeTooltip } from "../common/compute-tooltip";
|
||||||
import { handleClick } from "../common/handle-click";
|
import { handleClick } from "../common/handle-click";
|
||||||
import { longPress } from "../common/directives/long-press-directive";
|
import { longPress } from "../common/directives/long-press-directive";
|
||||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
||||||
import { LovelaceElement, LovelaceElementConfig } from "./types";
|
import { LovelaceElement, LovelaceElementConfig } from "./types";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
interface Config extends LovelaceElementConfig {
|
interface Config extends LovelaceElementConfig {
|
||||||
prefix?: string;
|
prefix?: string;
|
||||||
suffix?: string;
|
suffix?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
class HuiStateLabelElement extends hassLocalizeLitMixin(LitElement)
|
class HuiStateLabelElement extends hassLocalizeLitMixin(LitElement)
|
||||||
implements LovelaceElement {
|
implements LovelaceElement {
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
private _config?: Config;
|
private _config?: Config;
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return { hass: {}, _config: {} };
|
return { hass: {}, _config: {} };
|
||||||
}
|
}
|
||||||
|
|
||||||
public setConfig(config: Config): void {
|
public setConfig(config: Config): void {
|
||||||
if (!config.entity) {
|
if (!config.entity) {
|
||||||
throw Error("Invalid Configuration: 'entity' required");
|
throw Error("Invalid Configuration: 'entity' required");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this._config) {
|
if (!this._config) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = this.hass!.states[this._config.entity!];
|
const state = this.hass!.states[this._config.entity!];
|
||||||
return html`
|
return html`
|
||||||
${this.renderStyle()}
|
${this.renderStyle()}
|
||||||
<div
|
<div
|
||||||
.title="${computeTooltip(this.hass!, this._config)}"
|
.title="${computeTooltip(this.hass!, this._config)}"
|
||||||
@ha-click="${() => handleClick(this, this.hass!, this._config!, false)}"
|
@ha-click="${() => handleClick(this, this.hass!, this._config!, false)}"
|
||||||
@ha-hold="${() => handleClick(this, this.hass!, this._config!, true)}"
|
@ha-hold="${() => handleClick(this, this.hass!, this._config!, true)}"
|
||||||
.longPress="${longPress()}"
|
.longPress="${longPress()}"
|
||||||
>
|
>
|
||||||
${this._config.prefix}${
|
${this._config.prefix}${
|
||||||
state ? computeStateDisplay(this.localize, state) : "-"
|
state ? computeStateDisplay(this.localize, state) : "-"
|
||||||
}${this._config.suffix}
|
}${this._config.suffix}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderStyle(): TemplateResult {
|
private renderStyle(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
:host {
|
:host {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-state-label-element": HuiStateLabelElement;
|
"hui-state-label-element": HuiStateLabelElement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-state-label-element", HuiStateLabelElement);
|
customElements.define("hui-state-label-element", HuiStateLabelElement);
|
||||||
|
|||||||
@@ -1,65 +1,65 @@
|
|||||||
import { html, LitElement } from "@polymer/lit-element";
|
import { html, LitElement } from "@polymer/lit-element";
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
import "../../../components/ha-climate-state";
|
import "../../../components/ha-climate-state";
|
||||||
import "../components/hui-generic-entity-row";
|
import "../components/hui-generic-entity-row";
|
||||||
|
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { EntityRow, EntityConfig } from "./types";
|
import { EntityRow, EntityConfig } from "./types";
|
||||||
|
|
||||||
class HuiClimateEntityRow extends LitElement implements EntityRow {
|
class HuiClimateEntityRow extends LitElement implements EntityRow {
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
private _config?: EntityConfig;
|
private _config?: EntityConfig;
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: {},
|
hass: {},
|
||||||
_config: {},
|
_config: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public setConfig(config: EntityConfig): void {
|
public setConfig(config: EntityConfig): void {
|
||||||
if (!config || !config.entity) {
|
if (!config || !config.entity) {
|
||||||
throw new Error("Invalid Configuration: 'entity' required");
|
throw new Error("Invalid Configuration: 'entity' required");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this.hass || !this._config) {
|
if (!this.hass || !this._config) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${this.renderStyle()}
|
${this.renderStyle()}
|
||||||
<hui-generic-entity-row
|
<hui-generic-entity-row
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.config=${this._config}
|
.config=${this._config}
|
||||||
>
|
>
|
||||||
<ha-climate-state
|
<ha-climate-state
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.stateObj=${this.hass.states[this._config.entity]}
|
.stateObj=${this.hass.states[this._config.entity]}
|
||||||
></ha-climate-state>
|
></ha-climate-state>
|
||||||
</hui-generic-entity-row>
|
</hui-generic-entity-row>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderStyle(): TemplateResult {
|
private renderStyle(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
ha-climate-state {
|
ha-climate-state {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-climate-entity-row": HuiClimateEntityRow;
|
"hui-climate-entity-row": HuiClimateEntityRow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-climate-entity-row", HuiClimateEntityRow);
|
customElements.define("hui-climate-entity-row", HuiClimateEntityRow);
|
||||||
|
|||||||
@@ -1,74 +1,74 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import "../components/hui-generic-entity-row";
|
import "../components/hui-generic-entity-row";
|
||||||
import "../../../components/ha-cover-controls";
|
import "../../../components/ha-cover-controls";
|
||||||
import "../../../components/ha-cover-tilt-controls";
|
import "../../../components/ha-cover-tilt-controls";
|
||||||
import CoverEntity from "../../../util/cover-model";
|
import CoverEntity from "../../../util/cover-model";
|
||||||
|
|
||||||
class HuiCoverEntityRow extends PolymerElement {
|
class HuiCoverEntityRow extends PolymerElement {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
${this.styleTemplate}
|
${this.styleTemplate}
|
||||||
<hui-generic-entity-row
|
<hui-generic-entity-row
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
config="[[_config]]"
|
config="[[_config]]"
|
||||||
>
|
>
|
||||||
${this.coverControlTemplate}
|
${this.coverControlTemplate}
|
||||||
</hui-generic-entity-row>
|
</hui-generic-entity-row>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styleTemplate() {
|
static get styleTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
ha-cover-controls,
|
ha-cover-controls,
|
||||||
ha-cover-tilt-controls {
|
ha-cover-tilt-controls {
|
||||||
margin-right: -.57em;
|
margin-right: -.57em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get coverControlTemplate() {
|
static get coverControlTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<template is="dom-if" if="[[!_entityObj.isTiltOnly]]">
|
<template is="dom-if" if="[[!_entityObj.isTiltOnly]]">
|
||||||
<ha-cover-controls hass="[[hass]]" state-obj="[[_stateObj]]"></ha-cover-controls>
|
<ha-cover-controls hass="[[hass]]" state-obj="[[_stateObj]]"></ha-cover-controls>
|
||||||
</template>
|
</template>
|
||||||
<template is="dom-if" if="[[_entityObj.isTiltOnly]]">
|
<template is="dom-if" if="[[_entityObj.isTiltOnly]]">
|
||||||
<ha-cover-tilt-controls hass="[[hass]]" state-obj="[[_stateObj]]"></ha-cover-tilt-controls>
|
<ha-cover-tilt-controls hass="[[hass]]" state-obj="[[_stateObj]]"></ha-cover-tilt-controls>
|
||||||
</template>
|
</template>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
_config: Object,
|
_config: Object,
|
||||||
_stateObj: {
|
_stateObj: {
|
||||||
type: Object,
|
type: Object,
|
||||||
computed: "_computeStateObj(hass.states, _config.entity)",
|
computed: "_computeStateObj(hass.states, _config.entity)",
|
||||||
},
|
},
|
||||||
_entityObj: {
|
_entityObj: {
|
||||||
type: Object,
|
type: Object,
|
||||||
computed: "_computeEntityObj(hass, _stateObj)",
|
computed: "_computeEntityObj(hass, _stateObj)",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeStateObj(states, entityId) {
|
_computeStateObj(states, entityId) {
|
||||||
return states && entityId in states ? states[entityId] : null;
|
return states && entityId in states ? states[entityId] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeEntityObj(hass, stateObj) {
|
_computeEntityObj(hass, stateObj) {
|
||||||
return stateObj ? new CoverEntity(hass, stateObj) : null;
|
return stateObj ? new CoverEntity(hass, stateObj) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
if (!config || !config.entity) {
|
if (!config || !config.entity) {
|
||||||
throw new Error("Entity not configured.");
|
throw new Error("Entity not configured.");
|
||||||
}
|
}
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define("hui-cover-entity-row", HuiCoverEntityRow);
|
customElements.define("hui-cover-entity-row", HuiCoverEntityRow);
|
||||||
|
|||||||
@@ -1,78 +1,78 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import "../components/hui-generic-entity-row";
|
import "../components/hui-generic-entity-row";
|
||||||
import "../../../components/entity/ha-entity-toggle";
|
import "../../../components/entity/ha-entity-toggle";
|
||||||
|
|
||||||
import computeStateDisplay from "../../../common/entity/compute_state_display";
|
import computeStateDisplay from "../../../common/entity/compute_state_display";
|
||||||
import { DOMAINS_TOGGLE } from "../../../common/const";
|
import { DOMAINS_TOGGLE } from "../../../common/const";
|
||||||
import LocalizeMixin from "../../../mixins/localize-mixin";
|
import LocalizeMixin from "../../../mixins/localize-mixin";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin LocalizeMixin
|
* @appliesMixin LocalizeMixin
|
||||||
*/
|
*/
|
||||||
class HuiGroupEntityRow extends LocalizeMixin(PolymerElement) {
|
class HuiGroupEntityRow extends LocalizeMixin(PolymerElement) {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<hui-generic-entity-row
|
<hui-generic-entity-row
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
config="[[_config]]"
|
config="[[_config]]"
|
||||||
>
|
>
|
||||||
${this.groupControlTemplate}
|
${this.groupControlTemplate}
|
||||||
</hui-generic-entity-row>
|
</hui-generic-entity-row>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get groupControlTemplate() {
|
static get groupControlTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<template is="dom-if" if="[[_canToggle]]">
|
<template is="dom-if" if="[[_canToggle]]">
|
||||||
<ha-entity-toggle
|
<ha-entity-toggle
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
state-obj="[[_stateObj]]"
|
state-obj="[[_stateObj]]"
|
||||||
></ha-entity-toggle>
|
></ha-entity-toggle>
|
||||||
</template>
|
</template>
|
||||||
<template is="dom-if" if="[[!_canToggle]]">
|
<template is="dom-if" if="[[!_canToggle]]">
|
||||||
<div>
|
<div>
|
||||||
[[_computeState(_stateObj)]]
|
[[_computeState(_stateObj)]]
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
_config: Object,
|
_config: Object,
|
||||||
_stateObj: {
|
_stateObj: {
|
||||||
type: Object,
|
type: Object,
|
||||||
computed: "_computeStateObj(hass.states, _config.entity)",
|
computed: "_computeStateObj(hass.states, _config.entity)",
|
||||||
},
|
},
|
||||||
_canToggle: {
|
_canToggle: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
computed: "_computeCanToggle(_stateObj.attributes.entity_id)",
|
computed: "_computeCanToggle(_stateObj.attributes.entity_id)",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
if (!config || !config.entity) {
|
if (!config || !config.entity) {
|
||||||
throw new Error("Entity not configured.");
|
throw new Error("Entity not configured.");
|
||||||
}
|
}
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeStateObj(states, entityId) {
|
_computeStateObj(states, entityId) {
|
||||||
return states && entityId in states ? states[entityId] : null;
|
return states && entityId in states ? states[entityId] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeCanToggle(entityIds) {
|
_computeCanToggle(entityIds) {
|
||||||
return entityIds.some((entityId) =>
|
return entityIds.some((entityId) =>
|
||||||
DOMAINS_TOGGLE.has(entityId.split(".", 1)[0])
|
DOMAINS_TOGGLE.has(entityId.split(".", 1)[0])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeState(stateObj) {
|
_computeState(stateObj) {
|
||||||
return computeStateDisplay(this.localize, stateObj);
|
return computeStateDisplay(this.localize, stateObj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define("hui-group-entity-row", HuiGroupEntityRow);
|
customElements.define("hui-group-entity-row", HuiGroupEntityRow);
|
||||||
|
|||||||
@@ -1,170 +1,170 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import { IronResizableBehavior } from "@polymer/iron-resizable-behavior/iron-resizable-behavior";
|
import { IronResizableBehavior } from "@polymer/iron-resizable-behavior/iron-resizable-behavior";
|
||||||
import { mixinBehaviors } from "@polymer/polymer/lib/legacy/class";
|
import { mixinBehaviors } from "@polymer/polymer/lib/legacy/class";
|
||||||
|
|
||||||
import "../components/hui-generic-entity-row";
|
import "../components/hui-generic-entity-row";
|
||||||
import "../../../components/ha-slider";
|
import "../../../components/ha-slider";
|
||||||
|
|
||||||
class HuiInputNumberEntityRow extends mixinBehaviors(
|
class HuiInputNumberEntityRow extends mixinBehaviors(
|
||||||
[IronResizableBehavior],
|
[IronResizableBehavior],
|
||||||
PolymerElement
|
PolymerElement
|
||||||
) {
|
) {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
${this.styleTemplate}
|
${this.styleTemplate}
|
||||||
<hui-generic-entity-row
|
<hui-generic-entity-row
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
config="[[_config]]"
|
config="[[_config]]"
|
||||||
id="input_number_card"
|
id="input_number_card"
|
||||||
>
|
>
|
||||||
${this.inputNumberControlTemplate}
|
${this.inputNumberControlTemplate}
|
||||||
</hui-generic-entity-row>
|
</hui-generic-entity-row>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styleTemplate() {
|
static get styleTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
.flex {
|
.flex {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
.state {
|
.state {
|
||||||
min-width: 45px;
|
min-width: 45px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
paper-input {
|
paper-input {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get inputNumberControlTemplate() {
|
static get inputNumberControlTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<div>
|
<div>
|
||||||
<template is="dom-if" if="[[_equals(_stateObj.attributes.mode, 'slider')]]">
|
<template is="dom-if" if="[[_equals(_stateObj.attributes.mode, 'slider')]]">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<ha-slider
|
<ha-slider
|
||||||
min="[[_min]]"
|
min="[[_min]]"
|
||||||
max="[[_max]]"
|
max="[[_max]]"
|
||||||
value="{{_value}}"
|
value="{{_value}}"
|
||||||
step="[[_step]]"
|
step="[[_step]]"
|
||||||
pin
|
pin
|
||||||
on-change="_selectedValueChanged"
|
on-change="_selectedValueChanged"
|
||||||
ignore-bar-touch
|
ignore-bar-touch
|
||||||
></ha-slider>
|
></ha-slider>
|
||||||
<span class="state">[[_value]] [[_stateObj.attributes.unit_of_measurement]]</span>
|
<span class="state">[[_value]] [[_stateObj.attributes.unit_of_measurement]]</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template is="dom-if" if="[[_equals(_stateObj.attributes.mode, 'box')]]">
|
<template is="dom-if" if="[[_equals(_stateObj.attributes.mode, 'box')]]">
|
||||||
<paper-input
|
<paper-input
|
||||||
no-label-float
|
no-label-float
|
||||||
auto-validate
|
auto-validate
|
||||||
pattern="[0-9]+([\\.][0-9]+)?"
|
pattern="[0-9]+([\\.][0-9]+)?"
|
||||||
step="[[_step]]"
|
step="[[_step]]"
|
||||||
min="[[_min]]"
|
min="[[_min]]"
|
||||||
max="[[_max]]"
|
max="[[_max]]"
|
||||||
value="{{_value}}"
|
value="{{_value}}"
|
||||||
type="number"
|
type="number"
|
||||||
on-change="_selectedValueChanged"
|
on-change="_selectedValueChanged"
|
||||||
></paper-input>
|
></paper-input>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
_config: Object,
|
_config: Object,
|
||||||
_stateObj: {
|
_stateObj: {
|
||||||
type: Object,
|
type: Object,
|
||||||
computed: "_computeStateObj(hass.states, _config.entity)",
|
computed: "_computeStateObj(hass.states, _config.entity)",
|
||||||
observer: "_stateObjChanged",
|
observer: "_stateObjChanged",
|
||||||
},
|
},
|
||||||
_min: {
|
_min: {
|
||||||
type: Number,
|
type: Number,
|
||||||
value: 0,
|
value: 0,
|
||||||
},
|
},
|
||||||
_max: {
|
_max: {
|
||||||
type: Number,
|
type: Number,
|
||||||
value: 100,
|
value: 100,
|
||||||
},
|
},
|
||||||
_step: Number,
|
_step: Number,
|
||||||
_value: Number,
|
_value: Number,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
ready() {
|
ready() {
|
||||||
super.ready();
|
super.ready();
|
||||||
if (typeof ResizeObserver === "function") {
|
if (typeof ResizeObserver === "function") {
|
||||||
const ro = new ResizeObserver((entries) => {
|
const ro = new ResizeObserver((entries) => {
|
||||||
entries.forEach(() => {
|
entries.forEach(() => {
|
||||||
this._hiddenState();
|
this._hiddenState();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
ro.observe(this.$.input_number_card);
|
ro.observe(this.$.input_number_card);
|
||||||
} else {
|
} else {
|
||||||
this.addEventListener("iron-resize", this._hiddenState);
|
this.addEventListener("iron-resize", this._hiddenState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_equals(a, b) {
|
_equals(a, b) {
|
||||||
return a === b;
|
return a === b;
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeStateObj(states, entityId) {
|
_computeStateObj(states, entityId) {
|
||||||
return states && entityId in states ? states[entityId] : null;
|
return states && entityId in states ? states[entityId] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
if (!config || !config.entity) {
|
if (!config || !config.entity) {
|
||||||
throw new Error("Entity not configured.");
|
throw new Error("Entity not configured.");
|
||||||
}
|
}
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
_hiddenState() {
|
_hiddenState() {
|
||||||
if (
|
if (
|
||||||
!this.$ ||
|
!this.$ ||
|
||||||
!this._stateObj ||
|
!this._stateObj ||
|
||||||
this._stateObj.attributes.mode !== "slider"
|
this._stateObj.attributes.mode !== "slider"
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
const width = this.$.input_number_card.offsetWidth;
|
const width = this.$.input_number_card.offsetWidth;
|
||||||
const stateEl = this.shadowRoot.querySelector(".state");
|
const stateEl = this.shadowRoot.querySelector(".state");
|
||||||
if (!stateEl) return;
|
if (!stateEl) return;
|
||||||
stateEl.hidden = width <= 350;
|
stateEl.hidden = width <= 350;
|
||||||
}
|
}
|
||||||
|
|
||||||
_stateObjChanged(stateObj, oldStateObj) {
|
_stateObjChanged(stateObj, oldStateObj) {
|
||||||
if (!stateObj) return;
|
if (!stateObj) return;
|
||||||
|
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
_min: Number(stateObj.attributes.min),
|
_min: Number(stateObj.attributes.min),
|
||||||
_max: Number(stateObj.attributes.max),
|
_max: Number(stateObj.attributes.max),
|
||||||
_step: Number(stateObj.attributes.step),
|
_step: Number(stateObj.attributes.step),
|
||||||
_value: Number(stateObj.state),
|
_value: Number(stateObj.state),
|
||||||
});
|
});
|
||||||
if (
|
if (
|
||||||
oldStateObj &&
|
oldStateObj &&
|
||||||
stateObj.attributes.mode === "slider" &&
|
stateObj.attributes.mode === "slider" &&
|
||||||
oldStateObj.attributes.mode !== "slider"
|
oldStateObj.attributes.mode !== "slider"
|
||||||
) {
|
) {
|
||||||
this._hiddenState();
|
this._hiddenState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_selectedValueChanged() {
|
_selectedValueChanged() {
|
||||||
if (this._value === Number(this._stateObj.state)) return;
|
if (this._value === Number(this._stateObj.state)) return;
|
||||||
|
|
||||||
this.hass.callService("input_number", "set_value", {
|
this.hass.callService("input_number", "set_value", {
|
||||||
value: this._value,
|
value: this._value,
|
||||||
entity_id: this._stateObj.entity_id,
|
entity_id: this._stateObj.entity_id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define("hui-input-number-entity-row", HuiInputNumberEntityRow);
|
customElements.define("hui-input-number-entity-row", HuiInputNumberEntityRow);
|
||||||
|
|||||||
@@ -1,107 +1,107 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||||
import "@polymer/paper-item/paper-item";
|
import "@polymer/paper-item/paper-item";
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
import "@polymer/paper-listbox/paper-listbox";
|
||||||
|
|
||||||
import "../../../components/entity/state-badge";
|
import "../../../components/entity/state-badge";
|
||||||
|
|
||||||
import computeStateName from "../../../common/entity/compute_state_name";
|
import computeStateName from "../../../common/entity/compute_state_name";
|
||||||
|
|
||||||
import EventsMixin from "../../../mixins/events-mixin";
|
import EventsMixin from "../../../mixins/events-mixin";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin EventsMixin
|
* @appliesMixin EventsMixin
|
||||||
*/
|
*/
|
||||||
class HuiInputSelectEntityRow extends EventsMixin(PolymerElement) {
|
class HuiInputSelectEntityRow extends EventsMixin(PolymerElement) {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
${this.styleTemplate}
|
${this.styleTemplate}
|
||||||
<template is="dom-if" if="[[_stateObj]]">
|
<template is="dom-if" if="[[_stateObj]]">
|
||||||
<state-badge state-obj="[[_stateObj]]"></state-badge>
|
<state-badge state-obj="[[_stateObj]]"></state-badge>
|
||||||
<paper-dropdown-menu on-click="_stopPropagation" selected-item-label="{{_selected}}" label="[[_computeName(_config.name, _stateObj)]]">
|
<paper-dropdown-menu on-click="_stopPropagation" selected-item-label="{{_selected}}" label="[[_computeName(_config.name, _stateObj)]]">
|
||||||
<paper-listbox slot="dropdown-content" selected="[[_computeSelected(_stateObj)]]">
|
<paper-listbox slot="dropdown-content" selected="[[_computeSelected(_stateObj)]]">
|
||||||
<template is="dom-repeat" items="[[_stateObj.attributes.options]]">
|
<template is="dom-repeat" items="[[_stateObj.attributes.options]]">
|
||||||
<paper-item>[[item]]</paper-item>
|
<paper-item>[[item]]</paper-item>
|
||||||
</template>
|
</template>
|
||||||
</paper-listbox>
|
</paper-listbox>
|
||||||
</paper-dropdown-menu>
|
</paper-dropdown-menu>
|
||||||
</template>
|
</template>
|
||||||
<template is="dom-if" if="[[!_stateObj]]">
|
<template is="dom-if" if="[[!_stateObj]]">
|
||||||
<div class="not-found">
|
<div class="not-found">
|
||||||
Entity not available: [[_config.entity]]
|
Entity not available: [[_config.entity]]
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styleTemplate() {
|
static get styleTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
:host {
|
:host {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
paper-dropdown-menu {
|
paper-dropdown-menu {
|
||||||
margin-left: 16px;
|
margin-left: 16px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
.not-found {
|
.not-found {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background-color: yellow;
|
background-color: yellow;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
_config: Object,
|
_config: Object,
|
||||||
_stateObj: {
|
_stateObj: {
|
||||||
type: Object,
|
type: Object,
|
||||||
computed: "_computeStateObj(hass.states, _config.entity)",
|
computed: "_computeStateObj(hass.states, _config.entity)",
|
||||||
},
|
},
|
||||||
_selected: {
|
_selected: {
|
||||||
type: String,
|
type: String,
|
||||||
observer: "_selectedChanged",
|
observer: "_selectedChanged",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
if (!config || !config.entity) {
|
if (!config || !config.entity) {
|
||||||
throw new Error("Entity not configured.");
|
throw new Error("Entity not configured.");
|
||||||
}
|
}
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeStateObj(states, entityId) {
|
_computeStateObj(states, entityId) {
|
||||||
return states && entityId in states ? states[entityId] : null;
|
return states && entityId in states ? states[entityId] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeName(name, stateObj) {
|
_computeName(name, stateObj) {
|
||||||
return name || computeStateName(stateObj);
|
return name || computeStateName(stateObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeSelected(stateObj) {
|
_computeSelected(stateObj) {
|
||||||
return stateObj.attributes.options.indexOf(stateObj.state);
|
return stateObj.attributes.options.indexOf(stateObj.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
_selectedChanged(option) {
|
_selectedChanged(option) {
|
||||||
// Selected Option will transition to '' before transitioning to new value
|
// Selected Option will transition to '' before transitioning to new value
|
||||||
if (option === "" || option === this._stateObj.state) {
|
if (option === "" || option === this._stateObj.state) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.hass.callService("input_select", "select_option", {
|
this.hass.callService("input_select", "select_option", {
|
||||||
option: option,
|
option: option,
|
||||||
entity_id: this._stateObj.entity_id,
|
entity_id: this._stateObj.entity_id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_stopPropagation(ev) {
|
_stopPropagation(ev) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define("hui-input-select-entity-row", HuiInputSelectEntityRow);
|
customElements.define("hui-input-select-entity-row", HuiInputSelectEntityRow);
|
||||||
|
|||||||
@@ -1,73 +1,73 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
|
|
||||||
import "../components/hui-generic-entity-row";
|
import "../components/hui-generic-entity-row";
|
||||||
|
|
||||||
class HuiInputTextEntityRow extends PolymerElement {
|
class HuiInputTextEntityRow extends PolymerElement {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<hui-generic-entity-row
|
<hui-generic-entity-row
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
config="[[_config]]"
|
config="[[_config]]"
|
||||||
>
|
>
|
||||||
${this.inputTextControlTemplate}
|
${this.inputTextControlTemplate}
|
||||||
</hui-generic-entity-row>
|
</hui-generic-entity-row>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get inputTextControlTemplate() {
|
static get inputTextControlTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<paper-input
|
<paper-input
|
||||||
no-label-float
|
no-label-float
|
||||||
minlength="[[_stateObj.attributes.min]]"
|
minlength="[[_stateObj.attributes.min]]"
|
||||||
maxlength="[[_stateObj.attributes.max]]"
|
maxlength="[[_stateObj.attributes.max]]"
|
||||||
value="{{_value}}"
|
value="{{_value}}"
|
||||||
auto-validate="[[_stateObj.attributes.pattern]]"
|
auto-validate="[[_stateObj.attributes.pattern]]"
|
||||||
pattern="[[_stateObj.attributes.pattern]]"
|
pattern="[[_stateObj.attributes.pattern]]"
|
||||||
type="[[_stateObj.attributes.mode]]"
|
type="[[_stateObj.attributes.mode]]"
|
||||||
on-change="_selectedValueChanged"
|
on-change="_selectedValueChanged"
|
||||||
placeholder="(empty value)"
|
placeholder="(empty value)"
|
||||||
></paper-input>
|
></paper-input>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
_config: Object,
|
_config: Object,
|
||||||
_stateObj: {
|
_stateObj: {
|
||||||
type: Object,
|
type: Object,
|
||||||
computed: "_computeStateObj(hass.states, _config.entity)",
|
computed: "_computeStateObj(hass.states, _config.entity)",
|
||||||
observer: "_stateObjChanged",
|
observer: "_stateObjChanged",
|
||||||
},
|
},
|
||||||
_value: String,
|
_value: String,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeStateObj(states, entityId) {
|
_computeStateObj(states, entityId) {
|
||||||
return states && entityId in states ? states[entityId] : null;
|
return states && entityId in states ? states[entityId] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
if (!config || !config.entity) {
|
if (!config || !config.entity) {
|
||||||
throw new Error("Entity not configured.");
|
throw new Error("Entity not configured.");
|
||||||
}
|
}
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
_stateObjChanged(stateObj) {
|
_stateObjChanged(stateObj) {
|
||||||
this._value = stateObj && stateObj.state;
|
this._value = stateObj && stateObj.state;
|
||||||
}
|
}
|
||||||
|
|
||||||
_selectedValueChanged() {
|
_selectedValueChanged() {
|
||||||
if (this._value === this._stateObj.state) {
|
if (this._value === this._stateObj.state) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.hass.callService("input_text", "set_value", {
|
this.hass.callService("input_text", "set_value", {
|
||||||
value: this._value,
|
value: this._value,
|
||||||
entity_id: this._stateObj.entity_id,
|
entity_id: this._stateObj.entity_id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define("hui-input-text-entity-row", HuiInputTextEntityRow);
|
customElements.define("hui-input-text-entity-row", HuiInputTextEntityRow);
|
||||||
|
|||||||
@@ -1,83 +1,83 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
import "@polymer/paper-button/paper-button";
|
import "@polymer/paper-button/paper-button";
|
||||||
|
|
||||||
import "../components/hui-generic-entity-row";
|
import "../components/hui-generic-entity-row";
|
||||||
|
|
||||||
import LocalizeMixin from "../../../mixins/localize-mixin";
|
import LocalizeMixin from "../../../mixins/localize-mixin";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin LocalizeMixin
|
* @appliesMixin LocalizeMixin
|
||||||
*/
|
*/
|
||||||
class HuiLockEntityRow extends LocalizeMixin(PolymerElement) {
|
class HuiLockEntityRow extends LocalizeMixin(PolymerElement) {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
${this.styleTemplate}
|
${this.styleTemplate}
|
||||||
<hui-generic-entity-row
|
<hui-generic-entity-row
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
config="[[_config]]"
|
config="[[_config]]"
|
||||||
>
|
>
|
||||||
${this.lockControlTemplate}
|
${this.lockControlTemplate}
|
||||||
</hui-generic-entity-row>
|
</hui-generic-entity-row>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styleTemplate() {
|
static get styleTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
paper-button {
|
paper-button {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
margin-right: -.57em;
|
margin-right: -.57em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get lockControlTemplate() {
|
static get lockControlTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<paper-button on-click="_callService">
|
<paper-button on-click="_callService">
|
||||||
[[_computeButtonTitle(_stateObj.state)]]
|
[[_computeButtonTitle(_stateObj.state)]]
|
||||||
</paper-button>
|
</paper-button>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
_config: Object,
|
_config: Object,
|
||||||
_stateObj: {
|
_stateObj: {
|
||||||
type: Object,
|
type: Object,
|
||||||
computed: "_computeStateObj(hass.states, _config.entity)",
|
computed: "_computeStateObj(hass.states, _config.entity)",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeStateObj(states, entityId) {
|
_computeStateObj(states, entityId) {
|
||||||
return states && entityId in states ? states[entityId] : null;
|
return states && entityId in states ? states[entityId] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
if (!config || !config.entity) {
|
if (!config || !config.entity) {
|
||||||
throw new Error("Entity not configured.");
|
throw new Error("Entity not configured.");
|
||||||
}
|
}
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeButtonTitle(state) {
|
_computeButtonTitle(state) {
|
||||||
return state === "locked"
|
return state === "locked"
|
||||||
? this.localize("ui.card.lock.unlock")
|
? this.localize("ui.card.lock.unlock")
|
||||||
: this.localize("ui.card.lock.lock");
|
: this.localize("ui.card.lock.lock");
|
||||||
}
|
}
|
||||||
|
|
||||||
_callService(ev) {
|
_callService(ev) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
const stateObj = this._stateObj;
|
const stateObj = this._stateObj;
|
||||||
this.hass.callService(
|
this.hass.callService(
|
||||||
"lock",
|
"lock",
|
||||||
stateObj.state === "locked" ? "unlock" : "lock",
|
stateObj.state === "locked" ? "unlock" : "lock",
|
||||||
{ entity_id: stateObj.entity_id }
|
{ entity_id: stateObj.entity_id }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define("hui-lock-entity-row", HuiLockEntityRow);
|
customElements.define("hui-lock-entity-row", HuiLockEntityRow);
|
||||||
|
|||||||
@@ -1,162 +1,162 @@
|
|||||||
import "@polymer/paper-icon-button/paper-icon-button";
|
import "@polymer/paper-icon-button/paper-icon-button";
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import "../components/hui-generic-entity-row";
|
import "../components/hui-generic-entity-row";
|
||||||
|
|
||||||
import LocalizeMixin from "../../../mixins/localize-mixin";
|
import LocalizeMixin from "../../../mixins/localize-mixin";
|
||||||
|
|
||||||
const SUPPORT_PAUSE = 1;
|
const SUPPORT_PAUSE = 1;
|
||||||
const SUPPORT_NEXT_TRACK = 32;
|
const SUPPORT_NEXT_TRACK = 32;
|
||||||
const SUPPORTS_PLAY = 16384;
|
const SUPPORTS_PLAY = 16384;
|
||||||
const OFF_STATES = ["off", "idle"];
|
const OFF_STATES = ["off", "idle"];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin LocalizeMixin
|
* @appliesMixin LocalizeMixin
|
||||||
*/
|
*/
|
||||||
class HuiMediaPlayerEntityRow extends LocalizeMixin(PolymerElement) {
|
class HuiMediaPlayerEntityRow extends LocalizeMixin(PolymerElement) {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
${this.styleTemplate}
|
${this.styleTemplate}
|
||||||
<hui-generic-entity-row
|
<hui-generic-entity-row
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
config="[[_config]]"
|
config="[[_config]]"
|
||||||
show-secondary="false"
|
show-secondary="false"
|
||||||
>
|
>
|
||||||
${this.mediaPlayerControlTemplate}
|
${this.mediaPlayerControlTemplate}
|
||||||
</hui-generic-entity-row>
|
</hui-generic-entity-row>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styleTemplate() {
|
static get styleTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
.controls {
|
.controls {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get mediaPlayerControlTemplate() {
|
static get mediaPlayerControlTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<template is="dom-if" if="[[!_isOff(_stateObj.state)]]">
|
<template is="dom-if" if="[[!_isOff(_stateObj.state)]]">
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<template is="dom-if" if="[[_computeControlIcon(_stateObj)]]">
|
<template is="dom-if" if="[[_computeControlIcon(_stateObj)]]">
|
||||||
<paper-icon-button
|
<paper-icon-button
|
||||||
icon="[[_computeControlIcon(_stateObj)]]"
|
icon="[[_computeControlIcon(_stateObj)]]"
|
||||||
on-click="_playPause"
|
on-click="_playPause"
|
||||||
></paper-icon-button>
|
></paper-icon-button>
|
||||||
</template>
|
</template>
|
||||||
<template is="dom-if" if="[[_supportsNext(_stateObj)]]">
|
<template is="dom-if" if="[[_supportsNext(_stateObj)]]">
|
||||||
<paper-icon-button
|
<paper-icon-button
|
||||||
icon="hass:skip-next"
|
icon="hass:skip-next"
|
||||||
on-click="_nextTrack"
|
on-click="_nextTrack"
|
||||||
></paper-icon-button>
|
></paper-icon-button>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template is="dom-if" if="[[_isOff(_stateObj.state)]]">
|
<template is="dom-if" if="[[_isOff(_stateObj.state)]]">
|
||||||
<div>[[_computeState(_stateObj.state)]]</div>
|
<div>[[_computeState(_stateObj.state)]]</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div slot="secondary">
|
<div slot="secondary">
|
||||||
[[_computeMediaTitle(_stateObj)]]
|
[[_computeMediaTitle(_stateObj)]]
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
_config: Object,
|
_config: Object,
|
||||||
_stateObj: {
|
_stateObj: {
|
||||||
type: Object,
|
type: Object,
|
||||||
computed: "_computeStateObj(hass.states, _config.entity)",
|
computed: "_computeStateObj(hass.states, _config.entity)",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeStateObj(states, entityId) {
|
_computeStateObj(states, entityId) {
|
||||||
return states && entityId in states ? states[entityId] : null;
|
return states && entityId in states ? states[entityId] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
if (!config || !config.entity) {
|
if (!config || !config.entity) {
|
||||||
throw new Error("Entity not configured.");
|
throw new Error("Entity not configured.");
|
||||||
}
|
}
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeControlIcon(stateObj) {
|
_computeControlIcon(stateObj) {
|
||||||
if (!stateObj) return null;
|
if (!stateObj) return null;
|
||||||
|
|
||||||
if (stateObj.state !== "playing") {
|
if (stateObj.state !== "playing") {
|
||||||
return stateObj.attributes.supported_features & SUPPORTS_PLAY
|
return stateObj.attributes.supported_features & SUPPORTS_PLAY
|
||||||
? "hass:play"
|
? "hass:play"
|
||||||
: "";
|
: "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return stateObj.attributes.supported_features & SUPPORT_PAUSE
|
return stateObj.attributes.supported_features & SUPPORT_PAUSE
|
||||||
? "hass:pause"
|
? "hass:pause"
|
||||||
: "hass:stop";
|
: "hass:stop";
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeMediaTitle(stateObj) {
|
_computeMediaTitle(stateObj) {
|
||||||
if (!stateObj || this._isOff(stateObj.state)) return null;
|
if (!stateObj || this._isOff(stateObj.state)) return null;
|
||||||
|
|
||||||
switch (stateObj.attributes.media_content_type) {
|
switch (stateObj.attributes.media_content_type) {
|
||||||
case "music":
|
case "music":
|
||||||
return `${stateObj.attributes.media_artist}: ${
|
return `${stateObj.attributes.media_artist}: ${
|
||||||
stateObj.attributes.media_title
|
stateObj.attributes.media_title
|
||||||
}`;
|
}`;
|
||||||
case "tvshow":
|
case "tvshow":
|
||||||
return `${stateObj.attributes.media_series_title}: ${
|
return `${stateObj.attributes.media_series_title}: ${
|
||||||
stateObj.attributes.media_title
|
stateObj.attributes.media_title
|
||||||
}`;
|
}`;
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
stateObj.attributes.media_title ||
|
stateObj.attributes.media_title ||
|
||||||
stateObj.attributes.app_name ||
|
stateObj.attributes.app_name ||
|
||||||
stateObj.state
|
stateObj.state
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeState(state) {
|
_computeState(state) {
|
||||||
return (
|
return (
|
||||||
this.localize(`state.media_player.${state}`) ||
|
this.localize(`state.media_player.${state}`) ||
|
||||||
this.localize(`state.default.${state}`) ||
|
this.localize(`state.default.${state}`) ||
|
||||||
state
|
state
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_callService(service) {
|
_callService(service) {
|
||||||
this.hass.callService("media_player", service, {
|
this.hass.callService("media_player", service, {
|
||||||
entity_id: this._config.entity,
|
entity_id: this._config.entity,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_playPause(event) {
|
_playPause(event) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
this._callService("media_play_pause");
|
this._callService("media_play_pause");
|
||||||
}
|
}
|
||||||
|
|
||||||
_nextTrack(event) {
|
_nextTrack(event) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
if (this._stateObj.attributes.supported_features & SUPPORT_NEXT_TRACK) {
|
if (this._stateObj.attributes.supported_features & SUPPORT_NEXT_TRACK) {
|
||||||
this._callService("media_next_track");
|
this._callService("media_next_track");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_isOff(state) {
|
_isOff(state) {
|
||||||
return OFF_STATES.includes(state);
|
return OFF_STATES.includes(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
_supportsNext(stateObj) {
|
_supportsNext(stateObj) {
|
||||||
return (
|
return (
|
||||||
stateObj && stateObj.attributes.supported_features & SUPPORT_NEXT_TRACK
|
stateObj && stateObj.attributes.supported_features & SUPPORT_NEXT_TRACK
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define("hui-media-player-entity-row", HuiMediaPlayerEntityRow);
|
customElements.define("hui-media-player-entity-row", HuiMediaPlayerEntityRow);
|
||||||
|
|||||||
@@ -1,70 +1,70 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
import "@polymer/paper-button/paper-button";
|
import "@polymer/paper-button/paper-button";
|
||||||
|
|
||||||
import "../components/hui-generic-entity-row";
|
import "../components/hui-generic-entity-row";
|
||||||
|
|
||||||
import LocalizeMixin from "../../../mixins/localize-mixin";
|
import LocalizeMixin from "../../../mixins/localize-mixin";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin LocalizeMixin
|
* @appliesMixin LocalizeMixin
|
||||||
*/
|
*/
|
||||||
class HuiSceneEntityRow extends LocalizeMixin(PolymerElement) {
|
class HuiSceneEntityRow extends LocalizeMixin(PolymerElement) {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
${this.styleTemplate}
|
${this.styleTemplate}
|
||||||
<hui-generic-entity-row
|
<hui-generic-entity-row
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
config="[[_config]]"
|
config="[[_config]]"
|
||||||
>
|
>
|
||||||
${this.sceneControlTemplate}
|
${this.sceneControlTemplate}
|
||||||
</hui-generic-entity-row>
|
</hui-generic-entity-row>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styleTemplate() {
|
static get styleTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
paper-button {
|
paper-button {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
margin-right: -.57em;
|
margin-right: -.57em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get sceneControlTemplate() {
|
static get sceneControlTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<paper-button on-click="_callService">
|
<paper-button on-click="_callService">
|
||||||
[[localize('ui.card.scene.activate')]]
|
[[localize('ui.card.scene.activate')]]
|
||||||
</paper-button>
|
</paper-button>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
_config: Object,
|
_config: Object,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeStateObj(states, entityId) {
|
_computeStateObj(states, entityId) {
|
||||||
return states && entityId in states ? states[entityId] : null;
|
return states && entityId in states ? states[entityId] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
if (!config || !config.entity) {
|
if (!config || !config.entity) {
|
||||||
throw new Error("Entity not configured.");
|
throw new Error("Entity not configured.");
|
||||||
}
|
}
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
_callService(ev) {
|
_callService(ev) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.hass.callService("scene", "turn_on", {
|
this.hass.callService("scene", "turn_on", {
|
||||||
entity_id: this._config.entity,
|
entity_id: this._config.entity,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define("hui-scene-entity-row", HuiSceneEntityRow);
|
customElements.define("hui-scene-entity-row", HuiSceneEntityRow);
|
||||||
|
|||||||
@@ -1,78 +1,78 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
import "@polymer/paper-button/paper-button";
|
import "@polymer/paper-button/paper-button";
|
||||||
|
|
||||||
import "../components/hui-generic-entity-row";
|
import "../components/hui-generic-entity-row";
|
||||||
import "../../../components/entity/ha-entity-toggle";
|
import "../../../components/entity/ha-entity-toggle";
|
||||||
|
|
||||||
import LocalizeMixin from "../../../mixins/localize-mixin";
|
import LocalizeMixin from "../../../mixins/localize-mixin";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin LocalizeMixin
|
* @appliesMixin LocalizeMixin
|
||||||
*/
|
*/
|
||||||
class HuiScriptEntityRow extends LocalizeMixin(PolymerElement) {
|
class HuiScriptEntityRow extends LocalizeMixin(PolymerElement) {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
${this.styleTemplate}
|
${this.styleTemplate}
|
||||||
<hui-generic-entity-row
|
<hui-generic-entity-row
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
config="[[_config]]"
|
config="[[_config]]"
|
||||||
>
|
>
|
||||||
${this.scriptControlTemplate}
|
${this.scriptControlTemplate}
|
||||||
</hui-generic-entity-row>
|
</hui-generic-entity-row>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styleTemplate() {
|
static get styleTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
paper-button {
|
paper-button {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
margin-right: -.57em;
|
margin-right: -.57em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get scriptControlTemplate() {
|
static get scriptControlTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<template is="dom-if" if="[[_stateObj.attributes.can_cancel]]">
|
<template is="dom-if" if="[[_stateObj.attributes.can_cancel]]">
|
||||||
<ha-entity-toggle state-obj="[[_stateObj]]" hass="[[hass]]"></ha-entity-toggle>
|
<ha-entity-toggle state-obj="[[_stateObj]]" hass="[[hass]]"></ha-entity-toggle>
|
||||||
</template>
|
</template>
|
||||||
<template is="dom-if" if="[[!_stateObj.attributes.can_cancel]]">
|
<template is="dom-if" if="[[!_stateObj.attributes.can_cancel]]">
|
||||||
<paper-button on-click="_callService">[[localize('ui.card.script.execute')]]</paper-button>
|
<paper-button on-click="_callService">[[localize('ui.card.script.execute')]]</paper-button>
|
||||||
</template>
|
</template>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
_config: Object,
|
_config: Object,
|
||||||
_stateObj: {
|
_stateObj: {
|
||||||
type: Object,
|
type: Object,
|
||||||
computed: "_computeStateObj(hass.states, _config.entity)",
|
computed: "_computeStateObj(hass.states, _config.entity)",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeStateObj(states, entityId) {
|
_computeStateObj(states, entityId) {
|
||||||
return states && entityId in states ? states[entityId] : null;
|
return states && entityId in states ? states[entityId] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
if (!config || !config.entity) {
|
if (!config || !config.entity) {
|
||||||
throw new Error("Entity not configured.");
|
throw new Error("Entity not configured.");
|
||||||
}
|
}
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
_callService(ev) {
|
_callService(ev) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.hass.callService("script", "turn_on", {
|
this.hass.callService("script", "turn_on", {
|
||||||
entity_id: this._config.entity,
|
entity_id: this._config.entity,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define("hui-script-entity-row", HuiScriptEntityRow);
|
customElements.define("hui-script-entity-row", HuiScriptEntityRow);
|
||||||
|
|||||||
@@ -1,70 +1,70 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import "../components/hui-generic-entity-row";
|
import "../components/hui-generic-entity-row";
|
||||||
|
|
||||||
import computeStateDisplay from "../../../common/entity/compute_state_display";
|
import computeStateDisplay from "../../../common/entity/compute_state_display";
|
||||||
|
|
||||||
import LocalizeMixin from "../../../mixins/localize-mixin";
|
import LocalizeMixin from "../../../mixins/localize-mixin";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin LocalizeMixin
|
* @appliesMixin LocalizeMixin
|
||||||
*/
|
*/
|
||||||
class HuiTextEntityRow extends LocalizeMixin(PolymerElement) {
|
class HuiTextEntityRow extends LocalizeMixin(PolymerElement) {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
${this.styleTemplate}
|
${this.styleTemplate}
|
||||||
<hui-generic-entity-row
|
<hui-generic-entity-row
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
config="[[_config]]"
|
config="[[_config]]"
|
||||||
>
|
>
|
||||||
${this.textControlTemplate}
|
${this.textControlTemplate}
|
||||||
</hui-generic-entity-row>
|
</hui-generic-entity-row>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styleTemplate() {
|
static get styleTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
div {
|
div {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get textControlTemplate() {
|
static get textControlTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<div>
|
<div>
|
||||||
[[_computeState(_stateObj)]]
|
[[_computeState(_stateObj)]]
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
_config: Object,
|
_config: Object,
|
||||||
_stateObj: {
|
_stateObj: {
|
||||||
type: Object,
|
type: Object,
|
||||||
computed: "_computeStateObj(hass.states, _config.entity)",
|
computed: "_computeStateObj(hass.states, _config.entity)",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeStateObj(states, entityId) {
|
_computeStateObj(states, entityId) {
|
||||||
return states && entityId in states ? states[entityId] : null;
|
return states && entityId in states ? states[entityId] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
if (!config || !config.entity) {
|
if (!config || !config.entity) {
|
||||||
throw new Error("Entity not configured.");
|
throw new Error("Entity not configured.");
|
||||||
}
|
}
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeState(stateObj) {
|
_computeState(stateObj) {
|
||||||
return stateObj && computeStateDisplay(this.localize, stateObj);
|
return stateObj && computeStateDisplay(this.localize, stateObj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define("hui-text-entity-row", HuiTextEntityRow);
|
customElements.define("hui-text-entity-row", HuiTextEntityRow);
|
||||||
|
|||||||
@@ -1,103 +1,103 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import "../components/hui-generic-entity-row";
|
import "../components/hui-generic-entity-row";
|
||||||
|
|
||||||
import timerTimeRemaining from "../../../common/entity/timer_time_remaining";
|
import timerTimeRemaining from "../../../common/entity/timer_time_remaining";
|
||||||
import secondsToDuration from "../../../common/datetime/seconds_to_duration";
|
import secondsToDuration from "../../../common/datetime/seconds_to_duration";
|
||||||
|
|
||||||
class HuiTimerEntityRow extends PolymerElement {
|
class HuiTimerEntityRow extends PolymerElement {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<hui-generic-entity-row
|
<hui-generic-entity-row
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
config="[[_config]]"
|
config="[[_config]]"
|
||||||
>
|
>
|
||||||
${this.timerControlTemplate}
|
${this.timerControlTemplate}
|
||||||
</hui-generic-entity-row>
|
</hui-generic-entity-row>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get timerControlTemplate() {
|
static get timerControlTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<div>
|
<div>
|
||||||
[[_computeDisplay(_stateObj, _timeRemaining)]]
|
[[_computeDisplay(_stateObj, _timeRemaining)]]
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
_config: Object,
|
_config: Object,
|
||||||
_stateObj: {
|
_stateObj: {
|
||||||
type: Object,
|
type: Object,
|
||||||
computed: "_computeStateObj(hass.states, _config.entity)",
|
computed: "_computeStateObj(hass.states, _config.entity)",
|
||||||
observer: "_stateObjChanged",
|
observer: "_stateObjChanged",
|
||||||
},
|
},
|
||||||
_timeRemaining: Number,
|
_timeRemaining: Number,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
this._clearInterval();
|
this._clearInterval();
|
||||||
}
|
}
|
||||||
|
|
||||||
_stateObjChanged(stateObj) {
|
_stateObjChanged(stateObj) {
|
||||||
if (stateObj) {
|
if (stateObj) {
|
||||||
this._startInterval(stateObj);
|
this._startInterval(stateObj);
|
||||||
} else {
|
} else {
|
||||||
this._clearInterval();
|
this._clearInterval();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_clearInterval() {
|
_clearInterval() {
|
||||||
if (this._updateRemaining) {
|
if (this._updateRemaining) {
|
||||||
clearInterval(this._updateRemaining);
|
clearInterval(this._updateRemaining);
|
||||||
this._updateRemaining = null;
|
this._updateRemaining = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_startInterval(stateObj) {
|
_startInterval(stateObj) {
|
||||||
this._clearInterval();
|
this._clearInterval();
|
||||||
this._calculateRemaining(stateObj);
|
this._calculateRemaining(stateObj);
|
||||||
|
|
||||||
if (stateObj.state === "active") {
|
if (stateObj.state === "active") {
|
||||||
this._updateRemaining = setInterval(
|
this._updateRemaining = setInterval(
|
||||||
() => this._calculateRemaining(this._stateObj),
|
() => this._calculateRemaining(this._stateObj),
|
||||||
1000
|
1000
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_calculateRemaining(stateObj) {
|
_calculateRemaining(stateObj) {
|
||||||
this._timeRemaining = timerTimeRemaining(stateObj);
|
this._timeRemaining = timerTimeRemaining(stateObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeDisplay(stateObj, time) {
|
_computeDisplay(stateObj, time) {
|
||||||
if (!stateObj) return null;
|
if (!stateObj) return null;
|
||||||
|
|
||||||
if (stateObj.state === "idle" || time === 0) return stateObj.state;
|
if (stateObj.state === "idle" || time === 0) return stateObj.state;
|
||||||
|
|
||||||
let display = secondsToDuration(time);
|
let display = secondsToDuration(time);
|
||||||
|
|
||||||
if (stateObj.state === "paused") {
|
if (stateObj.state === "paused") {
|
||||||
display += " (paused)";
|
display += " (paused)";
|
||||||
}
|
}
|
||||||
|
|
||||||
return display;
|
return display;
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeStateObj(states, entityId) {
|
_computeStateObj(states, entityId) {
|
||||||
return states && entityId in states ? states[entityId] : null;
|
return states && entityId in states ? states[entityId] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
if (!config || !config.entity) {
|
if (!config || !config.entity) {
|
||||||
throw new Error("Entity not configured.");
|
throw new Error("Entity not configured.");
|
||||||
}
|
}
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define("hui-timer-entity-row", HuiTimerEntityRow);
|
customElements.define("hui-timer-entity-row", HuiTimerEntityRow);
|
||||||
|
|||||||
@@ -1,76 +1,76 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import "../components/hui-generic-entity-row";
|
import "../components/hui-generic-entity-row";
|
||||||
import "../../../components/entity/ha-entity-toggle";
|
import "../../../components/entity/ha-entity-toggle";
|
||||||
|
|
||||||
import computeStateDisplay from "../../../common/entity/compute_state_display";
|
import computeStateDisplay from "../../../common/entity/compute_state_display";
|
||||||
|
|
||||||
import LocalizeMixin from "../../../mixins/localize-mixin";
|
import LocalizeMixin from "../../../mixins/localize-mixin";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin LocalizeMixin
|
* @appliesMixin LocalizeMixin
|
||||||
*/
|
*/
|
||||||
class HuiToggleEntityRow extends LocalizeMixin(PolymerElement) {
|
class HuiToggleEntityRow extends LocalizeMixin(PolymerElement) {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<hui-generic-entity-row
|
<hui-generic-entity-row
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
config="[[_config]]"
|
config="[[_config]]"
|
||||||
>
|
>
|
||||||
${this.toggleControlTemplate}
|
${this.toggleControlTemplate}
|
||||||
</hui-generic-entity-row>
|
</hui-generic-entity-row>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get toggleControlTemplate() {
|
static get toggleControlTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<template is="dom-if" if="[[_canToggle]]">
|
<template is="dom-if" if="[[_canToggle]]">
|
||||||
<ha-entity-toggle
|
<ha-entity-toggle
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
state-obj="[[_stateObj]]"
|
state-obj="[[_stateObj]]"
|
||||||
></ha-entity-toggle>
|
></ha-entity-toggle>
|
||||||
</template>
|
</template>
|
||||||
<template is="dom-if" if="[[!_canToggle]]">
|
<template is="dom-if" if="[[!_canToggle]]">
|
||||||
<div>
|
<div>
|
||||||
[[_computeState(_stateObj)]]
|
[[_computeState(_stateObj)]]
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
_config: Object,
|
_config: Object,
|
||||||
_stateObj: {
|
_stateObj: {
|
||||||
type: Object,
|
type: Object,
|
||||||
computed: "_computeStateObj(hass.states, _config.entity)",
|
computed: "_computeStateObj(hass.states, _config.entity)",
|
||||||
},
|
},
|
||||||
_canToggle: {
|
_canToggle: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
computed: "_computeCanToggle(_stateObj.state)",
|
computed: "_computeCanToggle(_stateObj.state)",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeStateObj(states, entityId) {
|
_computeStateObj(states, entityId) {
|
||||||
return states && entityId in states ? states[entityId] : null;
|
return states && entityId in states ? states[entityId] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeCanToggle(state) {
|
_computeCanToggle(state) {
|
||||||
return state === "on" || state === "off";
|
return state === "on" || state === "off";
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeState(stateObj) {
|
_computeState(stateObj) {
|
||||||
return stateObj && computeStateDisplay(this.localize, stateObj);
|
return stateObj && computeStateDisplay(this.localize, stateObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
if (!config || !config.entity) {
|
if (!config || !config.entity) {
|
||||||
throw new Error("Entity not configured.");
|
throw new Error("Entity not configured.");
|
||||||
}
|
}
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define("hui-toggle-entity-row", HuiToggleEntityRow);
|
customElements.define("hui-toggle-entity-row", HuiToggleEntityRow);
|
||||||
|
|||||||
@@ -1,125 +1,125 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
import "@polymer/paper-button/paper-button";
|
import "@polymer/paper-button/paper-button";
|
||||||
|
|
||||||
import "../../layouts/hass-loading-screen";
|
import "../../layouts/hass-loading-screen";
|
||||||
import "../../layouts/hass-error-screen";
|
import "../../layouts/hass-error-screen";
|
||||||
import "./hui-root";
|
import "./hui-root";
|
||||||
|
|
||||||
class Lovelace extends PolymerElement {
|
class Lovelace extends PolymerElement {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
paper-button {
|
paper-button {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<template is='dom-if' if='[[_equal(_state, "loaded")]]' restamp>
|
<template is='dom-if' if='[[_equal(_state, "loaded")]]' restamp>
|
||||||
<hui-root
|
<hui-root
|
||||||
narrow="[[narrow]]"
|
narrow="[[narrow]]"
|
||||||
show-menu="[[showMenu]]"
|
show-menu="[[showMenu]]"
|
||||||
hass='[[hass]]'
|
hass='[[hass]]'
|
||||||
route="[[route]]"
|
route="[[route]]"
|
||||||
config='[[_config]]'
|
config='[[_config]]'
|
||||||
columns='[[_columns]]'
|
columns='[[_columns]]'
|
||||||
on-config-refresh='_fetchConfig'
|
on-config-refresh='_fetchConfig'
|
||||||
></hui-root>
|
></hui-root>
|
||||||
</template>
|
</template>
|
||||||
<template is='dom-if' if='[[_equal(_state, "loading")]]' restamp>
|
<template is='dom-if' if='[[_equal(_state, "loading")]]' restamp>
|
||||||
<hass-loading-screen
|
<hass-loading-screen
|
||||||
narrow="[[narrow]]"
|
narrow="[[narrow]]"
|
||||||
show-menu="[[showMenu]]"
|
show-menu="[[showMenu]]"
|
||||||
></hass-loading-screen>
|
></hass-loading-screen>
|
||||||
</template>
|
</template>
|
||||||
<template is='dom-if' if='[[_equal(_state, "error")]]' restamp>
|
<template is='dom-if' if='[[_equal(_state, "error")]]' restamp>
|
||||||
<hass-error-screen
|
<hass-error-screen
|
||||||
title='Lovelace'
|
title='Lovelace'
|
||||||
error='[[_errorMsg]]'
|
error='[[_errorMsg]]'
|
||||||
narrow="[[narrow]]"
|
narrow="[[narrow]]"
|
||||||
show-menu="[[showMenu]]"
|
show-menu="[[showMenu]]"
|
||||||
>
|
>
|
||||||
<paper-button on-click="_fetchConfig">Reload ui-lovelace.yaml</paper-button>
|
<paper-button on-click="_fetchConfig">Reload ui-lovelace.yaml</paper-button>
|
||||||
</hass-error-screen>
|
</hass-error-screen>
|
||||||
</template>
|
</template>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
|
|
||||||
narrow: {
|
narrow: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
showMenu: {
|
showMenu: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
route: Object,
|
route: Object,
|
||||||
|
|
||||||
_columns: {
|
_columns: {
|
||||||
type: Number,
|
type: Number,
|
||||||
value: 1,
|
value: 1,
|
||||||
},
|
},
|
||||||
|
|
||||||
_state: {
|
_state: {
|
||||||
type: String,
|
type: String,
|
||||||
value: "loading",
|
value: "loading",
|
||||||
},
|
},
|
||||||
|
|
||||||
_errorMsg: String,
|
_errorMsg: String,
|
||||||
|
|
||||||
_config: {
|
_config: {
|
||||||
type: Object,
|
type: Object,
|
||||||
value: null,
|
value: null,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static get observers() {
|
static get observers() {
|
||||||
return ["_updateColumns(narrow, showMenu)"];
|
return ["_updateColumns(narrow, showMenu)"];
|
||||||
}
|
}
|
||||||
|
|
||||||
ready() {
|
ready() {
|
||||||
this._fetchConfig();
|
this._fetchConfig();
|
||||||
this._updateColumns = this._updateColumns.bind(this);
|
this._updateColumns = this._updateColumns.bind(this);
|
||||||
this.mqls = [300, 600, 900, 1200].map((width) => {
|
this.mqls = [300, 600, 900, 1200].map((width) => {
|
||||||
const mql = matchMedia(`(min-width: ${width}px)`);
|
const mql = matchMedia(`(min-width: ${width}px)`);
|
||||||
mql.addListener(this._updateColumns);
|
mql.addListener(this._updateColumns);
|
||||||
return mql;
|
return mql;
|
||||||
});
|
});
|
||||||
this._updateColumns();
|
this._updateColumns();
|
||||||
super.ready();
|
super.ready();
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateColumns() {
|
_updateColumns() {
|
||||||
const matchColumns = this.mqls.reduce((cols, mql) => cols + mql.matches, 0);
|
const matchColumns = this.mqls.reduce((cols, mql) => cols + mql.matches, 0);
|
||||||
// Do -1 column if the menu is docked and open
|
// Do -1 column if the menu is docked and open
|
||||||
this._columns = Math.max(1, matchColumns - (!this.narrow && this.showMenu));
|
this._columns = Math.max(1, matchColumns - (!this.narrow && this.showMenu));
|
||||||
}
|
}
|
||||||
|
|
||||||
async _fetchConfig() {
|
async _fetchConfig() {
|
||||||
try {
|
try {
|
||||||
const conf = await this.hass.callWS({ type: "lovelace/config" });
|
const conf = await this.hass.callWS({ type: "lovelace/config" });
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
_config: conf,
|
_config: conf,
|
||||||
_state: "loaded",
|
_state: "loaded",
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
_state: "error",
|
_state: "error",
|
||||||
_errorMsg: err.message,
|
_errorMsg: err.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_equal(a, b) {
|
_equal(a, b) {
|
||||||
return a === b;
|
return a === b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("ha-panel-lovelace", Lovelace);
|
customElements.define("ha-panel-lovelace", Lovelace);
|
||||||
|
|||||||
@@ -1,380 +1,380 @@
|
|||||||
import "@polymer/app-layout/app-header-layout/app-header-layout";
|
import "@polymer/app-layout/app-header-layout/app-header-layout";
|
||||||
import "@polymer/app-layout/app-header/app-header";
|
import "@polymer/app-layout/app-header/app-header";
|
||||||
import "@polymer/app-layout/app-scroll-effects/effects/waterfall";
|
import "@polymer/app-layout/app-scroll-effects/effects/waterfall";
|
||||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||||
import "@polymer/app-route/app-route";
|
import "@polymer/app-route/app-route";
|
||||||
import "@polymer/paper-icon-button/paper-icon-button";
|
import "@polymer/paper-icon-button/paper-icon-button";
|
||||||
import "@polymer/paper-item/paper-item";
|
import "@polymer/paper-item/paper-item";
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
import "@polymer/paper-listbox/paper-listbox";
|
||||||
import "@polymer/paper-menu-button/paper-menu-button";
|
import "@polymer/paper-menu-button/paper-menu-button";
|
||||||
import "@polymer/paper-tabs/paper-tab";
|
import "@polymer/paper-tabs/paper-tab";
|
||||||
import "@polymer/paper-tabs/paper-tabs";
|
import "@polymer/paper-tabs/paper-tabs";
|
||||||
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import scrollToTarget from "../../common/dom/scroll-to-target";
|
import scrollToTarget from "../../common/dom/scroll-to-target";
|
||||||
|
|
||||||
import EventsMixin from "../../mixins/events-mixin";
|
import EventsMixin from "../../mixins/events-mixin";
|
||||||
import NavigateMixin from "../../mixins/navigate-mixin";
|
import NavigateMixin from "../../mixins/navigate-mixin";
|
||||||
|
|
||||||
import "../../layouts/ha-app-layout";
|
import "../../layouts/ha-app-layout";
|
||||||
import "../../components/ha-start-voice-button";
|
import "../../components/ha-start-voice-button";
|
||||||
import "../../components/ha-icon";
|
import "../../components/ha-icon";
|
||||||
import { loadModule, loadCSS, loadJS } from "../../common/dom/load_resource";
|
import { loadModule, loadCSS, loadJS } from "../../common/dom/load_resource";
|
||||||
import { subscribeNotifications } from "../../data/ws-notifications";
|
import { subscribeNotifications } from "../../data/ws-notifications";
|
||||||
import "./components/notifications/hui-notification-drawer";
|
import "./components/notifications/hui-notification-drawer";
|
||||||
import "./components/notifications/hui-notifications-button";
|
import "./components/notifications/hui-notifications-button";
|
||||||
import "./hui-unused-entities";
|
import "./hui-unused-entities";
|
||||||
import "./hui-view";
|
import "./hui-view";
|
||||||
import debounce from "../../common/util/debounce";
|
import debounce from "../../common/util/debounce";
|
||||||
|
|
||||||
import createCardElement from "./common/create-card-element";
|
import createCardElement from "./common/create-card-element";
|
||||||
import computeNotifications from "./common/compute-notifications";
|
import computeNotifications from "./common/compute-notifications";
|
||||||
|
|
||||||
// CSS and JS should only be imported once. Modules and HTML are safe.
|
// CSS and JS should only be imported once. Modules and HTML are safe.
|
||||||
const CSS_CACHE = {};
|
const CSS_CACHE = {};
|
||||||
const JS_CACHE = {};
|
const JS_CACHE = {};
|
||||||
|
|
||||||
class HUIRoot extends NavigateMixin(EventsMixin(PolymerElement)) {
|
class HUIRoot extends NavigateMixin(EventsMixin(PolymerElement)) {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<style include='ha-style'>
|
<style include='ha-style'>
|
||||||
:host {
|
:host {
|
||||||
-ms-user-select: none;
|
-ms-user-select: none;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-app-layout {
|
ha-app-layout {
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
}
|
}
|
||||||
paper-tabs {
|
paper-tabs {
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
--paper-tabs-selection-bar-color: var(--text-primary-color, #FFF);
|
--paper-tabs-selection-bar-color: var(--text-primary-color, #FFF);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
app-toolbar a {
|
app-toolbar a {
|
||||||
color: var(--text-primary-color, white);
|
color: var(--text-primary-color, white);
|
||||||
}
|
}
|
||||||
#view {
|
#view {
|
||||||
min-height: calc(100vh - 112px);
|
min-height: calc(100vh - 112px);
|
||||||
/**
|
/**
|
||||||
* Since we only set min-height, if child nodes need percentage
|
* Since we only set min-height, if child nodes need percentage
|
||||||
* heights they must use absolute positioning so we need relative
|
* heights they must use absolute positioning so we need relative
|
||||||
* positioning here.
|
* positioning here.
|
||||||
*
|
*
|
||||||
* https://www.w3.org/TR/CSS2/visudet.html#the-height-property
|
* https://www.w3.org/TR/CSS2/visudet.html#the-height-property
|
||||||
*/
|
*/
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
#view.tabs-hidden {
|
#view.tabs-hidden {
|
||||||
min-height: calc(100vh - 64px);
|
min-height: calc(100vh - 64px);
|
||||||
}
|
}
|
||||||
paper-item {
|
paper-item {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<app-route route="[[route]]" pattern="/:view" data="{{routeData}}"></app-route>
|
<app-route route="[[route]]" pattern="/:view" data="{{routeData}}"></app-route>
|
||||||
<hui-notification-drawer
|
<hui-notification-drawer
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
notifications="[[_notifications]]"
|
notifications="[[_notifications]]"
|
||||||
open="{{notificationsOpen}}"
|
open="{{notificationsOpen}}"
|
||||||
narrow="[[narrow]]"
|
narrow="[[narrow]]"
|
||||||
></hui-notification-drawer>
|
></hui-notification-drawer>
|
||||||
<ha-app-layout id="layout">
|
<ha-app-layout id="layout">
|
||||||
<app-header slot="header" effects="waterfall" fixed condenses>
|
<app-header slot="header" effects="waterfall" fixed condenses>
|
||||||
<template is='dom-if' if="[[!_editMode]]">
|
<template is='dom-if' if="[[!_editMode]]">
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button narrow='[[narrow]]' show-menu='[[showMenu]]'></ha-menu-button>
|
<ha-menu-button narrow='[[narrow]]' show-menu='[[showMenu]]'></ha-menu-button>
|
||||||
<div main-title>[[_computeTitle(config)]]</div>
|
<div main-title>[[_computeTitle(config)]]</div>
|
||||||
<hui-notifications-button
|
<hui-notifications-button
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
notifications-open="{{notificationsOpen}}"
|
notifications-open="{{notificationsOpen}}"
|
||||||
notifications="[[_notifications]]"
|
notifications="[[_notifications]]"
|
||||||
></hui-notifications-button>
|
></hui-notifications-button>
|
||||||
<ha-start-voice-button hass="[[hass]]"></ha-start-voice-button>
|
<ha-start-voice-button hass="[[hass]]"></ha-start-voice-button>
|
||||||
<paper-menu-button
|
<paper-menu-button
|
||||||
no-animations
|
no-animations
|
||||||
horizontal-align="right"
|
horizontal-align="right"
|
||||||
horizontal-offset="-5"
|
horizontal-offset="-5"
|
||||||
>
|
>
|
||||||
<paper-icon-button icon="hass:dots-vertical" slot="dropdown-trigger"></paper-icon-button>
|
<paper-icon-button icon="hass:dots-vertical" slot="dropdown-trigger"></paper-icon-button>
|
||||||
<paper-listbox on-iron-select="_deselect" slot="dropdown-content">
|
<paper-listbox on-iron-select="_deselect" slot="dropdown-content">
|
||||||
<paper-item on-click="_handleRefresh">Refresh</paper-item>
|
<paper-item on-click="_handleRefresh">Refresh</paper-item>
|
||||||
<paper-item on-click="_handleUnusedEntities">Unused entities</paper-item>
|
<paper-item on-click="_handleUnusedEntities">Unused entities</paper-item>
|
||||||
<paper-item on-click="_editModeEnable">Configure UI</paper-item>
|
<paper-item on-click="_editModeEnable">Configure UI</paper-item>
|
||||||
<paper-item on-click="_handleHelp">Help</paper-item>
|
<paper-item on-click="_handleHelp">Help</paper-item>
|
||||||
</paper-listbox>
|
</paper-listbox>
|
||||||
</paper-menu-button>
|
</paper-menu-button>
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
</template>
|
</template>
|
||||||
<template is='dom-if' if="[[_editMode]]">
|
<template is='dom-if' if="[[_editMode]]">
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<paper-icon-button
|
<paper-icon-button
|
||||||
icon='hass:close'
|
icon='hass:close'
|
||||||
on-click='_editModeDisable'
|
on-click='_editModeDisable'
|
||||||
></paper-icon-button>
|
></paper-icon-button>
|
||||||
<div main-title>Edit UI</div>
|
<div main-title>Edit UI</div>
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div sticky hidden$="[[_computeTabsHidden(config.views)]]">
|
<div sticky hidden$="[[_computeTabsHidden(config.views)]]">
|
||||||
<paper-tabs scrollable selected="[[_curView]]" on-iron-activate="_handleViewSelected">
|
<paper-tabs scrollable selected="[[_curView]]" on-iron-activate="_handleViewSelected">
|
||||||
<template is="dom-repeat" items="[[config.views]]">
|
<template is="dom-repeat" items="[[config.views]]">
|
||||||
<paper-tab>
|
<paper-tab>
|
||||||
<template is="dom-if" if="[[item.icon]]">
|
<template is="dom-if" if="[[item.icon]]">
|
||||||
<ha-icon title$="[[item.title]]" icon="[[item.icon]]"></ha-icon>
|
<ha-icon title$="[[item.title]]" icon="[[item.icon]]"></ha-icon>
|
||||||
</template>
|
</template>
|
||||||
<template is="dom-if" if="[[!item.icon]]">
|
<template is="dom-if" if="[[!item.icon]]">
|
||||||
[[_computeTabTitle(item.title)]]
|
[[_computeTabTitle(item.title)]]
|
||||||
</template>
|
</template>
|
||||||
</paper-tab>
|
</paper-tab>
|
||||||
</template>
|
</template>
|
||||||
</paper-tabs>
|
</paper-tabs>
|
||||||
</div>
|
</div>
|
||||||
</app-header>
|
</app-header>
|
||||||
|
|
||||||
<div id='view' on-rebuild-view='_debouncedConfigChanged'></div>
|
<div id='view' on-rebuild-view='_debouncedConfigChanged'></div>
|
||||||
</app-header-layout>
|
</app-header-layout>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
narrow: Boolean,
|
narrow: Boolean,
|
||||||
showMenu: Boolean,
|
showMenu: Boolean,
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
observer: "_hassChanged",
|
observer: "_hassChanged",
|
||||||
},
|
},
|
||||||
config: {
|
config: {
|
||||||
type: Object,
|
type: Object,
|
||||||
observer: "_configChanged",
|
observer: "_configChanged",
|
||||||
},
|
},
|
||||||
columns: {
|
columns: {
|
||||||
type: Number,
|
type: Number,
|
||||||
observer: "_columnsChanged",
|
observer: "_columnsChanged",
|
||||||
},
|
},
|
||||||
|
|
||||||
_curView: {
|
_curView: {
|
||||||
type: Number,
|
type: Number,
|
||||||
value: 0,
|
value: 0,
|
||||||
},
|
},
|
||||||
|
|
||||||
route: {
|
route: {
|
||||||
type: Object,
|
type: Object,
|
||||||
observer: "_routeChanged",
|
observer: "_routeChanged",
|
||||||
},
|
},
|
||||||
|
|
||||||
notificationsOpen: {
|
notificationsOpen: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
_persistentNotifications: {
|
_persistentNotifications: {
|
||||||
type: Array,
|
type: Array,
|
||||||
value: [],
|
value: [],
|
||||||
},
|
},
|
||||||
|
|
||||||
_notifications: {
|
_notifications: {
|
||||||
type: Array,
|
type: Array,
|
||||||
computed: "_updateNotifications(hass.states, _persistentNotifications)",
|
computed: "_updateNotifications(hass.states, _persistentNotifications)",
|
||||||
},
|
},
|
||||||
|
|
||||||
_editMode: {
|
_editMode: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
value: false,
|
||||||
observer: "_editModeChanged",
|
observer: "_editModeChanged",
|
||||||
},
|
},
|
||||||
|
|
||||||
routeData: Object,
|
routeData: Object,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this._debouncedConfigChanged = debounce(
|
this._debouncedConfigChanged = debounce(
|
||||||
() => this._selectView(this._curView),
|
() => this._selectView(this._curView),
|
||||||
100
|
100
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
this._unsubNotifications = subscribeNotifications(
|
this._unsubNotifications = subscribeNotifications(
|
||||||
this.hass.connection,
|
this.hass.connection,
|
||||||
(notifications) => {
|
(notifications) => {
|
||||||
this._persistentNotifications = notifications;
|
this._persistentNotifications = notifications;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
if (typeof this._unsubNotifications === "function") {
|
if (typeof this._unsubNotifications === "function") {
|
||||||
this._unsubNotifications();
|
this._unsubNotifications();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateNotifications(states, persistent) {
|
_updateNotifications(states, persistent) {
|
||||||
if (!states) return persistent;
|
if (!states) return persistent;
|
||||||
|
|
||||||
const configurator = computeNotifications(states);
|
const configurator = computeNotifications(states);
|
||||||
return persistent.concat(configurator);
|
return persistent.concat(configurator);
|
||||||
}
|
}
|
||||||
|
|
||||||
_routeChanged(route) {
|
_routeChanged(route) {
|
||||||
const views = this.config && this.config.views;
|
const views = this.config && this.config.views;
|
||||||
if (route.path === "" && route.prefix === "/lovelace" && views) {
|
if (route.path === "" && route.prefix === "/lovelace" && views) {
|
||||||
this.navigate(`/lovelace/${views[0].id || 0}`, true);
|
this.navigate(`/lovelace/${views[0].id || 0}`, true);
|
||||||
} else if (this.routeData.view) {
|
} else if (this.routeData.view) {
|
||||||
const view = this.routeData.view;
|
const view = this.routeData.view;
|
||||||
let index = 0;
|
let index = 0;
|
||||||
for (let i = 0; i < views.length; i++) {
|
for (let i = 0; i < views.length; i++) {
|
||||||
if (views[i].id === view || i === parseInt(view)) {
|
if (views[i].id === view || i === parseInt(view)) {
|
||||||
index = i;
|
index = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (index !== this._curView) this._selectView(index);
|
if (index !== this._curView) this._selectView(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeViewId(id, index) {
|
_computeViewId(id, index) {
|
||||||
return id || index;
|
return id || index;
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeTitle(config) {
|
_computeTitle(config) {
|
||||||
return config.title || "Home Assistant";
|
return config.title || "Home Assistant";
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeTabsHidden(views) {
|
_computeTabsHidden(views) {
|
||||||
return views.length < 2;
|
return views.length < 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeTabTitle(title) {
|
_computeTabTitle(title) {
|
||||||
return title || "Unnamed view";
|
return title || "Unnamed view";
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleRefresh() {
|
_handleRefresh() {
|
||||||
this.fire("config-refresh");
|
this.fire("config-refresh");
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleUnusedEntities() {
|
_handleUnusedEntities() {
|
||||||
this._selectView("unused");
|
this._selectView("unused");
|
||||||
}
|
}
|
||||||
|
|
||||||
_deselect(ev) {
|
_deselect(ev) {
|
||||||
ev.target.selected = null;
|
ev.target.selected = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleHelp() {
|
_handleHelp() {
|
||||||
window.open("https://www.home-assistant.io/lovelace/", "_blank");
|
window.open("https://www.home-assistant.io/lovelace/", "_blank");
|
||||||
}
|
}
|
||||||
|
|
||||||
_editModeEnable() {
|
_editModeEnable() {
|
||||||
this._editMode = true;
|
this._editMode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_editModeDisable() {
|
_editModeDisable() {
|
||||||
this._editMode = false;
|
this._editMode = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_editModeChanged() {
|
_editModeChanged() {
|
||||||
this._selectView(this._curView);
|
this._selectView(this._curView);
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleViewSelected(ev) {
|
_handleViewSelected(ev) {
|
||||||
const index = ev.detail.selected;
|
const index = ev.detail.selected;
|
||||||
if (index !== this._curView) {
|
if (index !== this._curView) {
|
||||||
const id = this.config.views[index].id || index;
|
const id = this.config.views[index].id || index;
|
||||||
this.navigate(`/lovelace/${id}`);
|
this.navigate(`/lovelace/${id}`);
|
||||||
}
|
}
|
||||||
scrollToTarget(this, this.$.layout.header.scrollTarget);
|
scrollToTarget(this, this.$.layout.header.scrollTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
_selectView(viewIndex) {
|
_selectView(viewIndex) {
|
||||||
this._curView = viewIndex;
|
this._curView = viewIndex;
|
||||||
|
|
||||||
// Recreate a new element to clear the applied themes.
|
// Recreate a new element to clear the applied themes.
|
||||||
const root = this.$.view;
|
const root = this.$.view;
|
||||||
if (root.lastChild) {
|
if (root.lastChild) {
|
||||||
root.removeChild(root.lastChild);
|
root.removeChild(root.lastChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
let view;
|
let view;
|
||||||
let background = this.config.background || "";
|
let background = this.config.background || "";
|
||||||
|
|
||||||
if (viewIndex === "unused") {
|
if (viewIndex === "unused") {
|
||||||
view = document.createElement("hui-unused-entities");
|
view = document.createElement("hui-unused-entities");
|
||||||
view.config = this.config;
|
view.config = this.config;
|
||||||
} else {
|
} else {
|
||||||
const viewConfig = this.config.views[this._curView];
|
const viewConfig = this.config.views[this._curView];
|
||||||
if (viewConfig.panel) {
|
if (viewConfig.panel) {
|
||||||
view = createCardElement(viewConfig.cards[0]);
|
view = createCardElement(viewConfig.cards[0]);
|
||||||
view.isPanel = true;
|
view.isPanel = true;
|
||||||
view.editMode = this._editMode;
|
view.editMode = this._editMode;
|
||||||
} else {
|
} else {
|
||||||
view = document.createElement("hui-view");
|
view = document.createElement("hui-view");
|
||||||
view.config = viewConfig;
|
view.config = viewConfig;
|
||||||
view.columns = this.columns;
|
view.columns = this.columns;
|
||||||
view.editMode = this._editMode;
|
view.editMode = this._editMode;
|
||||||
}
|
}
|
||||||
if (viewConfig.background) background = viewConfig.background;
|
if (viewConfig.background) background = viewConfig.background;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$.view.style.background = background;
|
this.$.view.style.background = background;
|
||||||
|
|
||||||
view.hass = this.hass;
|
view.hass = this.hass;
|
||||||
root.appendChild(view);
|
root.appendChild(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
_hassChanged(hass) {
|
_hassChanged(hass) {
|
||||||
if (!this.$.view.lastChild) return;
|
if (!this.$.view.lastChild) return;
|
||||||
this.$.view.lastChild.hass = hass;
|
this.$.view.lastChild.hass = hass;
|
||||||
}
|
}
|
||||||
|
|
||||||
_configChanged(config) {
|
_configChanged(config) {
|
||||||
this._loadResources(config.resources || []);
|
this._loadResources(config.resources || []);
|
||||||
// On config change, recreate the view from scratch.
|
// On config change, recreate the view from scratch.
|
||||||
this._selectView(this._curView);
|
this._selectView(this._curView);
|
||||||
this.$.view.classList.toggle("tabs-hidden", config.views.length < 2);
|
this.$.view.classList.toggle("tabs-hidden", config.views.length < 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
_columnsChanged(columns) {
|
_columnsChanged(columns) {
|
||||||
if (!this.$.view.lastChild) return;
|
if (!this.$.view.lastChild) return;
|
||||||
this.$.view.lastChild.columns = columns;
|
this.$.view.lastChild.columns = columns;
|
||||||
}
|
}
|
||||||
|
|
||||||
_loadResources(resources) {
|
_loadResources(resources) {
|
||||||
resources.forEach((resource) => {
|
resources.forEach((resource) => {
|
||||||
switch (resource.type) {
|
switch (resource.type) {
|
||||||
case "css":
|
case "css":
|
||||||
if (resource.url in CSS_CACHE) break;
|
if (resource.url in CSS_CACHE) break;
|
||||||
CSS_CACHE[resource.url] = loadCSS(resource.url);
|
CSS_CACHE[resource.url] = loadCSS(resource.url);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "js":
|
case "js":
|
||||||
if (resource.url in JS_CACHE) break;
|
if (resource.url in JS_CACHE) break;
|
||||||
JS_CACHE[resource.url] = loadJS(resource.url);
|
JS_CACHE[resource.url] = loadJS(resource.url);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "module":
|
case "module":
|
||||||
loadModule(resource.url);
|
loadModule(resource.url);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "html":
|
case "html":
|
||||||
import(/* webpackChunkName: "import-href-polyfill" */ "../../resources/html-import/import-href").then(
|
import(/* webpackChunkName: "import-href-polyfill" */ "../../resources/html-import/import-href").then(
|
||||||
({ importHref }) => importHref(resource.url)
|
({ importHref }) => importHref(resource.url)
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
console.warn("Unknown resource type specified: ${resource.type}");
|
console.warn("Unknown resource type specified: ${resource.type}");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define("hui-root", HUIRoot);
|
customElements.define("hui-root", HUIRoot);
|
||||||
|
|||||||
@@ -1,61 +1,61 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import computeUnusedEntities from "./common/compute-unused-entities";
|
import computeUnusedEntities from "./common/compute-unused-entities";
|
||||||
import createCardElement from "./common/create-card-element";
|
import createCardElement from "./common/create-card-element";
|
||||||
|
|
||||||
import "./cards/hui-entities-card.ts";
|
import "./cards/hui-entities-card.ts";
|
||||||
|
|
||||||
class HuiUnusedEntities extends PolymerElement {
|
class HuiUnusedEntities extends PolymerElement {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
#root {
|
#root {
|
||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 8px 0;
|
padding: 8px 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
observer: "_hassChanged",
|
observer: "_hassChanged",
|
||||||
},
|
},
|
||||||
config: {
|
config: {
|
||||||
type: Object,
|
type: Object,
|
||||||
observer: "_configChanged",
|
observer: "_configChanged",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_configChanged(config) {
|
_configChanged(config) {
|
||||||
const root = this.$.root;
|
const root = this.$.root;
|
||||||
if (root.lastChild) root.removeChild(root.lastChild);
|
if (root.lastChild) root.removeChild(root.lastChild);
|
||||||
|
|
||||||
const entities = computeUnusedEntities(this.hass, config).map((entity) => ({
|
const entities = computeUnusedEntities(this.hass, config).map((entity) => ({
|
||||||
entity,
|
entity,
|
||||||
secondary_info: "entity-id",
|
secondary_info: "entity-id",
|
||||||
}));
|
}));
|
||||||
const cardConfig = {
|
const cardConfig = {
|
||||||
type: "entities",
|
type: "entities",
|
||||||
title: "Unused entities",
|
title: "Unused entities",
|
||||||
entities,
|
entities,
|
||||||
show_header_toggle: false,
|
show_header_toggle: false,
|
||||||
};
|
};
|
||||||
const element = createCardElement(cardConfig);
|
const element = createCardElement(cardConfig);
|
||||||
element.hass = this.hass;
|
element.hass = this.hass;
|
||||||
root.appendChild(element);
|
root.appendChild(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
_hassChanged(hass) {
|
_hassChanged(hass) {
|
||||||
const root = this.$.root;
|
const root = this.$.root;
|
||||||
if (!root.lastChild) return;
|
if (!root.lastChild) return;
|
||||||
root.lastChild.hass = hass;
|
root.lastChild.hass = hass;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define("hui-unused-entities", HuiUnusedEntities);
|
customElements.define("hui-unused-entities", HuiUnusedEntities);
|
||||||
|
|||||||
@@ -1,218 +1,218 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import "../../components/entity/ha-state-label-badge";
|
import "../../components/entity/ha-state-label-badge";
|
||||||
import "./components/hui-card-options.ts";
|
import "./components/hui-card-options.ts";
|
||||||
|
|
||||||
import applyThemesOnElement from "../../common/dom/apply_themes_on_element";
|
import applyThemesOnElement from "../../common/dom/apply_themes_on_element";
|
||||||
|
|
||||||
import createCardElement from "./common/create-card-element";
|
import createCardElement from "./common/create-card-element";
|
||||||
import computeCardSize from "./common/compute-card-size";
|
import computeCardSize from "./common/compute-card-size";
|
||||||
|
|
||||||
class HUIView extends PolymerElement {
|
class HUIView extends PolymerElement {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
:host {
|
:host {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 4px 4px 0;
|
padding: 4px 4px 0;
|
||||||
transform: translateZ(0);
|
transform: translateZ(0);
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
#badges {
|
#badges {
|
||||||
margin: 8px 16px;
|
margin: 8px 16px;
|
||||||
font-size: 85%;
|
font-size: 85%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#columns {
|
#columns {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.column {
|
.column {
|
||||||
flex-basis: 0;
|
flex-basis: 0;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.column > * {
|
.column > * {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 4px 4px 8px;
|
margin: 4px 4px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 500px) {
|
@media (max-width: 500px) {
|
||||||
:host {
|
:host {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.column > * {
|
.column > * {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 599px) {
|
@media (max-width: 599px) {
|
||||||
.column {
|
.column {
|
||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<div id="badges"></div>
|
<div id="badges"></div>
|
||||||
<div id="columns"></div>
|
<div id="columns"></div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
observer: "_hassChanged",
|
observer: "_hassChanged",
|
||||||
},
|
},
|
||||||
config: Object,
|
config: Object,
|
||||||
columns: Number,
|
columns: Number,
|
||||||
editMode: Boolean,
|
editMode: Boolean,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static get observers() {
|
static get observers() {
|
||||||
return [
|
return [
|
||||||
// Put all properties in 1 observer so we only call configChanged once
|
// Put all properties in 1 observer so we only call configChanged once
|
||||||
"_createBadges(config)",
|
"_createBadges(config)",
|
||||||
"_createCards(config, columns, editMode)",
|
"_createCards(config, columns, editMode)",
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this._cards = [];
|
this._cards = [];
|
||||||
this._badges = [];
|
this._badges = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
_createBadges(config) {
|
_createBadges(config) {
|
||||||
const root = this.$.badges;
|
const root = this.$.badges;
|
||||||
while (root.lastChild) {
|
while (root.lastChild) {
|
||||||
root.removeChild(root.lastChild);
|
root.removeChild(root.lastChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config || !config.badges || !Array.isArray(config.badges)) {
|
if (!config || !config.badges || !Array.isArray(config.badges)) {
|
||||||
root.style.display = "none";
|
root.style.display = "none";
|
||||||
this._badges = [];
|
this._badges = [];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const elements = [];
|
const elements = [];
|
||||||
for (const entityId of config.badges) {
|
for (const entityId of config.badges) {
|
||||||
if (!(entityId in this.hass.states)) continue;
|
if (!(entityId in this.hass.states)) continue;
|
||||||
|
|
||||||
const element = document.createElement("ha-state-label-badge");
|
const element = document.createElement("ha-state-label-badge");
|
||||||
element.setProperties({
|
element.setProperties({
|
||||||
hass: this.hass,
|
hass: this.hass,
|
||||||
state: this.hass.states[entityId],
|
state: this.hass.states[entityId],
|
||||||
});
|
});
|
||||||
elements.push({ element, entityId });
|
elements.push({ element, entityId });
|
||||||
root.appendChild(element);
|
root.appendChild(element);
|
||||||
}
|
}
|
||||||
this._badges = elements;
|
this._badges = elements;
|
||||||
root.style.display = elements.length > 0 ? "block" : "none";
|
root.style.display = elements.length > 0 ? "block" : "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
_createCards(config) {
|
_createCards(config) {
|
||||||
const root = this.$.columns;
|
const root = this.$.columns;
|
||||||
|
|
||||||
while (root.lastChild) {
|
while (root.lastChild) {
|
||||||
root.removeChild(root.lastChild);
|
root.removeChild(root.lastChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config || !config.cards || !Array.isArray(config.cards)) {
|
if (!config || !config.cards || !Array.isArray(config.cards)) {
|
||||||
this._cards = [];
|
this._cards = [];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const elements = [];
|
const elements = [];
|
||||||
const elementsToAppend = [];
|
const elementsToAppend = [];
|
||||||
for (const cardConfig of config.cards) {
|
for (const cardConfig of config.cards) {
|
||||||
const element = createCardElement(cardConfig);
|
const element = createCardElement(cardConfig);
|
||||||
element.hass = this.hass;
|
element.hass = this.hass;
|
||||||
elements.push(element);
|
elements.push(element);
|
||||||
|
|
||||||
if (!this.editMode) {
|
if (!this.editMode) {
|
||||||
elementsToAppend.push(element);
|
elementsToAppend.push(element);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const wrapper = document.createElement("hui-card-options");
|
const wrapper = document.createElement("hui-card-options");
|
||||||
wrapper.hass = this.hass;
|
wrapper.hass = this.hass;
|
||||||
wrapper.cardId = cardConfig.id;
|
wrapper.cardId = cardConfig.id;
|
||||||
wrapper.editMode = this.editMode;
|
wrapper.editMode = this.editMode;
|
||||||
wrapper.appendChild(element);
|
wrapper.appendChild(element);
|
||||||
elementsToAppend.push(wrapper);
|
elementsToAppend.push(wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
let columns = [];
|
let columns = [];
|
||||||
const columnEntityCount = [];
|
const columnEntityCount = [];
|
||||||
for (let i = 0; i < this.columns; i++) {
|
for (let i = 0; i < this.columns; i++) {
|
||||||
columns.push([]);
|
columns.push([]);
|
||||||
columnEntityCount.push(0);
|
columnEntityCount.push(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find column with < 5 entities, else column with lowest count
|
// Find column with < 5 entities, else column with lowest count
|
||||||
function getColumnIndex(size) {
|
function getColumnIndex(size) {
|
||||||
let minIndex = 0;
|
let minIndex = 0;
|
||||||
for (let i = 0; i < columnEntityCount.length; i++) {
|
for (let i = 0; i < columnEntityCount.length; i++) {
|
||||||
if (columnEntityCount[i] < 5) {
|
if (columnEntityCount[i] < 5) {
|
||||||
minIndex = i;
|
minIndex = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (columnEntityCount[i] < columnEntityCount[minIndex]) {
|
if (columnEntityCount[i] < columnEntityCount[minIndex]) {
|
||||||
minIndex = i;
|
minIndex = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
columnEntityCount[minIndex] += size;
|
columnEntityCount[minIndex] += size;
|
||||||
|
|
||||||
return minIndex;
|
return minIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
elements.forEach((el, index) => {
|
elements.forEach((el, index) => {
|
||||||
const cardSize = computeCardSize(el);
|
const cardSize = computeCardSize(el);
|
||||||
// Element to append might be the wrapped card when we're editing.
|
// Element to append might be the wrapped card when we're editing.
|
||||||
columns[getColumnIndex(cardSize)].push(elementsToAppend[index]);
|
columns[getColumnIndex(cardSize)].push(elementsToAppend[index]);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Remove empty columns
|
// Remove empty columns
|
||||||
columns = columns.filter((val) => val.length > 0);
|
columns = columns.filter((val) => val.length > 0);
|
||||||
|
|
||||||
columns.forEach((column) => {
|
columns.forEach((column) => {
|
||||||
const columnEl = document.createElement("div");
|
const columnEl = document.createElement("div");
|
||||||
columnEl.classList.add("column");
|
columnEl.classList.add("column");
|
||||||
column.forEach((el) => columnEl.appendChild(el));
|
column.forEach((el) => columnEl.appendChild(el));
|
||||||
root.appendChild(columnEl);
|
root.appendChild(columnEl);
|
||||||
});
|
});
|
||||||
|
|
||||||
this._cards = elements;
|
this._cards = elements;
|
||||||
|
|
||||||
if ("theme" in config) {
|
if ("theme" in config) {
|
||||||
applyThemesOnElement(root, this.hass.themes, config.theme);
|
applyThemesOnElement(root, this.hass.themes, config.theme);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_hassChanged(hass) {
|
_hassChanged(hass) {
|
||||||
this._badges.forEach((badge) => {
|
this._badges.forEach((badge) => {
|
||||||
const { element, entityId } = badge;
|
const { element, entityId } = badge;
|
||||||
element.setProperties({
|
element.setProperties({
|
||||||
hass,
|
hass,
|
||||||
state: hass.states[entityId],
|
state: hass.states[entityId],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this._cards.forEach((element) => {
|
this._cards.forEach((element) => {
|
||||||
element.hass = hass;
|
element.hass = hass;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-view", HUIView);
|
customElements.define("hui-view", HUIView);
|
||||||
|
|||||||
@@ -1,93 +1,93 @@
|
|||||||
import { html, LitElement } from "@polymer/lit-element";
|
import { html, LitElement } from "@polymer/lit-element";
|
||||||
import "@polymer/paper-button/paper-button";
|
import "@polymer/paper-button/paper-button";
|
||||||
|
|
||||||
import "../../../components/ha-icon";
|
import "../../../components/ha-icon";
|
||||||
|
|
||||||
import callService from "../common/call-service";
|
import callService from "../common/call-service";
|
||||||
import { EntityRow, CallServiceConfig } from "../entity-rows/types";
|
import { EntityRow, CallServiceConfig } from "../entity-rows/types";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
class HuiCallServiceRow extends LitElement implements EntityRow {
|
class HuiCallServiceRow extends LitElement implements EntityRow {
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
private _config?: CallServiceConfig;
|
private _config?: CallServiceConfig;
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: {},
|
hass: {},
|
||||||
_config: {},
|
_config: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public setConfig(config: CallServiceConfig): void {
|
public setConfig(config: CallServiceConfig): void {
|
||||||
if (!config || !config.name || !config.service) {
|
if (!config || !config.name || !config.service) {
|
||||||
throw new Error("Error in card configuration.");
|
throw new Error("Error in card configuration.");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = { icon: "hass:remote", action_name: "Run", ...config };
|
this._config = { icon: "hass:remote", action_name: "Run", ...config };
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this._config) {
|
if (!this._config) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${this.renderStyle()}
|
${this.renderStyle()}
|
||||||
<ha-icon .icon="${this._config.icon}"></ha-icon>
|
<ha-icon .icon="${this._config.icon}"></ha-icon>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div>
|
<div>
|
||||||
${this._config.name}
|
${this._config.name}
|
||||||
</div>
|
</div>
|
||||||
<paper-button
|
<paper-button
|
||||||
@click="${this._callService}"
|
@click="${this._callService}"
|
||||||
>${this._config.action_name}</paper-button>
|
>${this._config.action_name}</paper-button>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderStyle(): TemplateResult {
|
private renderStyle(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
:host {
|
:host {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
ha-icon {
|
ha-icon {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
color: var(--paper-item-icon-color);
|
color: var(--paper-item-icon-color);
|
||||||
}
|
}
|
||||||
.flex {
|
.flex {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-left: 16px;
|
margin-left: 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
.flex div {
|
.flex div {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
paper-button {
|
paper-button {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
margin-right: -.57em;
|
margin-right: -.57em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _callService() {
|
private _callService() {
|
||||||
callService(this._config, this.hass);
|
callService(this._config, this.hass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-call-service-row": HuiCallServiceRow;
|
"hui-call-service-row": HuiCallServiceRow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-call-service-row", HuiCallServiceRow);
|
customElements.define("hui-call-service-row", HuiCallServiceRow);
|
||||||
|
|||||||
@@ -1,53 +1,53 @@
|
|||||||
import { html, LitElement } from "@polymer/lit-element";
|
import { html, LitElement } from "@polymer/lit-element";
|
||||||
import { EntityRow, DividerConfig } from "../entity-rows/types";
|
import { EntityRow, DividerConfig } from "../entity-rows/types";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
class HuiDividerRow extends LitElement implements EntityRow {
|
class HuiDividerRow extends LitElement implements EntityRow {
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
private _config?: DividerConfig;
|
private _config?: DividerConfig;
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
_config: {},
|
_config: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public setConfig(config): void {
|
public setConfig(config): void {
|
||||||
if (!config) {
|
if (!config) {
|
||||||
throw new Error("Error in card configuration.");
|
throw new Error("Error in card configuration.");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = {
|
this._config = {
|
||||||
style: {
|
style: {
|
||||||
height: "1px",
|
height: "1px",
|
||||||
"background-color": "var(--secondary-text-color)",
|
"background-color": "var(--secondary-text-color)",
|
||||||
},
|
},
|
||||||
...config,
|
...config,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this._config) {
|
if (!this._config) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
const el = document.createElement("div");
|
const el = document.createElement("div");
|
||||||
|
|
||||||
Object.keys(this._config.style).forEach((prop) => {
|
Object.keys(this._config.style).forEach((prop) => {
|
||||||
el.style.setProperty(prop, this._config!.style[prop]);
|
el.style.setProperty(prop, this._config!.style[prop]);
|
||||||
});
|
});
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${el}
|
${el}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-divider-row": HuiDividerRow;
|
"hui-divider-row": HuiDividerRow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-divider-row", HuiDividerRow);
|
customElements.define("hui-divider-row", HuiDividerRow);
|
||||||
|
|||||||
@@ -1,70 +1,70 @@
|
|||||||
import { html, LitElement } from "@polymer/lit-element";
|
import { html, LitElement } from "@polymer/lit-element";
|
||||||
import { EntityRow, SectionConfig } from "../entity-rows/types";
|
import { EntityRow, SectionConfig } from "../entity-rows/types";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
|
|
||||||
import "../../../components/ha-icon";
|
import "../../../components/ha-icon";
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
class HuiSectionRow extends LitElement implements EntityRow {
|
class HuiSectionRow extends LitElement implements EntityRow {
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
private _config?: SectionConfig;
|
private _config?: SectionConfig;
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
_config: {},
|
_config: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public setConfig(config: SectionConfig): void {
|
public setConfig(config: SectionConfig): void {
|
||||||
if (!config) {
|
if (!config) {
|
||||||
throw new Error("Error in card configuration.");
|
throw new Error("Error in card configuration.");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this._config) {
|
if (!this._config) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${this.renderStyle()}
|
${this.renderStyle()}
|
||||||
<div class=divider></div>
|
<div class=divider></div>
|
||||||
${
|
${
|
||||||
this._config.label
|
this._config.label
|
||||||
? html`<div class="label">${this._config.label}</div>`
|
? html`<div class="label">${this._config.label}</div>`
|
||||||
: html``
|
: html``
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderStyle(): TemplateResult {
|
private renderStyle(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
.label {
|
.label {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
.divider {
|
.divider {
|
||||||
height: 1px;
|
height: 1px;
|
||||||
background-color: var(--secondary-text-color);
|
background-color: var(--secondary-text-color);
|
||||||
opacity: 0.25;
|
opacity: 0.25;
|
||||||
margin-left: -16px;
|
margin-left: -16px;
|
||||||
margin-right: -16px;
|
margin-right: -16px;
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-section-row": HuiSectionRow;
|
"hui-section-row": HuiSectionRow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-section-row", HuiSectionRow);
|
customElements.define("hui-section-row", HuiSectionRow);
|
||||||
|
|||||||
@@ -1,75 +1,75 @@
|
|||||||
import { html, LitElement } from "@polymer/lit-element";
|
import { html, LitElement } from "@polymer/lit-element";
|
||||||
import { EntityRow, WeblinkConfig } from "../entity-rows/types";
|
import { EntityRow, WeblinkConfig } from "../entity-rows/types";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
|
|
||||||
import "../../../components/ha-icon";
|
import "../../../components/ha-icon";
|
||||||
|
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
class HuiWeblinkRow extends LitElement implements EntityRow {
|
class HuiWeblinkRow extends LitElement implements EntityRow {
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
private _config?: WeblinkConfig;
|
private _config?: WeblinkConfig;
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
_config: {},
|
_config: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public setConfig(config: WeblinkConfig): void {
|
public setConfig(config: WeblinkConfig): void {
|
||||||
if (!config || !config.url) {
|
if (!config || !config.url) {
|
||||||
throw new Error("Invalid Configuration: 'url' required");
|
throw new Error("Invalid Configuration: 'url' required");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = {
|
this._config = {
|
||||||
icon: "hass:link",
|
icon: "hass:link",
|
||||||
name: config.url,
|
name: config.url,
|
||||||
...config,
|
...config,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this._config) {
|
if (!this._config) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${this.renderStyle()}
|
${this.renderStyle()}
|
||||||
<a href="${this._config.url}">
|
<a href="${this._config.url}">
|
||||||
<ha-icon .icon="${this._config.icon}"></ha-icon>
|
<ha-icon .icon="${this._config.icon}"></ha-icon>
|
||||||
<div>${this._config.name}</div>
|
<div>${this._config.name}</div>
|
||||||
</a>
|
</a>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderStyle(): TemplateResult {
|
private renderStyle(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
a {
|
a {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
ha-icon {
|
ha-icon {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
color: var(--paper-item-icon-color);
|
color: var(--paper-item-icon-color);
|
||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
margin-left: 16px;
|
margin-left: 16px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-weblink-row": HuiWeblinkRow;
|
"hui-weblink-row": HuiWeblinkRow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("hui-weblink-row", HuiWeblinkRow);
|
customElements.define("hui-weblink-row", HuiWeblinkRow);
|
||||||
|
|||||||
@@ -1,53 +1,53 @@
|
|||||||
import assert from "assert";
|
import assert from "assert";
|
||||||
|
|
||||||
import parseAspectRatio from "../../../src/common/util/parse-aspect-ratio";
|
import parseAspectRatio from "../../../src/common/util/parse-aspect-ratio";
|
||||||
|
|
||||||
describe("parseAspectRatio", () => {
|
describe("parseAspectRatio", () => {
|
||||||
const ratio16by9 = { w: 16, h: 9 };
|
const ratio16by9 = { w: 16, h: 9 };
|
||||||
const ratio178 = { w: 1.78, h: 1 };
|
const ratio178 = { w: 1.78, h: 1 };
|
||||||
|
|
||||||
it("Parses 16x9", () => {
|
it("Parses 16x9", () => {
|
||||||
const r = parseAspectRatio("16x9");
|
const r = parseAspectRatio("16x9");
|
||||||
assert.deepEqual(r, ratio16by9);
|
assert.deepEqual(r, ratio16by9);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Parses 16:9", () => {
|
it("Parses 16:9", () => {
|
||||||
const r = parseAspectRatio("16:9");
|
const r = parseAspectRatio("16:9");
|
||||||
assert.deepEqual(r, ratio16by9);
|
assert.deepEqual(r, ratio16by9);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Parses 1.78x1", () => {
|
it("Parses 1.78x1", () => {
|
||||||
const r = parseAspectRatio("1.78x1");
|
const r = parseAspectRatio("1.78x1");
|
||||||
assert.deepEqual(r, ratio178);
|
assert.deepEqual(r, ratio178);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Parses 1.78:1", () => {
|
it("Parses 1.78:1", () => {
|
||||||
const r = parseAspectRatio("1.78:1");
|
const r = parseAspectRatio("1.78:1");
|
||||||
assert.deepEqual(r, ratio178);
|
assert.deepEqual(r, ratio178);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Parses 1.78", () => {
|
it("Parses 1.78", () => {
|
||||||
const r = parseAspectRatio("1.78");
|
const r = parseAspectRatio("1.78");
|
||||||
assert.deepEqual(r, ratio178);
|
assert.deepEqual(r, ratio178);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Skips null states", () => {
|
it("Skips null states", () => {
|
||||||
const r = parseAspectRatio(null);
|
const r = parseAspectRatio(null);
|
||||||
assert.equal(r, null);
|
assert.equal(r, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Skips empty states", () => {
|
it("Skips empty states", () => {
|
||||||
const r = parseAspectRatio(" ");
|
const r = parseAspectRatio(" ");
|
||||||
assert.equal(r, null);
|
assert.equal(r, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Skips invalid input", () => {
|
it("Skips invalid input", () => {
|
||||||
const r = parseAspectRatio("mary had a little lamb");
|
const r = parseAspectRatio("mary had a little lamb");
|
||||||
assert.equal(r, null);
|
assert.equal(r, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Skips invalid, but close input", () => {
|
it("Skips invalid, but close input", () => {
|
||||||
const r = parseAspectRatio("mary:lamb");
|
const r = parseAspectRatio("mary:lamb");
|
||||||
assert.equal(r, null);
|
assert.equal(r, null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user