Add shaded area to show when calling for heat (#617)

Adds a series to the climate graph to shade the area under the current
temperature line red when the thermostat is calling for heat.

Uses the current temperature because it's guaranteed to be in the right
temperature range for the graph and it should minimize overlap since by
definition when the thermostat is calling for heat the current temp would
be below the target temp.

Uses `steppedArea` because most other series types don't handle
intermittent (i.e. with sections of `null` throughout) data very well.
This commit is contained in:
Klaas Hoekema 2017-11-14 23:44:24 -05:00 committed by Paulus Schoutsen
parent 8cf0c0e94d
commit de87c5b19b
2 changed files with 35 additions and 20 deletions

View File

@ -79,7 +79,7 @@
}
if (!this.chartEngine) {
this.chartEngine = new window.google.visualization.LineChart(this);
this.chartEngine = new window.google.visualization.ComboChart(this);
}
if (deviceStates.length === 0) {
@ -105,6 +105,12 @@
axis: 'horizontal',
maxZoomIn: 0.1,
},
seriesType: 'line',
// The "heating" series uses steppedArea to shade the area below the current
// temperature when the thermostat is calling for heat. It would be nice to
// apply this config in a more direct way than by column index, but it
// doesn't seem to be possible.
series: { 1: { type: 'steppedArea' } }
};
if (this.isSingleDevice) {
@ -146,6 +152,7 @@
var hasTargetRange;
var processState;
var noInterpolations;
var series;
dataTable.addColumn({ type: 'datetime', id: 'Time' });
function pushData(values, noInterpolationValues) {
@ -175,33 +182,41 @@
}, false);
dataTable.addColumn('number', name + ' current temperature');
dataTable.addColumn('number', name + ' heating');
if (hasTargetRange) {
dataTable.addColumn('number', name + ' target temperature high');
dataTable.addColumn('number', name + ' target temperature low');
noInterpolations = [false, true, true];
} else {
dataTable.addColumn('number', name + ' target temperature');
}
processState = function (state) {
var curTemp = saveParseFloat(state.attributes.current_temperature);
// Drawing the 'heating' area up to the current temp should keep it from
// overlapping but avoid any weird gaps or range mismatches
var heating = state.attributes.operation === 'heat' ? curTemp : null;
series = [curTemp, heating];
noInterpolations = [false, true];
if (hasTargetRange) {
var targetHigh = saveParseFloat(state.attributes.target_temp_high);
var targetLow = saveParseFloat(state.attributes.target_temp_low);
series = series.concat([targetHigh, targetLow]);
noInterpolations = noInterpolations.concat([true, true]);
} else {
var target = saveParseFloat(state.attributes.temperature);
series.push(target);
noInterpolations.push(true);
}
pushData(
[new Date(state.last_changed), curTemp, targetHigh, targetLow],
[new Date(state.last_changed)].concat(series),
noInterpolations
);
};
} else {
dataTable.addColumn('number', name + ' target temperature');
noInterpolations = [false, true];
processState = function (state) {
var curTemp = saveParseFloat(state.attributes.current_temperature);
var target = saveParseFloat(state.attributes.temperature);
pushData([new Date(state.last_changed), curTemp, target], noInterpolations);
};
}
states.states.forEach(processState);
} else {

View File

@ -5,7 +5,7 @@
const RECENT_THRESHOLD = 60000; // 1 minute
const RECENT_CACHE = {};
const DOMAINS_USE_LAST_UPDATED = ['thermostat', 'climate'];
const LINE_ATTRIBUTES_TO_KEEP = ['temperature', 'current_temperature', 'target_temp_low', 'target_temp_high'];
const LINE_ATTRIBUTES_TO_KEEP = ['temperature', 'current_temperature', 'target_temp_low', 'target_temp_high', 'operation'];
window.stateHistoryCache = window.stateHistoryCache || {};
function computeHistory(stateHistory) {