Adding Gauge Card to Lovelace (#1742)

* Commiting Only needed Files. Adds Gallery Entry

* Adding Attribute current_temperature to gallery entry config

* Fixing code from review and updating gallery

* Updating Gallery to show errors

* Resolving Reviews and updating gallery

* Deleting unused line

* Minor changes

* Address my own comments.
This commit is contained in:
Zack Arnett 2018-10-11 04:30:56 -04:00 committed by Paulus Schoutsen
parent 90c3350d40
commit 69eb007ea2
3 changed files with 286 additions and 0 deletions

View File

@ -0,0 +1,83 @@
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import '../components/demo-cards.js';
const CONFIGS = [
{
heading: 'Basic example',
config: `
- type: gauge
entity: sensor.brightness
`
},
{
heading: 'With title',
config: `
- type: gauge
title: Humidity
entity: sensor.outside_humidity
`
},
{
heading: 'Custom Unit of Measurement',
config: `
- type: gauge
entity: sensor.outside_temperature
unit_of_measurement: C
`
},
{
heading: 'Setting Severity Levels',
config: `
- type: gauge
entity: sensor.brightness
severity:
red: 32
green: 0
yellow: 23
`
},
{
heading: 'Setting Min and Max Values',
config: `
- type: gauge
entity: sensor.brightness
min: 0
max: 38
`
},
{
heading: 'Invalid Entity',
config: `
- type: gauge
entity: sensor.invalid_entity
`
},
{
heading: 'Non-Numeric Value',
config: `
- type: gauge
entity: plant.bonsai
`
},
];
class DemoGaugeEntity extends PolymerElement {
static get template() {
return html`
<demo-cards configs="[[_configs]]"></demo-cards>
`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS
}
};
}
}
customElements.define('demo-hui-gauge-card', DemoGaugeEntity);

View File

@ -0,0 +1,201 @@
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 = Object.assign({ 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

@ -19,6 +19,7 @@ import '../cards/hui-plant-status-card.js';
import '../cards/hui-sensor-card.js';
import '../cards/hui-vertical-stack-card.js';
import '../cards/hui-weather-forecast-card';
import '../cards/hui-gauge-card.js';
import createErrorCardConfig from './create-error-card-config.js';
@ -27,6 +28,7 @@ const CARD_TYPES = new Set([
'entities',
'entity-filter',
'error',
'gauge',
'glance',
'history-graph',
'horizontal-stack',