/** * Runtime thermostat detail chart. * * @param {object} data The chart data. */ beestat.component.chart.runtime_thermostat_detail = function(data) { this.data_ = data; beestat.component.chart.apply(this, arguments); }; beestat.extend(beestat.component.chart.runtime_thermostat_detail, beestat.component.chart); /** * Override for get_options_xAxis_labels_formatter_. * * @return {Function} xAxis labels formatter. */ beestat.component.chart.runtime_thermostat_detail.prototype.get_options_xAxis_labels_formatter_ = function() { var current_day; var current_hour; return function() { var hour = this.value.format('ha'); var day = this.value.format('ddd'); var label_parts = []; if (day !== current_day) { label_parts.push(day); } if (hour !== current_hour) { label_parts.push(hour); } current_hour = hour; current_day = day; return label_parts.join(' '); }; }; /** * Override for get_options_series_. * * @return {Array} All of the series to display on the chart. */ beestat.component.chart.runtime_thermostat_detail.prototype.get_options_series_ = function() { var self = this; var series = []; // Indoor/Outdoor Temperature [ 'indoor_temperature', 'outdoor_temperature' ].forEach(function(series_code) { if (self.data_.metadata.series[series_code].active === true) { series.push({ 'name': series_code, 'data': self.data_.series[series_code], 'color': beestat.series[series_code].color, 'yAxis': 0, 'type': 'spline', 'dashStyle': (series_code === 'indoor_temperature') ? 'Solid' : 'ShortDash', 'lineWidth': (series_code === 'indoor_temperature') ? 2 : 1 }); } }); // Setpoint Heat/Cool [ 'setpoint_heat', 'setpoint_cool' ].forEach(function(series_code) { if (self.data_.metadata.series[series_code].active === true) { series.push({ 'name': series_code, 'data': self.data_.series[series_code], 'color': beestat.series[series_code].color, 'yAxis': 0, 'type': 'line', 'lineWidth': 1, 'step': 'right', 'className': 'crisp_edges' }); } }); // Indoor/Outdoor Humidity [ 'indoor_humidity', 'outdoor_humidity' ].forEach(function(series_code) { if (self.data_.metadata.series[series_code].active === true) { series.push({ 'name': series_code, 'data': self.data_.series[series_code], 'color': beestat.series[series_code].color, 'yAxis': 1, 'type': 'spline', 'dashStyle': (series_code === 'indoor_humidity') ? 'Solid' : 'ShortDash', 'lineWidth': (series_code === 'indoor_humidity') ? 2 : 1, 'visible': false }); } }); // Swimlanes [ 'calendar_event_smartrecovery', 'calendar_event_home', 'calendar_event_away', 'calendar_event_sleep', 'calendar_event_smarthome', 'calendar_event_smartaway', 'calendar_event_hold', 'calendar_event_vacation', 'calendar_event_quicksave', 'calendar_event_other', 'calendar_event_custom', 'compressor_heat_1', 'compressor_heat_2', 'auxiliary_heat_1', 'auxiliary_heat_2', 'compressor_cool_1', 'compressor_cool_2', 'fan', 'humidifier', 'dehumidifier', 'ventilator', 'economizer' ].forEach(function(series_code) { if (self.data_.metadata.series[series_code].active === true) { var line_width; if ( series_code.includes('heat') === true || series_code.includes('cool') === true ) { line_width = 12; } else { line_width = 6; } series.push({ 'name': series_code, 'data': self.data_.series[series_code], 'color': beestat.series[series_code].color, 'yAxis': 2, 'type': 'line', 'lineWidth': line_width, 'linecap': 'square', 'className': 'crisp_edges', 'showInLegend': false }); } }); series.push({ 'name': '', 'data': self.data_.series.dummy, 'yAxis': 2, 'type': 'line', 'lineWidth': 0, 'showInLegend': false }); return series; }; /** * Override for get_options_yAxis_. * * @return {Array} The y-axis options. */ beestat.component.chart.runtime_thermostat_detail.prototype.get_options_yAxis_ = function() { /** * Highcharts doesn't seem to respect axis behavior well so just overriding * it completely here. */ var y_min = Math.floor((this.data_.metadata.chart.y_min) / 10) * 10; var y_max = Math.ceil((this.data_.metadata.chart.y_max) / 10) * 10; var tick_positions = []; var tick_interval = (beestat.setting('temperature_unit') === '°F') ? 10 : 5; var current_tick_position = Math.floor(y_min / tick_interval) * tick_interval; while (current_tick_position <= y_max) { tick_positions.push(current_tick_position); current_tick_position += tick_interval; } return [ // Temperature { 'height': '80%', 'top': '20%', 'gridLineColor': beestat.style.color.bluegray.light, 'gridLineDashStyle': 'longdash', 'title': {'text': null}, 'labels': { 'style': {'color': beestat.style.color.gray.base}, 'formatter': function() { return this.value + beestat.setting('temperature_unit'); } }, 'tickPositions': tick_positions }, // Humidity { 'height': '80%', 'top': '20%', 'alignTicks': false, 'gridLineColor': null, 'opposite': true, 'title': {'text': null}, 'labels': { 'style': {'color': beestat.style.color.gray.base}, 'formatter': function() { return this.value + '%'; } }, // https://github.com/highcharts/highcharts/issues/3403 'min': 0, 'minRange': 100, 'ceiling': 100 }, // Swimlanes { 'height': '20%', 'top': '0%', 'min': 0, 'max': 100, 'gridLineWidth': 0, 'title': {'text': null}, 'labels': {'enabled': false} } ]; }; // https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/stock/demo/candlestick-and-volume/ /** * Override for get_options_tooltip_formatter_. * * @return {Function} The tooltip formatter. */ beestat.component.chart.runtime_thermostat_detail.prototype.get_options_tooltip_formatter_ = function() { var self = this; return function() { var sections = []; var groups = { 'mode': [], 'data': [], 'equipment': [] }; var values = {}; this.points.forEach(function(point) { values[point.series.name] = point.y; }); // HVAC Mode var system_mode; var system_mode_color; switch (self.data_.metadata.series.system_mode[this.x.valueOf()]) { case 'auto': system_mode = 'Auto'; system_mode_color = beestat.style.color.gray.base; break; case 'heat': system_mode = 'Heat'; system_mode_color = beestat.series.compressor_heat_1.color; break; case 'cool': system_mode = 'Cool'; system_mode_color = beestat.series.compressor_cool_1.color; break; case 'off': system_mode = 'Off'; system_mode_color = beestat.style.color.gray.base; break; case 'auxiliary_heat': system_mode = 'Aux'; system_mode_color = beestat.series.auxiliary_heat_1.color; break; } if (system_mode !== undefined) { groups.mode.push({ 'label': 'System Mode', 'value': system_mode, 'color': system_mode_color }); } this.points.forEach(function(point) { var label; var value; var color; var group; if ( point.series.name.includes('temperature') === true || point.series.name.includes('setpoint') === true ) { group = 'data'; label = beestat.series[point.series.name].name; color = point.series.color; value = beestat.temperature({ 'temperature': values[point.series.name], 'convert': false, 'units': true }); } else if (point.series.name.includes('humidity') === true) { group = 'data'; label = beestat.series[point.series.name].name; color = point.series.color; value = Math.round(values[point.series.name]) + '%'; } else if ( point.series.name === 'fan' || point.series.name === 'compressor_heat_1' || point.series.name === 'auxiliary_heat_1' || point.series.name === 'compressor_cool_1' || point.series.name === 'dehumidifier' || point.series.name === 'economizer' || point.series.name === 'humidifier' || point.series.name === 'ventilator' ) { group = 'equipment'; label = beestat.series[point.series.name].name; color = point.series.color; value = beestat.time( self.data_.metadata.series[point.series.name].durations[point.x.valueOf()] ); } else if ( point.series.name.includes('calendar_event') ) { group = 'mode'; label = 'Comfort Profile'; color = point.series.color; value = self.data_.metadata.series.calendar_event_name[point.x.valueOf()]; } else { return; } groups[group].push({ 'label': label, 'value': value, 'color': color }); // Show stage 2 duration on stage 1, if applicable. if ( point.series.name === 'compressor_heat_1' && self.data_.metadata.series.compressor_heat_2.durations[point.x.valueOf()].seconds > 0 ) { groups.equipment.push({ 'label': beestat.series.compressor_heat_2.name, 'value': beestat.time( self.data_.metadata.series.compressor_heat_2.durations[point.x.valueOf()] ), 'color': beestat.series.compressor_heat_2.color }); } if ( point.series.name === 'auxiliary_heat_1' && self.data_.metadata.series.auxiliary_heat_2.durations[point.x.valueOf()].seconds > 0 ) { groups.equipment.push({ 'label': beestat.series.auxiliary_heat_2.name, 'value': beestat.time( self.data_.metadata.series.auxiliary_heat_2.durations[point.x.valueOf()] ), 'color': beestat.series.auxiliary_heat_2.color }); } if ( point.series.name === 'compressor_cool_1' && self.data_.metadata.series.compressor_cool_2.durations[point.x.valueOf()].seconds > 0 ) { groups.equipment.push({ 'label': beestat.series.compressor_cool_2.name, 'value': beestat.time( self.data_.metadata.series.compressor_cool_2.durations[point.x.valueOf()] ), 'color': beestat.series.compressor_cool_2.color }); } }); if ( groups.mode.length === 0 && groups.equipment.length === 0 && groups.data.length === 0 ) { groups.mode.push({ 'label': 'No data', 'value': '', 'color': beestat.style.color.gray.base }); } sections.push(groups.mode); sections.push(groups.equipment); sections.push(groups.data); var title = this.x.format('ddd, MMM D @ h:mma'); return self.tooltip_formatter_helper_( title, sections ); }; };