mirror of
https://github.com/beestat/app.git
synced 2025-06-03 05:36:51 -04:00
A number of improvements and bug fixes to Runtime Sensor Detail
This commit is contained in:
parent
04ff6369a7
commit
59199163ca
@ -24,22 +24,22 @@ beestat.style.color = {
|
|||||||
'bluegreen': {
|
'bluegreen': {
|
||||||
'light': '#2bcbba',
|
'light': '#2bcbba',
|
||||||
'base': '#0fb9b1',
|
'base': '#0fb9b1',
|
||||||
'dark': ''
|
'dark': '#00867e'
|
||||||
},
|
},
|
||||||
'lightblue': {
|
'lightblue': {
|
||||||
'light': '#45aaf2',
|
'light': '#45aaf2',
|
||||||
'base': '#2d98da',
|
'base': '#2d98da',
|
||||||
'dark': ''
|
'dark': '#147fc1'
|
||||||
},
|
},
|
||||||
'blue': {
|
'blue': {
|
||||||
'light': '#4b7bec',
|
'light': '#4b7bec',
|
||||||
'base': '#3867d6',
|
'base': '#3867d6',
|
||||||
'dark': ''
|
'dark': '#1f4ebd'
|
||||||
},
|
},
|
||||||
'purple': {
|
'purple': {
|
||||||
'light': '#a55eea',
|
'light': '#a55eea',
|
||||||
'base': '#8854d0',
|
'base': '#8854d0',
|
||||||
'dark': ''
|
'dark': '#6f3bb7'
|
||||||
},
|
},
|
||||||
'gray': {
|
'gray': {
|
||||||
'light': '#d1d8e0',
|
'light': '#d1d8e0',
|
||||||
|
@ -109,7 +109,7 @@ beestat.component.card.runtime_sensor_detail.prototype.decorate_contents_ = func
|
|||||||
}
|
}
|
||||||
|
|
||||||
var api_call = new beestat.api();
|
var api_call = new beestat.api();
|
||||||
Object.values(beestat.cache.sensor).forEach(function(sensor) {
|
this.get_sensors_().forEach(function(sensor) {
|
||||||
if (sensor.thermostat_id === beestat.setting('thermostat_id')) {
|
if (sensor.thermostat_id === beestat.setting('thermostat_id')) {
|
||||||
api_call.add_call(
|
api_call.add_call(
|
||||||
'runtime_sensor',
|
'runtime_sensor',
|
||||||
@ -276,7 +276,8 @@ beestat.component.card.runtime_sensor_detail.prototype.get_data_ = function() {
|
|||||||
'title': this.get_title_(),
|
'title': this.get_title_(),
|
||||||
'subtitle': this.get_subtitle_(),
|
'subtitle': this.get_subtitle_(),
|
||||||
'y_min': Infinity,
|
'y_min': Infinity,
|
||||||
'y_max': -Infinity
|
'y_max': -Infinity,
|
||||||
|
'sensors': null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -289,8 +290,15 @@ beestat.component.card.runtime_sensor_detail.prototype.get_data_ = function() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
var series_codes = [];
|
var series_codes = [];
|
||||||
Object.values(beestat.cache.sensor).forEach(function(sensor) {
|
|
||||||
|
// Get and sort all the sensors.
|
||||||
|
var sensors = this.get_sensors_();
|
||||||
|
data.metadata.sensors = sensors;
|
||||||
|
|
||||||
|
// Set up the series_codes.
|
||||||
|
sensors.forEach(function(sensor) {
|
||||||
if (sensor.thermostat_id === beestat.setting('thermostat_id')) {
|
if (sensor.thermostat_id === beestat.setting('thermostat_id')) {
|
||||||
series_codes.push('temperature_' + sensor.sensor_id);
|
series_codes.push('temperature_' + sensor.sensor_id);
|
||||||
series_codes.push('occupancy_' + sensor.sensor_id);
|
series_codes.push('occupancy_' + sensor.sensor_id);
|
||||||
@ -350,7 +358,7 @@ beestat.component.card.runtime_sensor_detail.prototype.get_data_ = function() {
|
|||||||
var moving = [];
|
var moving = [];
|
||||||
var moving_count;
|
var moving_count;
|
||||||
if (beestat.setting('runtime_sensor_detail_smoothing') === true) {
|
if (beestat.setting('runtime_sensor_detail_smoothing') === true) {
|
||||||
moving_count = 15;
|
moving_count = 5;
|
||||||
} else {
|
} else {
|
||||||
moving_count = 1;
|
moving_count = 1;
|
||||||
}
|
}
|
||||||
@ -360,6 +368,11 @@ beestat.component.card.runtime_sensor_detail.prototype.get_data_ = function() {
|
|||||||
moving.push(runtime_sensors[begin_m.valueOf() + offset]);
|
moving.push(runtime_sensors[begin_m.valueOf() + offset]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Garage sensor is not returned in runtime data until halfway
|
||||||
|
// through...so the series data never got added early on so it just gets
|
||||||
|
// slapped on the beginning. It also takes over a previous "j" value and
|
||||||
|
// pushes everything around. Instead of looping over runtime_sensor I need to loop over sensor and grab the values.
|
||||||
|
|
||||||
// Loop.
|
// Loop.
|
||||||
var current_m = begin_m;
|
var current_m = begin_m;
|
||||||
while (
|
while (
|
||||||
@ -373,9 +386,16 @@ beestat.component.card.runtime_sensor_detail.prototype.get_data_ = function() {
|
|||||||
data.metadata.series.dummy.active = true;
|
data.metadata.series.dummy.active = true;
|
||||||
|
|
||||||
if (runtime_sensors[current_m.valueOf()] !== undefined) {
|
if (runtime_sensors[current_m.valueOf()] !== undefined) {
|
||||||
runtime_sensors[current_m.valueOf()].forEach(function(runtime_sensor, j) {
|
sensors.forEach(function(sensor, j) {
|
||||||
|
var runtime_sensor = runtime_sensors[current_m.valueOf()][sensor.sensor_id];
|
||||||
|
if (runtime_sensor === undefined) {
|
||||||
|
data.series['temperature_' + sensor.sensor_id].push(null);
|
||||||
|
data.series['occupancy_' + sensor.sensor_id].push(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var temperature_moving = beestat.temperature(
|
var temperature_moving = beestat.temperature(
|
||||||
self.get_average_(moving, j)
|
self.get_average_(moving, sensor.sensor_id)
|
||||||
);
|
);
|
||||||
data.series['temperature_' + runtime_sensor.sensor_id].push(temperature_moving);
|
data.series['temperature_' + runtime_sensor.sensor_id].push(temperature_moving);
|
||||||
y_min_max(temperature_moving);
|
y_min_max(temperature_moving);
|
||||||
@ -384,7 +404,7 @@ beestat.component.card.runtime_sensor_detail.prototype.get_data_ = function() {
|
|||||||
if (runtime_sensor.occupancy === true) {
|
if (runtime_sensor.occupancy === true) {
|
||||||
let swimlane_properties =
|
let swimlane_properties =
|
||||||
beestat.component.chart.runtime_sensor_detail.get_swimlane_properties(
|
beestat.component.chart.runtime_sensor_detail.get_swimlane_properties(
|
||||||
runtime_sensors[current_m.valueOf()].length,
|
sensors.length,
|
||||||
j
|
j
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -394,7 +414,7 @@ beestat.component.card.runtime_sensor_detail.prototype.get_data_ = function() {
|
|||||||
if (sequential['occupancy_' + runtime_sensor.sensor_id] > 0) {
|
if (sequential['occupancy_' + runtime_sensor.sensor_id] > 0) {
|
||||||
let swimlane_properties =
|
let swimlane_properties =
|
||||||
beestat.component.chart.runtime_sensor_detail.get_swimlane_properties(
|
beestat.component.chart.runtime_sensor_detail.get_swimlane_properties(
|
||||||
runtime_sensors[current_m.valueOf()].length,
|
sensors.length,
|
||||||
j
|
j
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -406,7 +426,7 @@ beestat.component.card.runtime_sensor_detail.prototype.get_data_ = function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Object.values(beestat.cache.sensor).forEach(function(sensor) {
|
sensors.forEach(function(sensor) {
|
||||||
if (sensor.thermostat_id === beestat.setting('thermostat_id')) {
|
if (sensor.thermostat_id === beestat.setting('thermostat_id')) {
|
||||||
data.series['temperature_' + sensor.sensor_id].push(null);
|
data.series['temperature_' + sensor.sensor_id].push(null);
|
||||||
data.series['occupancy_' + sensor.sensor_id].push(null);
|
data.series['occupancy_' + sensor.sensor_id].push(null);
|
||||||
@ -439,9 +459,11 @@ beestat.component.card.runtime_sensor_detail.prototype.get_runtime_sensor_by_dat
|
|||||||
beestat.cache.runtime_sensor.forEach(function(runtime_sensor) {
|
beestat.cache.runtime_sensor.forEach(function(runtime_sensor) {
|
||||||
var timestamp = [moment(runtime_sensor.timestamp).valueOf()];
|
var timestamp = [moment(runtime_sensor.timestamp).valueOf()];
|
||||||
if (runtime_sensors[timestamp] === undefined) {
|
if (runtime_sensors[timestamp] === undefined) {
|
||||||
runtime_sensors[timestamp] = [];
|
// runtime_sensors[timestamp] = [];
|
||||||
|
runtime_sensors[timestamp] = {};
|
||||||
}
|
}
|
||||||
runtime_sensors[timestamp].push(runtime_sensor);
|
// runtime_sensors[timestamp].push(runtime_sensor);
|
||||||
|
runtime_sensors[timestamp][runtime_sensor.sensor_id] = runtime_sensor;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return runtime_sensors;
|
return runtime_sensors;
|
||||||
@ -453,19 +475,28 @@ beestat.component.card.runtime_sensor_detail.prototype.get_runtime_sensor_by_dat
|
|||||||
* moving average.
|
* moving average.
|
||||||
*
|
*
|
||||||
* @param {array} runtime_sensors
|
* @param {array} runtime_sensors
|
||||||
* @param {string} j The index in the sub-array
|
* @param {string} sensor_id The index in the sub-array
|
||||||
*
|
*
|
||||||
* @return {number} The average.
|
* @return {number} The average.
|
||||||
*/
|
*/
|
||||||
beestat.component.card.runtime_sensor_detail.prototype.get_average_ = function(runtime_sensors, j) {
|
beestat.component.card.runtime_sensor_detail.prototype.get_average_ = function(runtime_sensors, sensor_id) {
|
||||||
var average = 0;
|
var average = 0;
|
||||||
var count = 0;
|
var count = 0;
|
||||||
for (var i = 0; i < runtime_sensors.length; i++) {
|
for (var i = 0; i < runtime_sensors.length; i++) {
|
||||||
if (runtime_sensors[i] !== undefined) {
|
if (
|
||||||
average += runtime_sensors[i][j].temperature;
|
runtime_sensors[i] !== undefined &&
|
||||||
|
runtime_sensors[i][sensor_id] !== undefined &&
|
||||||
|
runtime_sensors[i][sensor_id].temperature !== null
|
||||||
|
) {
|
||||||
|
average += runtime_sensors[i][sensor_id].temperature;
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (count === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return average / count;
|
return average / count;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -526,3 +557,24 @@ beestat.component.card.runtime_sensor_detail.prototype.data_synced_ = function(r
|
|||||||
current_sync_end.isSameOrAfter(required_sync_end)
|
current_sync_end.isSameOrAfter(required_sync_end)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a sorted list of all sensors attached to the current thermostat.
|
||||||
|
*
|
||||||
|
* @return {array} The sensors.
|
||||||
|
*/
|
||||||
|
beestat.component.card.runtime_sensor_detail.prototype.get_sensors_ = function() {
|
||||||
|
// Get and sort all the sensors.
|
||||||
|
var sensors = [];
|
||||||
|
$.values(beestat.cache.sensor).forEach(function(sensor) {
|
||||||
|
if (sensor.thermostat_id === beestat.setting('thermostat_id')) {
|
||||||
|
sensors.push(sensor);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sensors.sort(function(a, b) {
|
||||||
|
return a.name.localeCompare(b.name, 'en', {'sensitivity': 'base'});
|
||||||
|
});
|
||||||
|
|
||||||
|
return sensors;
|
||||||
|
};
|
||||||
|
@ -71,7 +71,19 @@ beestat.component.chart.prototype.get_options_legend_ = function() {
|
|||||||
'itemHiddenStyle': {
|
'itemHiddenStyle': {
|
||||||
'color': '#7f8c8d'
|
'color': '#7f8c8d'
|
||||||
},
|
},
|
||||||
'labelFormatter': this.get_options_legend_labelFormatter_()
|
'labelFormatter': this.get_options_legend_labelFormatter_(),
|
||||||
|
|
||||||
|
|
||||||
|
// 'layout': 'vertical',
|
||||||
|
// 'align': 'right',
|
||||||
|
// 'verticalAlign': 'top'
|
||||||
|
|
||||||
|
// 'maxHeight': 1000, // To prevent the navigation thing
|
||||||
|
// 'floating': true,
|
||||||
|
// 'verticalAlign': 'top',
|
||||||
|
// 'y': 50,
|
||||||
|
// 'borderWidth': 1
|
||||||
|
//
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -175,10 +187,20 @@ beestat.component.chart.prototype.get_options_chart_ = function() {
|
|||||||
'display': 'none'
|
'display': 'none'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'height': this.get_options_chart_height_()
|
'height': this.get_options_chart_height_(),
|
||||||
|
'events': this.get_options_chart_events_()
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the events list for the chart.
|
||||||
|
*
|
||||||
|
* @return {number} The events list for the chart.
|
||||||
|
*/
|
||||||
|
beestat.component.chart.prototype.get_options_chart_events_ = function() {
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the height of the chart.
|
* Get the height of the chart.
|
||||||
*
|
*
|
||||||
@ -240,7 +262,8 @@ beestat.component.chart.prototype.get_options_exporting_ = function() {
|
|||||||
beestat.style.size.gutter,
|
beestat.style.size.gutter,
|
||||||
beestat.style.size.gutter,
|
beestat.style.size.gutter,
|
||||||
beestat.style.size.gutter
|
beestat.style.size.gutter
|
||||||
]
|
],
|
||||||
|
'events': this.get_options_exporting_chart_events_()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -290,6 +313,15 @@ beestat.component.chart.prototype.get_options_exporting_filename_ = function() {
|
|||||||
return filename.join(' ');
|
return filename.join(' ');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the events list for the chart on export.
|
||||||
|
*
|
||||||
|
* @return {string} The events list for the chart on export.
|
||||||
|
*/
|
||||||
|
beestat.component.chart.prototype.get_options_exporting_chart_events_ = function() {
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the credits options.
|
* Get the credits options.
|
||||||
*
|
*
|
||||||
|
@ -62,10 +62,26 @@ beestat.component.chart.runtime_sensor_detail.prototype.get_options_series_ = fu
|
|||||||
beestat.style.color.orange.base,
|
beestat.style.color.orange.base,
|
||||||
beestat.style.color.bluegreen.base,
|
beestat.style.color.bluegreen.base,
|
||||||
beestat.style.color.purple.base,
|
beestat.style.color.purple.base,
|
||||||
beestat.style.color.lightblue.base
|
beestat.style.color.lightblue.base,
|
||||||
|
beestat.style.color.blue.light,
|
||||||
|
beestat.style.color.red.light,
|
||||||
|
beestat.style.color.yellow.light,
|
||||||
|
beestat.style.color.green.light,
|
||||||
|
beestat.style.color.orange.light,
|
||||||
|
beestat.style.color.bluegreen.light,
|
||||||
|
beestat.style.color.purple.light,
|
||||||
|
beestat.style.color.lightblue.light,
|
||||||
|
beestat.style.color.blue.dark,
|
||||||
|
beestat.style.color.red.dark,
|
||||||
|
beestat.style.color.yellow.dark,
|
||||||
|
beestat.style.color.green.dark,
|
||||||
|
beestat.style.color.orange.dark,
|
||||||
|
beestat.style.color.bluegreen.dark,
|
||||||
|
beestat.style.color.purple.dark,
|
||||||
|
beestat.style.color.lightblue.dark
|
||||||
];
|
];
|
||||||
|
|
||||||
Object.values(beestat.cache.sensor).forEach(function(sensor, i) {
|
this.data_.metadata.sensors.forEach(function(sensor, i) {
|
||||||
if (sensor.thermostat_id === beestat.setting('thermostat_id')) {
|
if (sensor.thermostat_id === beestat.setting('thermostat_id')) {
|
||||||
series.push({
|
series.push({
|
||||||
'name': 'temperature_' + sensor.sensor_id,
|
'name': 'temperature_' + sensor.sensor_id,
|
||||||
@ -76,7 +92,7 @@ beestat.component.chart.runtime_sensor_detail.prototype.get_options_series_ = fu
|
|||||||
'lineWidth': 1
|
'lineWidth': 1
|
||||||
});
|
});
|
||||||
|
|
||||||
var sensor_count = (Object.keys(self.data_.series).length - 1) / 2;
|
// var sensor_count = (Object.keys(self.data_.series).length - 1) / 2;
|
||||||
|
|
||||||
series.push({
|
series.push({
|
||||||
'linkedTo': ':previous',
|
'linkedTo': ':previous',
|
||||||
@ -85,7 +101,7 @@ beestat.component.chart.runtime_sensor_detail.prototype.get_options_series_ = fu
|
|||||||
'color': colors[i],
|
'color': colors[i],
|
||||||
'yAxis': 1,
|
'yAxis': 1,
|
||||||
'type': 'line',
|
'type': 'line',
|
||||||
'lineWidth': beestat.component.chart.runtime_sensor_detail.get_swimlane_properties(sensor_count, 1).line_width,
|
'lineWidth': beestat.component.chart.runtime_sensor_detail.get_swimlane_properties(self.data_.metadata.sensors.length, 1).line_width,
|
||||||
'linecap': 'square',
|
'linecap': 'square',
|
||||||
'className': 'crisp_edges'
|
'className': 'crisp_edges'
|
||||||
});
|
});
|
||||||
@ -114,37 +130,13 @@ beestat.component.chart.runtime_sensor_detail.prototype.get_options_yAxis_ = fun
|
|||||||
* Highcharts doesn't seem to respect axis behavior well so just overriding
|
* Highcharts doesn't seem to respect axis behavior well so just overriding
|
||||||
* it completely here.
|
* it completely here.
|
||||||
*/
|
*/
|
||||||
|
var y_min = Math.floor((this.data_.metadata.chart.y_min) / 5) * 5;
|
||||||
|
var y_max = Math.ceil((this.data_.metadata.chart.y_max) / 5) * 5;
|
||||||
|
|
||||||
var sensor_count = (Object.keys(this.data_.series).length - 1) / 2;
|
y_max += ((beestat.setting('temperature_unit') === '°F') ? 10 : 4);
|
||||||
|
|
||||||
var y_min = Math.floor((this.data_.metadata.chart.y_min - 5) / 10) * 10;
|
|
||||||
var y_max = Math.ceil((this.data_.metadata.chart.y_max + 10) / 10) * 10;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is unfortunate. Axis heights can be done in either pixels or
|
|
||||||
* percentages. If you use percentages, it's percentage of the plot height
|
|
||||||
* which includes the y-axis labels and the legend. These heights are
|
|
||||||
* variable, so setting a 20% height on the swimlane axis means the axis
|
|
||||||
* height can actually change depending on external factors. When trying to
|
|
||||||
* accurately position lanes, this variation can mess up pixel-perfect
|
|
||||||
* spacing.
|
|
||||||
*
|
|
||||||
* If you use pixels you can get more exact, but since there's no way to
|
|
||||||
* determine the available height for the chart (plot area minus y-axis
|
|
||||||
* labels minus legend), you're left in the dark on how high to make your
|
|
||||||
* "rest of the space" axis. There's also no way to set the height of one
|
|
||||||
* axis and have the other axis take the remaining space.
|
|
||||||
*
|
|
||||||
* So, as a workaround, setting the swimlane axis to a fixed height and
|
|
||||||
* having it sit on top of a full height axis works well enough. Adding a
|
|
||||||
* bit of padding to the primary axis prevents those values from flowing on
|
|
||||||
* top. It's not perfect because you get the main axis all the way up the
|
|
||||||
* side but it's not terrible.
|
|
||||||
*/
|
|
||||||
y_max += ((sensor_count > 8) ? 20 : 10);
|
|
||||||
|
|
||||||
var tick_positions = [];
|
var tick_positions = [];
|
||||||
var tick_interval = (beestat.setting('temperature_unit') === '°F') ? 10 : 5;
|
var tick_interval = (beestat.setting('temperature_unit') === '°F') ? 5 : 2;
|
||||||
var current_tick_position =
|
var current_tick_position =
|
||||||
Math.floor(y_min / tick_interval) * tick_interval;
|
Math.floor(y_min / tick_interval) * tick_interval;
|
||||||
while (current_tick_position <= y_max) {
|
while (current_tick_position <= y_max) {
|
||||||
@ -152,6 +144,7 @@ beestat.component.chart.runtime_sensor_detail.prototype.get_options_yAxis_ = fun
|
|||||||
current_tick_position += tick_interval;
|
current_tick_position += tick_interval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
// Temperature
|
// Temperature
|
||||||
{
|
{
|
||||||
@ -170,13 +163,20 @@ beestat.component.chart.runtime_sensor_detail.prototype.get_options_yAxis_ = fun
|
|||||||
// Swimlanes
|
// Swimlanes
|
||||||
{
|
{
|
||||||
'height': 100,
|
'height': 100,
|
||||||
'top': 15,
|
// 'top': 0,
|
||||||
'min': 0,
|
'min': 0,
|
||||||
'max': 100,
|
'max': 100,
|
||||||
'reversed': true,
|
'reversed': true,
|
||||||
'gridLineWidth': 0,
|
'gridLineWidth': 0,
|
||||||
'title': {'text': null},
|
'title': {'text': null},
|
||||||
'labels': {'enabled': false}
|
'labels': {'enabled': false},
|
||||||
|
'plotBands': {
|
||||||
|
'zIndex': 2,
|
||||||
|
// 'color': 'red',
|
||||||
|
'color': beestat.style.color.bluegray.dark,
|
||||||
|
'from': 0,
|
||||||
|
'to': 51
|
||||||
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
@ -193,39 +193,55 @@ beestat.component.chart.runtime_sensor_detail.prototype.get_options_tooltip_form
|
|||||||
var sections = [];
|
var sections = [];
|
||||||
var group = [];
|
var group = [];
|
||||||
|
|
||||||
|
// Get all the point values and index them by series_code for reference.
|
||||||
var values = {};
|
var values = {};
|
||||||
this.points.forEach(function(point) {
|
this.points.forEach(function(point) {
|
||||||
values[point.series.name] = point.y;
|
values[point.series.name] = point.y;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.points.forEach(function(point) {
|
/**
|
||||||
|
* Get a couple of other properties and index them by series_code for
|
||||||
|
* reference. This dives up to the chart itself because the tooltip shows
|
||||||
|
* all series unless explicitly disabled and those aren't always in the
|
||||||
|
* points array.
|
||||||
|
*/
|
||||||
|
var colors = {};
|
||||||
|
var visible = {};
|
||||||
|
self.chart_.series.forEach(function(series) {
|
||||||
|
colors[series.name] = series.color;
|
||||||
|
visible[series.name] = series.visible;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (var series_code in self.data_.series) {
|
||||||
var label;
|
var label;
|
||||||
var value;
|
var value;
|
||||||
var color;
|
var color;
|
||||||
|
|
||||||
if (point.series.name.includes('temperature') === true) {
|
if (series_code.includes('temperature') && visible[series_code] === true) {
|
||||||
label = self.data_.metadata.series[point.series.name].name;
|
label = self.data_.metadata.series[series_code].name;
|
||||||
color = point.series.color;
|
color = colors[series_code];
|
||||||
value = beestat.temperature({
|
if (values[series_code] === undefined) {
|
||||||
'temperature': values[point.series.name],
|
value = '-';
|
||||||
'convert': false,
|
} else {
|
||||||
'units': true
|
value = beestat.temperature({
|
||||||
});
|
'temperature': values[series_code],
|
||||||
|
'convert': false,
|
||||||
|
'units': true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var occupancy_key = point.series.name.replace('temperature', 'occupancy');
|
var occupancy_key = series_code.replace('temperature', 'occupancy');
|
||||||
if (values[occupancy_key] !== undefined && values[occupancy_key] !== null) {
|
if (values[occupancy_key] !== undefined && values[occupancy_key] !== null) {
|
||||||
value += ' ●';
|
value += ' ●';
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
group.push({
|
group.push({
|
||||||
'label': label,
|
'label': label,
|
||||||
'value': value,
|
'value': value,
|
||||||
'color': color
|
'color': color
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (group.length === 0) {
|
if (group.length === 0) {
|
||||||
group.push({
|
group.push({
|
||||||
@ -255,14 +271,17 @@ beestat.component.chart.runtime_sensor_detail.prototype.get_options_tooltip_form
|
|||||||
* @return {Object} The swimlane line width and y position.
|
* @return {Object} The swimlane line width and y position.
|
||||||
*/
|
*/
|
||||||
beestat.component.chart.runtime_sensor_detail.get_swimlane_properties = function(count, i) {
|
beestat.component.chart.runtime_sensor_detail.get_swimlane_properties = function(count, i) {
|
||||||
|
// Available height for all swimlanes
|
||||||
var height = 50;
|
var height = 50;
|
||||||
|
|
||||||
|
// Some sensible max height if you have very few sensors.
|
||||||
var max_line_width = 16;
|
var max_line_width = 16;
|
||||||
|
|
||||||
// Spacing.
|
// Spacing. This is arbitrary...spacing decreases to 0 after you hit 15 sensors.
|
||||||
var spacing = 4;
|
var spacing = Math.floor(15 / count);
|
||||||
|
|
||||||
// Base line width is a percentage height of the container.
|
// Base line width is a percentage height of the container.
|
||||||
var line_width = Math.round(height / count);
|
var line_width = Math.floor(height / count);
|
||||||
|
|
||||||
// Cap to a max line width.
|
// Cap to a max line width.
|
||||||
line_width = Math.min(line_width, max_line_width);
|
line_width = Math.min(line_width, max_line_width);
|
||||||
@ -274,8 +293,69 @@ beestat.component.chart.runtime_sensor_detail.get_swimlane_properties = function
|
|||||||
// Make the lines slightly less tall to create space between them.
|
// Make the lines slightly less tall to create space between them.
|
||||||
line_width -= spacing;
|
line_width -= spacing;
|
||||||
|
|
||||||
|
// Center within the swimlane area.
|
||||||
|
var occupied_space = (line_width * count) + (spacing * count);
|
||||||
|
var empty_space = height - occupied_space;
|
||||||
|
// y += (empty_space / 2);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'line_width': line_width,
|
'line_width': line_width,
|
||||||
'y': y
|
'y': y
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is unfortunate. Axis heights can be done in either pixels or
|
||||||
|
* percentages. If you use percentages, it's percentage of the plot height
|
||||||
|
* which includes the y-axis labels and the legend. These heights are
|
||||||
|
* variable, so setting a 20% height on the swimlane axis means the axis
|
||||||
|
* height can actually change depending on external factors. When trying to
|
||||||
|
* accurately position lanes, this variation can mess up pixel-perfect
|
||||||
|
* spacing.
|
||||||
|
*
|
||||||
|
* If you use pixels you can get more exact, but since there's no way to
|
||||||
|
* determine the available height for the chart (plot area minus y-axis labels
|
||||||
|
* minus legend), you're left in the dark on how high to make your "rest of
|
||||||
|
* the space" axis. There's also no way to set the height of one axis and have
|
||||||
|
* the other axis take the remaining space.
|
||||||
|
*
|
||||||
|
* So, as a workaround...I simply overlay the swimlanes on the top of a
|
||||||
|
* full-height temperature chart. Then I draw a rectangle on top of y-axis
|
||||||
|
* labels I want to hide so it appears to be on it's own.
|
||||||
|
*
|
||||||
|
* Helpful: https://www.highcharts.com/demo/renderer
|
||||||
|
*
|
||||||
|
* @return {object} The events list for the chart.
|
||||||
|
*/
|
||||||
|
beestat.component.chart.runtime_sensor_detail.prototype.get_options_chart_events_ = function() {
|
||||||
|
return {
|
||||||
|
'load': function() {
|
||||||
|
this.renderer.rect(0, 0, 30, 80)
|
||||||
|
.attr({
|
||||||
|
'fill': beestat.style.color.bluegray.base,
|
||||||
|
'zIndex': 10
|
||||||
|
})
|
||||||
|
.add();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See comment on get_options_chart_events_. This is done separately to
|
||||||
|
* override the normal load event rectangle draw because on export I also add
|
||||||
|
* padding and a title which screws up the positioning a bit.
|
||||||
|
*
|
||||||
|
* @return {object} The events list for the chart on export.
|
||||||
|
*/
|
||||||
|
beestat.component.chart.runtime_sensor_detail.prototype.get_options_exporting_chart_events_ = function() {
|
||||||
|
return {
|
||||||
|
'load': function() {
|
||||||
|
this.renderer.rect(beestat.style.size.gutter, 60, 30, 60)
|
||||||
|
.attr({
|
||||||
|
'fill': beestat.style.color.bluegray.base,
|
||||||
|
'zIndex': 10
|
||||||
|
})
|
||||||
|
.add();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user