mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-15 13:26:34 +00:00
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:
parent
90c3350d40
commit
69eb007ea2
83
gallery/src/demos/demo-hui-gauge-card.js
Normal file
83
gallery/src/demos/demo-hui-gauge-card.js
Normal 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);
|
201
src/panels/lovelace/cards/hui-gauge-card.js
Normal file
201
src/panels/lovelace/cards/hui-gauge-card.js
Normal 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);
|
@ -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',
|
||||
|
Loading…
x
Reference in New Issue
Block a user