+
+
+ ${_entity.state}
+ ${this._computeUom(_entity)}
+
+
+
+ ${_line ? svg`
+
` : ''}
+
+
+ `;
+ }
+
+ _handleClick() {
+ this.fire('hass-more-info', { entityId: this._config.entity });
+ }
+
+ _computeIcon(item) {
+ return this._config.icon || stateIcon(item);
+ }
+
+ _computeName(item) {
+ return this._config.name || computeStateName(item);
+ }
+
+ _computeUom(item) {
+ return this._config.unit || item.attributes.unit_of_measurement;
+ }
+
+ _getGraph(items, width, height) {
+ const values = this._getValueArr(items);
+ const coords = this._calcCoordinates(values, width, height);
+ return this._getPath(coords);
+ }
+
+ _getValueArr(items) {
+ return items.map(item => Number(item.state) || 0);
+ }
+
+ _calcCoordinates(values, width, height) {
+ const margin = this._config.line_width;
+ width -= margin * 2;
+ height -= margin * 2;
+ const min = Math.floor(Math.min.apply(null, values) * 0.95);
+ const max = Math.ceil(Math.max.apply(null, values) * 1.05);
+
+ const yRatio = (max - min) / height;
+ const xRatio = width / (values.length - 1);
+
+ return values.map((value, i) => {
+ const y = height - ((value - min) / yRatio) || 0;
+ const x = (xRatio * i) + margin;
+ return [x, y];
+ });
+ }
+
+ _getPath(points) {
+ const SPACE = ' ';
+ let next; let Z;
+ const X = 0;
+ const Y = 1;
+ let path = '';
+ let point = points[0];
+
+ path += 'M' + point[X] + ',' + point[Y];
+ const first = point;
+
+ for (let i = 0; i < points.length; i++) {
+ next = points[i];
+ Z = this._midPoint(point[X], point[Y], next[X], next[Y]);
+ path += SPACE + Z[X] + ',' + Z[Y];
+ path += 'Q' + Math.floor(next[X]) + ',' + next[Y];
+ point = next;
+ }
+
+ const second = points[1];
+ Z = this._midPoint(first[X], first[Y], second[X], second[Y]);
+ path += SPACE + Math.floor(next[X]) + '.' + points[points.length - 1];
+ return path;
+ }
+
+ _midPoint(Ax, Ay, Bx, By) {
+ const Zx = (Ax - Bx) / 2 + Bx;
+ const Zy = (Ay - By) / 2 + By;
+ return [Zx, Zy];
+ }
+
+ async _getHistory() {
+ const endTime = new Date();
+ const startTime = new Date();
+ startTime.setHours(endTime.getHours() - this._config.hours_to_show);
+ const stateHistory = await this._fetchRecent(this._config.entity, startTime, endTime);
+ const history = stateHistory[0];
+ const valArray = [history[history.length - 1]];
+
+ let pos = history.length - 1;
+ const accuracy = (this._config.accuracy) <= pos ? this._config.accuracy : pos;
+ let increment = Math.ceil(history.length / accuracy);
+ increment = (increment <= 0) ? 1 : increment;
+ for (let i = accuracy; i >= 2; i--) {
+ pos -= increment;
+ valArray.unshift(pos >= 0 ? history[pos] : history[0]);
+ }
+ this._line = this._getGraph(valArray, 500, this._config.height);
+ }
+
+ async _fetchRecent(entityId, startTime, endTime) {
+ let url = 'history/period';
+ if (startTime) url += '/' + startTime.toISOString();
+ url += '?filter_entity_id=' + entityId;
+ if (endTime) url += '&end_time=' + endTime.toISOString();
+
+ return await this._hass.callApi('GET', url);
+ }
+
+ getCardSize() {
+ return 3;
+ }
+
+ _style() {
+ return html`
+ `;
+ }
+}
+
+customElements.define('hui-sensor-card', HuiSensorCard);
diff --git a/src/panels/lovelace/common/create-card-element.js b/src/panels/lovelace/common/create-card-element.js
index f2648cb78f..0975aef47f 100644
--- a/src/panels/lovelace/common/create-card-element.js
+++ b/src/panels/lovelace/common/create-card-element.js
@@ -16,6 +16,7 @@ import '../cards/hui-picture-elements-card';
import '../cards/hui-picture-entity-card';
import '../cards/hui-picture-glance-card';
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';
@@ -38,6 +39,7 @@ const CARD_TYPES = new Set([
'picture-entity',
'picture-glance',
'plant-status',
+ 'sensor',
'vertical-stack',
'weather-forecast'
]);