diff --git a/js/beestat/get_sync_progress.js b/js/beestat/get_sync_progress.js deleted file mode 100755 index 86ff9fc..0000000 --- a/js/beestat/get_sync_progress.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Get the sync progress for a thermostat. - * - * @param {number} thermostat_id - * - * @return {number} A number between 0 and 100 inclusive. - */ -beestat.get_sync_progress = function(thermostat_id) { - var thermostat = beestat.cache.thermostat[thermostat_id]; - - var current_sync_begin = moment.utc(thermostat.sync_begin); - var current_sync_end = moment.utc(thermostat.sync_end); - - var required_sync_begin = moment.max( - moment(thermostat.first_connected), - moment().subtract(1, 'year') - ); - var required_sync_end = moment().subtract(1, 'hour'); - - var denominator = required_sync_end.diff(required_sync_begin, 'day'); - var numerator = current_sync_end.diff(current_sync_begin, 'day'); - - return Math.min(100, Math.round(numerator / denominator * 100)) || 0; -}; diff --git a/js/beestat/runtime_sensor.js b/js/beestat/runtime_sensor.js index 971afb6..ccde640 100644 --- a/js/beestat/runtime_sensor.js +++ b/js/beestat/runtime_sensor.js @@ -78,13 +78,6 @@ beestat.runtime_sensor.get_data = function(thermostat_id, range) { ); } - // TODO: This needs to be max of begin and when I actually have sensor data - var thermostat = beestat.cache.thermostat[thermostat_id]; - begin_m = moment.max( - begin_m, - moment(thermostat.first_connected) - ); - begin_m .minute(Math.ceil(begin_m.minute() / 5) * 5) .second(0) diff --git a/js/beestat/thermostat.js b/js/beestat/thermostat.js new file mode 100644 index 0000000..ccd2e6b --- /dev/null +++ b/js/beestat/thermostat.js @@ -0,0 +1,59 @@ +beestat.thermostat = {}; + +/** + * Get the sync progress for a thermostat. + * + * @param {number} thermostat_id + * + * @return {number|null} A number between 0 and 100 inclusive. Can return null + * for unknown. + */ +beestat.thermostat.get_sync_progress = function(thermostat_id) { + var thermostat = beestat.cache.thermostat[thermostat_id]; + + var current_sync_begin = moment(thermostat.sync_begin); + var current_sync_end = moment(thermostat.sync_end); + + var required_sync_begin = moment.max( + moment(thermostat.first_connected), + moment().subtract(1, 'year') + ); + var required_sync_end = moment().subtract(1, 'hour'); + + // If the thermostat was connected within the last hour. + if (required_sync_end.isSameOrBefore(required_sync_begin) === true) { + return null; + } + + var denominator = required_sync_end.diff(required_sync_begin, 'day'); + var numerator = current_sync_end.diff(current_sync_begin, 'day'); + + return Math.min(100, Math.round(numerator / denominator * 100)) || 0; +}; + +/** + * Determine whether or not the data to render the desired date range has been + * synced. + * + * @param {number} thermostat_id + * @param {moment} required_sync_begin + * @param {moment} required_sync_end + * + * @return {boolean} Whether or not the data is synced. + */ +beestat.thermostat.data_synced = function(thermostat_id, required_sync_begin, required_sync_end) { + // Demo can just grab whatever data is there. + if (window.is_demo === true) { + return true; + } + + var thermostat = beestat.cache.thermostat[thermostat_id]; + + var current_sync_begin = moment(thermostat.sync_begin); + var current_sync_end = moment(thermostat.sync_end); + + return ( + current_sync_begin.isSameOrBefore(required_sync_begin) === true && + current_sync_end.isSameOrAfter(required_sync_end) === true + ); +}; diff --git a/js/component/card/comparison_settings.js b/js/component/card/comparison_settings.js index 5ac122e..3a48190 100644 --- a/js/component/card/comparison_settings.js +++ b/js/component/card/comparison_settings.js @@ -278,7 +278,7 @@ beestat.component.card.comparison_settings.prototype.decorate_top_right_ = funct * @return {boolean} Whether or not all of the data has been loaded. */ beestat.component.card.comparison_settings.prototype.data_available_ = function() { - var sync_progress = beestat.get_sync_progress(beestat.setting('thermostat_id')); + var sync_progress = beestat.thermostat.get_sync_progress(beestat.setting('thermostat_id')); if (sync_progress >= 95) { this.show_loading_('Calculating Scores'); diff --git a/js/component/card/runtime_sensor_detail.js b/js/component/card/runtime_sensor_detail.js index faf8719..1a0a0dd 100644 --- a/js/component/card/runtime_sensor_detail.js +++ b/js/component/card/runtime_sensor_detail.js @@ -19,6 +19,7 @@ beestat.component.card.runtime_sensor_detail = function(thermostat_id) { * for when rerendering. */ var change_function = beestat.debounce(function() { + self.get_data_(true); self.rerender(); }, 10); @@ -44,30 +45,29 @@ beestat.extend(beestat.component.card.runtime_sensor_detail, beestat.component.c beestat.component.card.runtime_sensor_detail.prototype.decorate_contents_ = function(parent) { var self = this; - var range = { - 'type': beestat.setting('runtime_sensor_detail_range_type'), - 'dynamic': beestat.setting('runtime_sensor_detail_range_dynamic'), - 'static_begin': beestat.setting('runtime_sensor_detail_range_static_begin'), - 'static_end': beestat.setting('runtime_sensor_detail_range_static_end') - }; - - var sensor_data = beestat.runtime_sensor.get_data(this.thermostat_id_, range); - var thermostat_data = beestat.runtime_thermostat.get_data(this.thermostat_id_, range); - - var data = sensor_data; - - Object.assign(data.series, thermostat_data.series); - Object.assign(data.metadata.series, thermostat_data.metadata.series); - this.charts_ = { - 'equipment': new beestat.component.chart.runtime_thermostat_detail_equipment(data), - 'occupancy': new beestat.component.chart.runtime_sensor_detail_occupancy(data), - 'temperature': new beestat.component.chart.runtime_sensor_detail_temperature(data) + 'equipment': new beestat.component.chart.runtime_thermostat_detail_equipment( + this.get_data_() + ), + 'occupancy': new beestat.component.chart.runtime_sensor_detail_occupancy( + this.get_data_() + ), + 'temperature': new beestat.component.chart.runtime_sensor_detail_temperature( + this.get_data_() + ) }; - this.charts_.equipment.render(parent); - this.charts_.occupancy.render(parent); - this.charts_.temperature.render(parent); + var container = $.createElement('div').style({ + 'position': 'relative' + }); + parent.appendChild(container); + + var chart_container = $.createElement('div'); + container.appendChild(chart_container); + + this.charts_.equipment.render(chart_container); + this.charts_.occupancy.render(chart_container); + this.charts_.temperature.render(chart_container); // Sync extremes and crosshair. Object.values(this.charts_).forEach(function(source_chart) { @@ -130,12 +130,12 @@ beestat.component.card.runtime_sensor_detail.prototype.decorate_contents_ = func * cache is empty, then query the data. If the needed data does not exist in * the database, check every 2 seconds until it does. */ - if (this.data_synced_(required_begin, required_end) === true) { + if (beestat.thermostat.data_synced(this.thermostat_id_, required_begin, required_end) === true) { if ( beestat.cache.runtime_sensor === undefined || beestat.cache.data.runtime_thermostat_last !== 'runtime_sensor_detail' ) { - this.show_loading_('Loading Sensor Detail'); + this.show_loading_('Loading'); var value; var operator; @@ -201,9 +201,25 @@ beestat.component.card.runtime_sensor_detail.prototype.decorate_contents_ = func }); api_call.send(); + } else if (this.has_data_() === false) { + chart_container.style('filter', 'blur(3px)'); + var no_data = $.createElement('div'); + no_data.style({ + 'position': 'absolute', + 'top': 0, + 'left': 0, + 'width': '100%', + 'height': '100%', + 'display': 'flex', + 'flex-direction': 'column', + 'justify-content': 'center', + 'text-align': 'center' + }); + no_data.innerText('No data to display'); + container.appendChild(no_data); } } else { - this.show_loading_('Syncing Sensor Detail'); + this.show_loading_('Syncing'); setTimeout(function() { new beestat.api() .add_call( @@ -312,6 +328,58 @@ beestat.component.card.runtime_sensor_detail.prototype.decorate_top_right_ = fun })); }; +/** + * Whether or not there is data to display on the chart. + * + * @return {boolean} Whether or not there is data to display on the chart. + */ +beestat.component.card.runtime_sensor_detail.prototype.has_data_ = function() { + var data = this.get_data_(); + for (var series_code in data.metadata.series) { + if ( + series_code !== 'dummy' && + data.metadata.series[series_code].active === true + ) { + return true; + } + } + + return false; +}; + +/** + * Get data. This doesn't directly or indirectly make any API calls, but it + * caches the data so it doesn't have to loop over everything more than once. + * + * @param {boolean} force Force get the data? + * + * @return {object} The data. + */ +beestat.component.card.runtime_sensor_detail.prototype.get_data_ = function(force) { + if (this.data_ === undefined || force === true) { + + var range = { + 'type': beestat.setting('runtime_sensor_detail_range_type'), + 'dynamic': beestat.setting('runtime_sensor_detail_range_dynamic'), + 'static_begin': beestat.setting('runtime_sensor_detail_range_static_begin'), + 'static_end': beestat.setting('runtime_sensor_detail_range_static_end') + }; + + var sensor_data = beestat.runtime_sensor.get_data(this.thermostat_id_, range); + var thermostat_data = beestat.runtime_thermostat.get_data(this.thermostat_id_, range); + + this.data_ = sensor_data; + + Object.assign(this.data_.series, thermostat_data.series); + Object.assign(this.data_.metadata.series, thermostat_data.metadata.series); + + this.data_.metadata.chart.title = this.get_title_(); + this.data_.metadata.chart.subtitle = this.get_subtitle_(); + } + + return this.data_; +}; + /** * Get the title of the card. * @@ -343,29 +411,3 @@ beestat.component.card.runtime_sensor_detail.prototype.get_subtitle_ = function( return begin + ' to ' + end; }; - -/** - * Determine whether or not the data to render the desired date range has been - * synced. - * - * @param {moment} required_sync_begin - * @param {moment} required_sync_end - * - * @return {boolean} Whether or not the data is synced. - */ -beestat.component.card.runtime_sensor_detail.prototype.data_synced_ = function(required_sync_begin, required_sync_end) { - // Demo can just grab whatever data is there. - if (window.is_demo === true) { - return true; - } - - var thermostat = beestat.cache.thermostat[beestat.setting('thermostat_id')]; - - var current_sync_begin = moment.utc(thermostat.sync_begin); - var current_sync_end = moment.utc(thermostat.sync_end); - - return ( - current_sync_begin.isSameOrBefore(required_sync_begin) && - current_sync_end.isSameOrAfter(required_sync_end) - ); -}; diff --git a/js/component/card/runtime_thermostat_detail.js b/js/component/card/runtime_thermostat_detail.js index 31d5502..2ea5399 100644 --- a/js/component/card/runtime_thermostat_detail.js +++ b/js/component/card/runtime_thermostat_detail.js @@ -27,7 +27,8 @@ beestat.component.card.runtime_thermostat_detail = function(thermostat_id) { [ 'setting.runtime_thermostat_detail_range_type', 'setting.runtime_thermostat_detail_range_dynamic', - 'cache.runtime_thermostat' + 'cache.runtime_thermostat', + 'cache.thermostat' ], change_function ); @@ -36,32 +37,6 @@ beestat.component.card.runtime_thermostat_detail = function(thermostat_id) { }; beestat.extend(beestat.component.card.runtime_thermostat_detail, beestat.component.card); -/** - * Get data. This doesn't directly or indirectly make any API calls, but it - * caches the data so it doesn't have to loop over everything more than once. - * - * @param {boolean} force Force get the data? - * - * @return {object} The data. - */ -beestat.component.card.runtime_thermostat_detail.prototype.get_data_ = function(force) { - if (this.data_ === undefined || force === true) { - var range = { - 'type': beestat.setting('runtime_thermostat_detail_range_type'), - 'dynamic': beestat.setting('runtime_thermostat_detail_range_dynamic'), - 'static_begin': beestat.setting('runtime_thermostat_detail_range_static_begin'), - 'static_end': beestat.setting('runtime_thermostat_detail_range_static_end') - }; - - this.data_ = beestat.runtime_thermostat.get_data(this.thermostat_id_, range); - - this.data_.metadata.chart.title = this.get_title_(); - this.data_.metadata.chart.subtitle = this.get_subtitle_(); - } - - return this.data_; -}; - /** * Decorate * @@ -174,12 +149,12 @@ beestat.component.card.runtime_thermostat_detail.prototype.decorate_contents_ = * cache is empty, then query the data. If the needed data does not exist in * the database, check every 2 seconds until it does. */ - if (this.data_synced_(required_begin, required_end) === true) { + if (beestat.thermostat.data_synced(this.thermostat_id_, required_begin, required_end) === true) { if ( beestat.cache.runtime_thermostat === undefined || beestat.cache.data.runtime_thermostat_last !== 'runtime_thermostat_detail' ) { - this.show_loading_('Loading Thermostat Detail'); + this.show_loading_('Loading'); var value; var operator; @@ -232,7 +207,7 @@ beestat.component.card.runtime_thermostat_detail.prototype.decorate_contents_ = container.appendChild(no_data); } } else { - this.show_loading_('Syncing Thermostat Detail'); + this.show_loading_('Syncing'); setTimeout(function() { new beestat.api() .add_call( @@ -247,7 +222,6 @@ beestat.component.card.runtime_thermostat_detail.prototype.decorate_contents_ = ) .set_callback(function(response) { beestat.cache.set('thermostat', response); - self.rerender(); }) .send(); }, 2000); @@ -343,6 +317,51 @@ beestat.component.card.runtime_thermostat_detail.prototype.decorate_top_right_ = })); }; +/** + * Whether or not there is data to display on the chart. + * + * @return {boolean} Whether or not there is data to display on the chart. + */ +beestat.component.card.runtime_thermostat_detail.prototype.has_data_ = function() { + var data = this.get_data_(); + for (var series_code in data.metadata.series) { + if ( + series_code !== 'dummy' && + data.metadata.series[series_code].active === true + ) { + return true; + } + } + + return false; +}; + +/** + * Get data. This doesn't directly or indirectly make any API calls, but it + * caches the data so it doesn't have to loop over everything more than once. + * + * @param {boolean} force Force get the data? + * + * @return {object} The data. + */ +beestat.component.card.runtime_thermostat_detail.prototype.get_data_ = function(force) { + if (this.data_ === undefined || force === true) { + var range = { + 'type': beestat.setting('runtime_thermostat_detail_range_type'), + 'dynamic': beestat.setting('runtime_thermostat_detail_range_dynamic'), + 'static_begin': beestat.setting('runtime_thermostat_detail_range_static_begin'), + 'static_end': beestat.setting('runtime_thermostat_detail_range_static_end') + }; + + this.data_ = beestat.runtime_thermostat.get_data(this.thermostat_id_, range); + + this.data_.metadata.chart.title = this.get_title_(); + this.data_.metadata.chart.subtitle = this.get_subtitle_(); + } + + return this.data_; +}; + /** * Get the title of the card. * @@ -374,48 +393,3 @@ beestat.component.card.runtime_thermostat_detail.prototype.get_subtitle_ = funct return begin + ' to ' + end; }; - -/** - * Determine whether or not the data to render the desired date range has been - * synced. - * - * @param {moment} required_sync_begin - * @param {moment} required_sync_end - * - * @return {boolean} Whether or not the data is synced. - */ -beestat.component.card.runtime_thermostat_detail.prototype.data_synced_ = function(required_sync_begin, required_sync_end) { - // Demo can just grab whatever data is there. - if (window.is_demo === true) { - return true; - } - - var thermostat = beestat.cache.thermostat[this.thermostat_id_]; - - var current_sync_begin = moment.utc(thermostat.sync_begin); - var current_sync_end = moment.utc(thermostat.sync_end); - - return ( - current_sync_begin.isSameOrBefore(required_sync_begin) && - current_sync_end.isSameOrAfter(required_sync_end) - ); -}; - -/** - * Whether or not there is data to display on the chart. - * - * @return {boolean} Whether or not there is data to display on the chart. - */ -beestat.component.card.runtime_thermostat_detail.prototype.has_data_ = function() { - var data = this.get_data_(); - for (var series_code in data.metadata.series) { - if ( - series_code !== 'dummy' && - data.metadata.series[series_code].active === true - ) { - return true; - } - } - - return false; -}; diff --git a/js/component/card/runtime_thermostat_summary.js b/js/component/card/runtime_thermostat_summary.js index fe8b388..3bfb011 100755 --- a/js/component/card/runtime_thermostat_summary.js +++ b/js/component/card/runtime_thermostat_summary.js @@ -14,7 +14,7 @@ beestat.component.card.runtime_thermostat_summary = function(thermostat_id) { * long the sync will take to complete. */ this.sync_begin_m_ = moment(); - this.sync_begin_progress_ = beestat.get_sync_progress(thermostat_id); + this.sync_begin_progress_ = beestat.thermostat.get_sync_progress(thermostat_id); /* * When a setting is changed clear all of the data. Then rerender which will @@ -49,13 +49,37 @@ beestat.extend(beestat.component.card.runtime_thermostat_summary, beestat.compon * @param {rocket.Elements} parent */ beestat.component.card.runtime_thermostat_summary.prototype.decorate_contents_ = function(parent) { + var container = $.createElement('div').style({ + 'position': 'relative' + }); + parent.appendChild(container); + + var chart_container = $.createElement('div'); + container.appendChild(chart_container); + var data = this.get_data_(); this.chart_ = new beestat.component.chart.runtime_thermostat_summary(data); - this.chart_.render(parent); + this.chart_.render(chart_container); - var sync_progress = beestat.get_sync_progress(this.thermostat_id_); + var sync_progress = beestat.thermostat.get_sync_progress(this.thermostat_id_); - if (sync_progress < 100) { + if (sync_progress === null) { + chart_container.style('filter', 'blur(3px)'); + var no_data = $.createElement('div'); + no_data.style({ + 'position': 'absolute', + 'top': 0, + 'left': 0, + 'width': '100%', + 'height': '100%', + 'display': 'flex', + 'flex-direction': 'column', + 'justify-content': 'center', + 'text-align': 'center' + }); + no_data.innerText('No data to display'); + container.appendChild(no_data); + } else if (sync_progress < 100) { var time_taken = moment.duration(moment().diff(this.sync_begin_m_)); var percent_taken = sync_progress - this.sync_begin_progress_; var percent_per_second = percent_taken / time_taken.asSeconds(); @@ -73,7 +97,7 @@ beestat.component.card.runtime_thermostat_summary.prototype.decorate_contents_ = } } - this.show_loading_('Syncing Thermostat Summary (' + sync_progress + '%)
' + string_remain + ' remaining'); + this.show_loading_('Syncing (' + sync_progress + '%)
' + string_remain + ' remaining'); setTimeout(function() { var api = new beestat.api(); api.add_call( diff --git a/js/js.php b/js/js.php index fcf3781..f5f50d4 100755 --- a/js/js.php +++ b/js/js.php @@ -29,7 +29,7 @@ if($setting->get('environment') === 'dev' || $setting->get('environment') === 'd echo '' . PHP_EOL; echo '' . PHP_EOL; echo '' . PHP_EOL; - echo '' . PHP_EOL; + echo '' . PHP_EOL; echo '' . PHP_EOL; echo '' . PHP_EOL; echo '' . PHP_EOL;