Gauge convert and fix issues (#1886)

* Gauge convert and fix issues

* Fixing Unit_of_Measurement

* Addressing Reviews

* Updating typing
This commit is contained in:
Zack Arnett 2018-10-28 15:47:21 -04:00 committed by Paulus Schoutsen
parent 8c155d4d0e
commit b6d0d777bf
3 changed files with 267 additions and 212 deletions

View File

@ -1,211 +0,0 @@
import { html } from "@polymer/polymer/lib/utils/html-tag.js";
import { PolymerElement } from "@polymer/polymer/polymer-element.js";
import "../../../components/ha-card.js";
import EventsMixin from "../../../mixins/events-mixin.js";
/*
* @appliesMixin EventsMixin
*/
class HuiGaugeCard extends EventsMixin(PolymerElement) {
static get template() {
return html`
<style>
ha-card {
--base-unit: 50px;
height: calc(var(--base-unit)*3);
position: relative;
cursor: pointer;
}
.container{
width: calc(var(--base-unit) * 4);
height: calc(var(--base-unit) * 2);
position: absolute;
top: calc(var(--base-unit)*1.5);
left: 50%;
overflow: hidden;
text-align: center;
transform: translate(-50%, -50%);
}
.gauge-a{
z-index: 1;
position: absolute;
background-color: var(--primary-background-color);
width: calc(var(--base-unit) * 4);
height: calc(var(--base-unit) * 2);
top: 0%;
border-radius:calc(var(--base-unit) * 2.5) calc(var(--base-unit) * 2.5) 0px 0px ;
}
.gauge-b{
z-index: 3;
position: absolute;
background-color: var(--paper-card-background-color);
width: calc(var(--base-unit) * 2.5);
height: calc(var(--base-unit) * 1.25);
top: calc(var(--base-unit) * 0.75);
margin-left: calc(var(--base-unit) * 0.75);
margin-right: auto;
border-radius: calc(var(--base-unit) * 2.5) calc(var(--base-unit) * 2.5) 0px 0px ;
}
.gauge-c{
z-index: 2;
position: absolute;
background-color: var(--label-badge-blue);
width: calc(var(--base-unit) * 4);
height: calc(var(--base-unit) * 2);
top: calc(var(--base-unit) * 2);
margin-left: auto;
margin-right: auto;
border-radius: 0px 0px calc(var(--base-unit) * 2) calc(var(--base-unit) * 2) ;
transform-origin: center top;
transition: all 1.3s ease-in-out;
}
.gauge-data{
z-index: 4;
color: var(--primary-text-color);
line-height: calc(var(--base-unit) * 0.3);
position: absolute;
width: calc(var(--base-unit) * 4);
height: calc(var(--base-unit) * 2.1);
top: calc(var(--base-unit) * 1.2);
margin-left: auto;
margin-right: auto;
transition: all 1s ease-out;
}
.gauge-data #percent{
font-size: calc(var(--base-unit) * 0.55);
}
.gauge-data #title{
padding-top: calc(var(--base-unit) * 0.15);
font-size: calc(var(--base-unit) * 0.30);
}
.not-found {
flex: 1;
background-color: yellow;
padding: 8px;
}
</style>
<ha-card on-click='_handleClick'>
<div class='container'>
<div class='gauge-a'></div>
<div class='gauge-b'></div>
<div class='gauge-c' id='gauge'></div>
<div class='gauge-data'>
<div id='percent'>[[_computeStateDisplay(_stateObj)]]</div>
<div id='title'>[[_computeTitle(_stateObj)]]</div>
</div>
</div>
</ha-card>
`;
}
static get properties() {
return {
hass: {
type: Object,
},
_config: Object,
_stateObj: {
type: Object,
computed: "_computeStateObj(hass.states, _config.entity)",
observer: "_stateObjChanged",
},
};
}
getCardSize() {
return 1;
}
setConfig(config) {
if (!config || !config.entity)
throw new Error("Invalid card configuration");
this._config = { min: 0, max: 100, ...config };
}
_computeStateObj(states, entityId) {
return states && entityId in states ? states[entityId] : null;
}
_stateObjChanged(stateObj) {
if (!stateObj || isNaN(stateObj.state)) return;
const config = this._config;
const turn = this._translateTurn(stateObj.state, config) / 10;
this.$.gauge.style.transform = `rotate(${turn}turn)`;
this.$.gauge.style.backgroundColor = this._computeSeverity(
stateObj.state,
config.severity
);
}
_computeStateDisplay(stateObj) {
if (!stateObj || isNaN(stateObj.state)) return "";
const unitOfMeasurement =
this._config.unit_of_measurement ||
stateObj.attributes.unit_of_measurement ||
"";
return `${stateObj.state} ${unitOfMeasurement}`;
}
_computeTitle(stateObj) {
if (!stateObj) {
this.$.title.className = "not-found";
return "Entity not available: " + this._config.entity;
}
if (isNaN(stateObj.state)) {
this.$.title.className = "not-found";
return "Entity is non-numeric: " + this._config.entity;
}
this.$.title.className = "";
return this._config.title;
}
_computeSeverity(stateValue, sections) {
const numberValue = Number(stateValue);
const severityMap = {
red: "var(--label-badge-red)",
green: "var(--label-badge-green)",
yellow: "var(--label-badge-yellow)",
normal: "var(--label-badge-blue)",
};
if (!sections) return severityMap.normal;
const sectionsArray = Object.keys(sections);
const sortable = sectionsArray.map((severity) => [
severity,
sections[severity],
]);
for (var i = 0; i < sortable.length; i++) {
if (severityMap[sortable[i][0]] == null || isNaN(sortable[i][1])) {
return severityMap.normal;
}
}
sortable.sort((a, b) => a[1] - b[1]);
if (numberValue >= sortable[0][1] && numberValue < sortable[1][1]) {
return severityMap[sortable[0][0]];
}
if (numberValue >= sortable[1][1] && numberValue < sortable[2][1]) {
return severityMap[sortable[1][0]];
}
if (numberValue >= sortable[2][1]) {
return severityMap[sortable[2][0]];
}
return severityMap.normal;
}
_translateTurn(value, config) {
return (5 * (value - config.min)) / (config.max - config.min);
}
_handleClick() {
this.fire("hass-more-info", { entityId: this._config.entity });
}
}
customElements.define("hui-gauge-card", HuiGaugeCard);

View File

@ -0,0 +1,266 @@
import {
html,
LitElement,
PropertyDeclarations,
PropertyValues,
} from "@polymer/lit-element";
import { LovelaceCard, LovelaceConfig } from "../types.js";
import { HomeAssistant } from "../../../types.js";
import isValidEntityId from "../../../common/entity/valid_entity_id.js";
import { fireEvent } from "../../../common/dom/fire_event.js";
import "../../../components/ha-card.js";
interface Config extends LovelaceConfig {
entity: string;
title?: string;
unit_of_measurement?: string;
min?: number;
max?: number;
severity?: object;
}
const severityMap = {
red: "var(--label-badge-red)",
green: "var(--label-badge-green)",
yellow: "var(--label-badge-yellow)",
normal: "var(--label-badge-blue)",
};
class HuiGaugeCard extends LitElement implements LovelaceCard {
public hass?: HomeAssistant;
private _config?: Config;
static get properties(): PropertyDeclarations {
return {
hass: {},
_config: {},
};
}
public getCardSize() {
return 2;
}
public setConfig(config) {
if (!config || !config.entity) {
throw new Error("Invalid card configuration");
}
if (!isValidEntityId(config.entity)) {
throw new Error("Invalid Entity");
}
this._config = { min: 0, max: 100, ...config };
}
protected render() {
if (!this._config || !this.hass) {
return html``;
}
const stateObj = this.hass.states[this._config.entity];
let error;
if (!stateObj) {
error = "Entity not available: " + this._config.entity;
} else if (isNaN(Number(stateObj.state))) {
error = "Entity is non-numeric: " + this._config.entity;
}
return html`
${this.renderStyle()}
<ha-card @click="${this._handleClick}">
${
error
? html`<div class="not-found">${error}</div>`
: html`
<div class='container'>
<div class='gauge-a'></div>
<div class='gauge-b'></div>
<div class='gauge-c' id='gauge'></div>
<div class='gauge-data'>
<div id='percent'>${stateObj.state}
${this._config.unit_of_measurement ||
stateObj.attributes.unit_of_measurement ||
""}
</div>
<div id='title'>${this._config.title}
</div>
</div>
</div>
`
}
</ha-card>
`;
}
protected shouldUpdate(changedProps: PropertyValues) {
if (changedProps.get("hass")) {
return (
(changedProps.get("hass") as any).states[this._config!.entity] !==
this.hass!.states[this._config!.entity]
);
}
if (changedProps.get("_config")) {
return changedProps.get("_config") !== this._config;
}
return (changedProps as unknown) as boolean;
}
protected updated() {
if (
!this._config ||
!this.hass ||
!this.shadowRoot!.getElementById("gauge")
) {
return;
}
const stateObj = this.hass.states[this._config.entity];
if (isNaN(Number(stateObj.state))) {
return;
}
const turn = this._translateTurn(Number(stateObj.state), this._config);
this.shadowRoot!.getElementById(
"gauge"
)!.style.cssText = `transform: rotate(${turn}turn); background-color: ${this._computeSeverity(
stateObj.state,
this._config.severity!
)}`;
(this.shadowRoot!.querySelector(
"ha-card"
)! as HTMLElement).style.setProperty(
"--base-unit",
this._computeBaseUnit()
);
}
private renderStyle() {
return html`
<style>
ha-card {
--base-unit: 50px;
height: calc(var(--base-unit)*3);
position: relative;
cursor: pointer;
}
.container{
width: calc(var(--base-unit) * 4);
height: calc(var(--base-unit) * 2);
position: absolute;
top: calc(var(--base-unit)*1.5);
left: 50%;
overflow: hidden;
text-align: center;
transform: translate(-50%, -50%);
}
.gauge-a{
z-index: 1;
position: absolute;
background-color: var(--primary-background-color);
width: calc(var(--base-unit) * 4);
height: calc(var(--base-unit) * 2);
top: 0%;
border-radius:calc(var(--base-unit) * 2.5) calc(var(--base-unit) * 2.5) 0px 0px ;
}
.gauge-b{
z-index: 3;
position: absolute;
background-color: var(--paper-card-background-color);
width: calc(var(--base-unit) * 2.5);
height: calc(var(--base-unit) * 1.25);
top: calc(var(--base-unit) * 0.75);
margin-left: calc(var(--base-unit) * 0.75);
margin-right: auto;
border-radius: calc(var(--base-unit) * 2.5) calc(var(--base-unit) * 2.5) 0px 0px ;
}
.gauge-c{
z-index: 2;
position: absolute;
background-color: var(--label-badge-blue);
width: calc(var(--base-unit) * 4);
height: calc(var(--base-unit) * 2);
top: calc(var(--base-unit) * 2);
margin-left: auto;
margin-right: auto;
border-radius: 0px 0px calc(var(--base-unit) * 2) calc(var(--base-unit) * 2) ;
transform-origin: center top;
transition: all 1.3s ease-in-out;
}
.gauge-data{
z-index: 4;
color: var(--primary-text-color);
line-height: calc(var(--base-unit) * 0.3);
position: absolute;
width: calc(var(--base-unit) * 4);
height: calc(var(--base-unit) * 2.1);
top: calc(var(--base-unit) * 1.2);
margin-left: auto;
margin-right: auto;
transition: all 1s ease-out;
}
.gauge-data #percent{
font-size: calc(var(--base-unit) * 0.55);
}
.gauge-data #title{
padding-top: calc(var(--base-unit) * 0.15);
font-size: calc(var(--base-unit) * 0.30);
}
.not-found {
flex: 1;
background-color: yellow;
padding: 8px;
}
</style>
`;
}
private _computeSeverity(stateValue: string, sections: object) {
const numberValue = Number(stateValue);
if (!sections) {
return severityMap.normal;
}
const sectionsArray = Object.keys(sections);
const sortable = sectionsArray.map((severity) => [
severity,
sections[severity],
]);
for (const severity of sortable) {
if (severityMap[severity[0]] == null || isNaN(severity[1])) {
return severityMap.normal;
}
}
sortable.sort((a, b) => a[1] - b[1]);
if (numberValue >= sortable[0][1] && numberValue < sortable[1][1]) {
return severityMap[sortable[0][0]];
}
if (numberValue >= sortable[1][1] && numberValue < sortable[2][1]) {
return severityMap[sortable[1][0]];
}
if (numberValue >= sortable[2][1]) {
return severityMap[sortable[2][0]];
}
return severityMap.normal;
}
private _translateTurn(value: number, config: Config) {
const maxTurnValue = Math.min(Math.max(value, config.min!), config.max!);
return (
(5 * (maxTurnValue - config.min!)) / (config.max! - config.min!) / 10
);
}
private _computeBaseUnit() {
return this.clientWidth < 200 ? this.clientWidth / 5 + "px" : "50px";
}
private _handleClick() {
fireEvent(this, "hass-more-info", { entityId: this._config!.entity });
}
}
customElements.define("hui-gauge-card", HuiGaugeCard);

View File

@ -22,7 +22,7 @@ import "../cards/hui-sensor-card.js";
import "../cards/hui-vertical-stack-card.ts";
import "../cards/hui-thermostat-card.ts";
import "../cards/hui-weather-forecast-card";
import "../cards/hui-gauge-card.js";
import "../cards/hui-gauge-card";
import createErrorCardConfig from "./create-error-card-config.js";