mirror of
				https://github.com/beestat/app.git
				synced 2025-10-30 17:52:25 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			618 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			618 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  * System card. Shows a big picture of your thermostat, it's sensors, and lets
 | |
|  * you switch between thermostats.
 | |
|  *
 | |
|  * @param {number} thermostat_id
 | |
|  */
 | |
| beestat.component.card.system = function(thermostat_id) {
 | |
|   var self = this;
 | |
| 
 | |
|   this.thermostat_id_ = thermostat_id;
 | |
| 
 | |
|   var change_function = beestat.debounce(function() {
 | |
|     self.rerender();
 | |
|   }, 10);
 | |
| 
 | |
|   beestat.dispatcher.addEventListener(
 | |
|     [
 | |
|       'cache.thermostat',
 | |
|       'cache.ecobee_thermostat'
 | |
|     ],
 | |
|     change_function
 | |
|   );
 | |
| 
 | |
|   beestat.component.card.apply(this, arguments);
 | |
| };
 | |
| beestat.extend(beestat.component.card.system, beestat.component.card);
 | |
| 
 | |
| beestat.component.card.system.prototype.decorate_contents_ = function(parent) {
 | |
|   this.decorate_circle_(parent);
 | |
|   this.decorate_weather_(parent);
 | |
|   this.decorate_equipment_(parent);
 | |
|   this.decorate_climate_(parent);
 | |
| 
 | |
|   this.decorate_time_to_temperature_(parent);
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Decorate the circle containing temperature and humidity.
 | |
|  *
 | |
|  * @param {rocket.Elements} parent
 | |
|  */
 | |
| beestat.component.card.system.prototype.decorate_circle_ = function(parent) {
 | |
|   var thermostat = beestat.cache.thermostat[this.thermostat_id_];
 | |
| 
 | |
|   var temperature = beestat.temperature(thermostat.temperature);
 | |
|   var temperature_whole = Math.floor(temperature);
 | |
|   var temperature_fractional = (temperature % 1).toFixed(1).substring(2);
 | |
| 
 | |
|   var circle = $.createElement('div')
 | |
|     .style({
 | |
|       'padding': (beestat.style.size.gutter * 3),
 | |
|       'border-radius': '50%',
 | |
|       'background': beestat.thermostat.get_color(this.thermostat_id_),
 | |
|       'height': '180px',
 | |
|       'width': '180px',
 | |
|       'margin': beestat.style.size.gutter + 'px auto ' + beestat.style.size.gutter + 'px auto',
 | |
|       'text-align': 'center',
 | |
|       'text-shadow': '1px 1px 1px rgba(0, 0, 0, 0.2)'
 | |
|     });
 | |
|   parent.appendChild(circle);
 | |
| 
 | |
|   var temperature_container = $.createElement('div');
 | |
|   circle.appendChild(temperature_container);
 | |
| 
 | |
|   var temperature_whole_container = $.createElement('span')
 | |
|     .style({
 | |
|       'font-size': '48px',
 | |
|       'font-weight': beestat.style.font_weight.light
 | |
|     })
 | |
|     .innerHTML(temperature_whole);
 | |
|   temperature_container.appendChild(temperature_whole_container);
 | |
| 
 | |
|   var temperature_fractional_container = $.createElement('span')
 | |
|     .style({
 | |
|       'font-size': '24px'
 | |
|     })
 | |
|     .innerHTML('.' + temperature_fractional);
 | |
|   temperature_container.appendChild(temperature_fractional_container);
 | |
| 
 | |
|   var humidity_container = $.createElement('div')
 | |
|     .style({
 | |
|       'display': 'inline-flex',
 | |
|       'align-items': 'center'
 | |
|     });
 | |
|   circle.appendChild(humidity_container);
 | |
| 
 | |
|   (new beestat.component.icon('water_percent', 'Humidity')
 | |
|     .set_size(24)
 | |
|   ).render(humidity_container);
 | |
| 
 | |
|   humidity_container.appendChild(
 | |
|     $.createElement('span').innerHTML(thermostat.humidity + '%')
 | |
|   );
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Decorate the weather
 | |
|  *
 | |
|  * @param {rocket.Elements} parent Parent
 | |
|  */
 | |
| beestat.component.card.system.prototype.decorate_weather_ = function(parent) {
 | |
|   var thermostat = beestat.cache.thermostat[this.thermostat_id_];
 | |
| 
 | |
|   var circle = $.createElement('div')
 | |
|     .style({
 | |
|       'padding': (beestat.style.size.gutter / 2),
 | |
|       'border-radius': '50%',
 | |
|       'background': beestat.style.color.bluegray.light,
 | |
|       'height': '66px',
 | |
|       'width': '66px',
 | |
|       'text-align': 'center',
 | |
|       'text-shadow': '1px 1px 1px rgba(0, 0, 0, 0.2)',
 | |
|       'position': 'absolute',
 | |
|       'top': '90px',
 | |
|       'left': '50%',
 | |
|       'margin-left': '40px',
 | |
|       'cursor': 'pointer',
 | |
|       'transition': 'background 200ms ease'
 | |
|     });
 | |
|   parent.appendChild(circle);
 | |
| 
 | |
|   circle
 | |
|     .addEventListener('mouseover', function() {
 | |
|       circle.style('background', beestat.style.color.gray.dark);
 | |
|     })
 | |
|     .addEventListener('mouseout', function() {
 | |
|       circle.style('background', beestat.style.color.bluegray.light);
 | |
|     })
 | |
|     .addEventListener('click', function() {
 | |
|       (new beestat.component.modal.weather()).render();
 | |
|     });
 | |
| 
 | |
|   var temperature_container = $.createElement('div');
 | |
|   circle.appendChild(temperature_container);
 | |
| 
 | |
|   var temperature_whole_container = $.createElement('span')
 | |
|     .style({
 | |
|       'font-size': '22px',
 | |
|       'font-weight': beestat.style.font_weight.light
 | |
|     })
 | |
|     .innerHTML(beestat.temperature({
 | |
|       'round': 0,
 | |
|       'units': false,
 | |
|       'temperature': thermostat.weather.temperature
 | |
|     }));
 | |
|   temperature_container.appendChild(temperature_whole_container);
 | |
| 
 | |
|   var humidity_container = $.createElement('div')
 | |
|     .style({
 | |
|       'display': 'inline-flex',
 | |
|       'align-items': 'center'
 | |
|     });
 | |
|   circle.appendChild(humidity_container);
 | |
| 
 | |
|   (new beestat.component.icon('water_percent', 'Humidity')
 | |
|     .set_size(16)
 | |
|   ).render(humidity_container);
 | |
| 
 | |
|   humidity_container.appendChild(
 | |
|     $.createElement('span')
 | |
|       .innerHTML(thermostat.weather.humidity_relative + '%')
 | |
|       .style({
 | |
|         'font-size': '10px'
 | |
|       })
 | |
|   );
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Decorate the running equipment list on the bottom left.
 | |
|  *
 | |
|  * @param {rocket.Elements} parent
 | |
|  */
 | |
| beestat.component.card.system.prototype.decorate_equipment_ = function(parent) {
 | |
|   var thermostat = beestat.cache.thermostat[this.thermostat_id_];
 | |
| 
 | |
|   var render_icon = function(icon_parent, icon, color, subscript, tooltip) {
 | |
|     (new beestat.component.icon(icon, tooltip)
 | |
|       .set_size(24)
 | |
|       .set_color(color)
 | |
|     ).render(icon_parent);
 | |
| 
 | |
|     if (subscript !== undefined) {
 | |
|       var sub = $.createElement('sub')
 | |
|         .style({
 | |
|           'font-size': '10px',
 | |
|           'font-weight': beestat.style.font_weight.bold,
 | |
|           'color': color
 | |
|         })
 | |
|         .innerHTML(subscript);
 | |
|       icon_parent.appendChild(sub);
 | |
|     } else {
 | |
|       // A little spacer to help things look less uneven.
 | |
|       icon_parent.appendChild($.createElement('span')
 | |
|         .style('margin-right', beestat.style.size.gutter / 4));
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   if (thermostat.running_equipment.length === 0) {
 | |
|     render_icon(parent, 'cancel', beestat.style.color.gray.base, 'none', 'No equipment running');
 | |
|   } else {
 | |
|     thermostat.running_equipment.forEach(function(equipment) {
 | |
|       let subscript;
 | |
|       let tooltip;
 | |
|       switch (equipment) {
 | |
|       case 'fan':
 | |
|         render_icon(parent, 'fan', beestat.style.color.gray.light, undefined, 'Fan');
 | |
|         break;
 | |
|       case 'cool_1':
 | |
|         tooltip = 'Cool';
 | |
|         if (thermostat.system_type.detected.cool.stages > 1) {
 | |
|           subscript = '1';
 | |
|           tooltip += ' 1';
 | |
|         } else {
 | |
|           subscript = undefined;
 | |
|         }
 | |
|         render_icon(parent, 'snowflake', beestat.style.color.blue.light, subscript, tooltip);
 | |
|         break;
 | |
|       case 'cool_2':
 | |
|         render_icon(parent, 'snowflake', beestat.style.color.blue.light, '2', 'Cool 2');
 | |
|         break;
 | |
|       case 'heat_1':
 | |
|         tooltip = 'Heat';
 | |
|         if (thermostat.system_type.detected.heat.stages > 1) {
 | |
|           subscript = '1';
 | |
|           tooltip += ' 1';
 | |
|         } else {
 | |
|           subscript = undefined;
 | |
|         }
 | |
|         render_icon(parent, 'fire', beestat.style.color.orange.base, subscript, tooltip);
 | |
|         break;
 | |
|       case 'heat_2':
 | |
|         render_icon(parent, 'fire', beestat.style.color.orange.base, '2', 'Heat 2');
 | |
|         break;
 | |
|       case 'heat_3':
 | |
|         render_icon(parent, 'fire', beestat.style.color.orange.base, '3', 'Heat 3');
 | |
|         break;
 | |
|       case 'auxiliary_heat_1':
 | |
|         tooltip = 'Aux heat';
 | |
|         if (thermostat.system_type.detected.auxiliary_heat.stages > 1) {
 | |
|           subscript = '1';
 | |
|           tooltip += ' 1';
 | |
|         } else {
 | |
|           subscript = undefined;
 | |
|         }
 | |
|         render_icon(parent, 'fire', beestat.style.color.red.base, subscript, tooltip);
 | |
|         break;
 | |
|       case 'auxiliary_heat_2':
 | |
|         render_icon(parent, 'fire', beestat.style.color.red.base, '2', 'Aux heat 2');
 | |
|         break;
 | |
|       case 'auxiliary_heat_3':
 | |
|         render_icon(parent, 'fire', beestat.style.color.red.base, '3', 'Aux heat 3');
 | |
|         break;
 | |
|       case 'humidifier':
 | |
|         render_icon(parent, 'water_percent', beestat.style.color.gray.base, '', 'Humidifier');
 | |
|         break;
 | |
|       case 'dehumidifier':
 | |
|         render_icon(parent, 'water_off', beestat.style.color.gray.base, '', 'Dehumidifier');
 | |
|         break;
 | |
|       case 'ventilator':
 | |
|         render_icon(parent, 'air_purifier', beestat.style.color.gray.base, 'v', 'Ventilator');
 | |
|         break;
 | |
|       case 'economizer':
 | |
|         render_icon(parent, 'cash', beestat.style.color.gray.base, '', 'Economizer');
 | |
|         break;
 | |
|       }
 | |
|     });
 | |
|   }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Decorate the climate text on the bottom right.
 | |
|  *
 | |
|  * @param {rocket.Elements} parent
 | |
|  */
 | |
| beestat.component.card.system.prototype.decorate_climate_ = function(parent) {
 | |
|   var thermostat = beestat.cache.thermostat[this.thermostat_id_];
 | |
| 
 | |
|   var climate = beestat.thermostat.get_current_climate(
 | |
|     thermostat.thermostat_id
 | |
|   );
 | |
| 
 | |
|   var climate_container = $.createElement('div')
 | |
|     .style({
 | |
|       'display': 'inline-flex',
 | |
|       'align-items': 'center',
 | |
|       'float': 'right'
 | |
|     });
 | |
|   parent.appendChild(climate_container);
 | |
| 
 | |
|   var icon;
 | |
|   if (climate.climateRef === 'home') {
 | |
|     icon = 'home';
 | |
|   } else if (climate.climateRef === 'away') {
 | |
|     icon = 'update';
 | |
|   } else if (climate.climateRef === 'sleep') {
 | |
|     icon = 'alarm_snooze';
 | |
|   } else {
 | |
|     icon = (climate.isOccupied === true) ? 'home' : 'update';
 | |
|   }
 | |
| 
 | |
|   (new beestat.component.icon(icon)
 | |
|     .set_size(24)
 | |
|   ).render(climate_container);
 | |
| 
 | |
|   climate_container.appendChild($.createElement('span')
 | |
|     .innerHTML(climate.name)
 | |
|     .style('margin-left', beestat.style.size.gutter / 4));
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Decorate time to heat/cool. This is how long it will take your home to heat
 | |
|  * or cool to the desired setpoint.
 | |
|  *
 | |
|  * @param {rocket.Elements} parent
 | |
|  */
 | |
| beestat.component.card.system.prototype.decorate_time_to_temperature_ = function(parent) {
 | |
|   const thermostat = beestat.cache.thermostat[this.thermostat_id_];
 | |
|   const indoor_temperature = thermostat.temperature;
 | |
| 
 | |
|   const operating_mode = beestat.thermostat.get_operating_mode(
 | |
|     thermostat.thermostat_id
 | |
|   );
 | |
| 
 | |
|   // Convert "heat_1" etc to "heat"
 | |
|   const simplified_operating_mode = operating_mode.replace(/[_\d]|auxiliary/g, '');
 | |
| 
 | |
|   /**
 | |
|    * If the system is off or if we've already reached the setpoint, don't show
 | |
|    * this. The HVAC system can still be running due to minimum runtimes or if
 | |
|    * the setpoint changes suddenly.
 | |
|    */
 | |
|   if (
 | |
|     operating_mode === 'off' ||
 | |
|     (
 | |
|       simplified_operating_mode === 'heat' &&
 | |
|       indoor_temperature >= thermostat.setpoint_heat
 | |
|     ) ||
 | |
|     (
 | |
|       simplified_operating_mode === 'cool' &&
 | |
|       indoor_temperature <= thermostat.setpoint_cool
 | |
|     )
 | |
|   ) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   const container = $.createElement('div').style({
 | |
|     'background': beestat.style.color.bluegray.dark,
 | |
|     'padding': beestat.style.size.gutter / 2,
 | |
|     'margin-top': beestat.style.size.gutter,
 | |
|     'border-radius': beestat.style.size.border_radius
 | |
|   });
 | |
|   parent.appendChild(container);
 | |
| 
 | |
|   let header_text = 'Time to ' + simplified_operating_mode;
 | |
|   let text;
 | |
|   if (
 | |
|     thermostat.profile === null ||
 | |
|     thermostat.profile.temperature[operating_mode] === null
 | |
|   ) {
 | |
|     // If there is no profile data; TTT is unknown.
 | |
|     text = 'Unknown';
 | |
|   } else {
 | |
|     const linear_trendline = thermostat.profile.temperature[operating_mode].linear_trendline;
 | |
|     const outdoor_temperature = thermostat.weather.temperature;
 | |
|     const degrees_per_hour = (linear_trendline.slope * outdoor_temperature) + linear_trendline.intercept;
 | |
| 
 | |
|     // header_text += ' (' +
 | |
|     //   beestat.temperature({
 | |
|     //     'temperature': degrees_per_hour,
 | |
|     //     'delta': true,
 | |
|     //     'units': true
 | |
|     //   }) +
 | |
|     //   ' / h)';
 | |
| 
 | |
|     if (
 | |
|       (
 | |
|         simplified_operating_mode === 'heat' &&
 | |
|         degrees_per_hour < 0.05
 | |
|       ) ||
 | |
|       (
 | |
|         simplified_operating_mode === 'cool' &&
 | |
|         degrees_per_hour > -0.05
 | |
|       )
 | |
|     ) {
 | |
|       // If the degrees would display as 0.0/h, go for "Never" as the time.
 | |
|       text = 'Never';
 | |
|     } else {
 | |
|       let degrees_to_go;
 | |
|       let hours_to_go;
 | |
|       switch (simplified_operating_mode) {
 | |
|       case 'heat':
 | |
|         degrees_to_go = thermostat.setpoint_heat - indoor_temperature;
 | |
|         hours_to_go = degrees_to_go / degrees_per_hour;
 | |
|         text = beestat.time(hours_to_go * 60 * 60)
 | |
|           .replace(/^0h /, '');
 | |
|         break;
 | |
|       case 'cool':
 | |
|         degrees_to_go = indoor_temperature - thermostat.setpoint_cool;
 | |
|         hours_to_go = degrees_to_go / degrees_per_hour * -1;
 | |
|         text = beestat.time(hours_to_go * 60 * 60)
 | |
|           .replace(/^0h /, '');
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       /**
 | |
|        * Show the actual time the temperature will be reached if there are
 | |
|        * less than 12 hours to go. Otherwise it's mostly irrelevant.
 | |
|        */
 | |
|       if (hours_to_go <= 12) {
 | |
|         text += ' (' +
 | |
|           moment()
 | |
|             .add(hours_to_go, 'hour')
 | |
|             .format('h:mm a') +
 | |
|           ')';
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const grid = $.createElement('div')
 | |
|     .style({
 | |
|       'display': 'grid',
 | |
|       'grid-template-columns': '3fr 1fr', // 75% for left, 25% for right
 | |
|       'grid-gap': `${beestat.style.size.gutter}px`,
 | |
|       'align-items': 'center', // Center content vertically
 | |
|     });
 | |
| 
 | |
|   const left = $.createElement('div');
 | |
|     // .style({
 | |
|     //   'overflow-wrap': 'break-word', // Allow wrapping if the content doesn't fit
 | |
|     //   'word-wrap': 'break-word',
 | |
|     //   'word-break': 'break-word',
 | |
|     // });
 | |
| 
 | |
|   left.appendChild(
 | |
|     $.createElement('div')
 | |
|       .style('font-weight', 'bold')
 | |
|       .innerText(header_text)
 | |
|   );
 | |
|   left.appendChild(
 | |
|     $.createElement('div')
 | |
|       .innerText(text)
 | |
|   );
 | |
| 
 | |
|   const right = $.createElement('div')
 | |
|     .style({
 | |
|       'text-align': 'right', // Right-align the content of the right column
 | |
|     });
 | |
| 
 | |
|   var cancel = new beestat.component.tile()
 | |
|     .set_icon('chart_line')
 | |
|     .set_shadow(false)
 | |
|     .set_background_hover_color('#fff')
 | |
|     .set_text_hover_color(beestat.style.color.bluegray.dark)
 | |
|     .set_text('Detail')
 | |
|     .addEventListener('click', function () {
 | |
|       (new beestat.component.modal.time_to_detail()).render();
 | |
|     })
 | |
|     .render(right);
 | |
| 
 | |
|   grid.appendChild(left);
 | |
|   grid.appendChild(right);
 | |
| 
 | |
|   container.appendChild(grid);
 | |
| 
 | |
|   // setTimeout(function() {
 | |
|   //   (new beestat.component.modal.time_to_detail()).render();
 | |
|   // }, 0);
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Decorate the menu
 | |
|  *
 | |
|  * @param {rocket.Elements} parent
 | |
|  */
 | |
| beestat.component.card.system.prototype.decorate_top_right_ = function(parent) {
 | |
|   var thermostat = beestat.cache.thermostat[this.thermostat_id_];
 | |
| 
 | |
|   var menu = (new beestat.component.menu()).render(parent);
 | |
| 
 | |
|   menu.add_menu_item(new beestat.component.menu_item()
 | |
|     .set_text('Thermostat Info')
 | |
|     .set_icon('thermostat')
 | |
|     .set_callback(function() {
 | |
|       (new beestat.component.modal.thermostat_info()).render();
 | |
|     }));
 | |
| 
 | |
|   if ($.values(thermostat.filters).length > 0) {
 | |
|     menu.add_menu_item(new beestat.component.menu_item()
 | |
|       .set_text('Filter Info')
 | |
|       .set_icon('air_filter')
 | |
|       .set_callback(function() {
 | |
|         (new beestat.component.modal.filter_info()).render();
 | |
|       }));
 | |
|   }
 | |
| 
 | |
|   menu.add_menu_item(new beestat.component.menu_item()
 | |
|     .set_text('Help')
 | |
|     .set_icon('help_circle')
 | |
|     .set_callback(function() {
 | |
|       window.open('https://doc.beestat.io/System-44b441bdd99b4c3991d6e0ace2dce893');
 | |
|     }));
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Get the title of the card.
 | |
|  *
 | |
|  * @return {string} The title of the card.
 | |
|  */
 | |
| beestat.component.card.system.prototype.get_title_ = function() {
 | |
|   var thermostat = beestat.cache.thermostat[this.thermostat_id_];
 | |
| 
 | |
|   return 'System - ' + thermostat.name;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Get the subtitle of the card.
 | |
|  *
 | |
|  * @return {string} The subtitle of the card.
 | |
|  */
 | |
| beestat.component.card.system.prototype.get_subtitle_ = function() {
 | |
|   var thermostat = beestat.cache.thermostat[beestat.setting('thermostat_id')];
 | |
| 
 | |
|   var ecobee_thermostat = beestat.cache.ecobee_thermostat[
 | |
|     thermostat.ecobee_thermostat_id
 | |
|   ];
 | |
| 
 | |
|   var climate = beestat.thermostat.get_current_climate(
 | |
|     thermostat.thermostat_id
 | |
|   );
 | |
| 
 | |
|   // Is the temperature overridden?
 | |
|   var override = (
 | |
|     thermostat.setpoint_heat !== climate.heatTemp ||
 | |
|     thermostat.setpoint_cool !== climate.coolTemp
 | |
|   );
 | |
| 
 | |
|   // Get the heat/cool values to display.
 | |
|   var heat;
 | |
|   if (override === true) {
 | |
|     heat = thermostat.setpoint_heat;
 | |
|   } else {
 | |
|     heat = climate.heatTemp;
 | |
|   }
 | |
| 
 | |
|   var cool;
 | |
|   if (override === true) {
 | |
|     cool = thermostat.setpoint_cool;
 | |
|   } else {
 | |
|     cool = climate.coolTemp;
 | |
|   }
 | |
| 
 | |
|   // Translate ecobee strings to GUI strings.
 | |
|   var hvac_modes = {
 | |
|     'off': 'Off',
 | |
|     'auto': 'Auto',
 | |
|     'auxHeatOnly': 'Aux',
 | |
|     'cool': 'Cool',
 | |
|     'heat': 'Heat'
 | |
|   };
 | |
| 
 | |
|   var hvac_mode = hvac_modes[ecobee_thermostat.settings.hvacMode];
 | |
| 
 | |
|   heat = beestat.temperature({
 | |
|     'temperature': heat
 | |
|   });
 | |
|   cool = beestat.temperature({
 | |
|     'temperature': cool
 | |
|   });
 | |
| 
 | |
|   var subtitle = hvac_mode;
 | |
| 
 | |
|   if (ecobee_thermostat.settings.hvacMode !== 'off') {
 | |
|     if (override === true) {
 | |
|       subtitle += ' • Overridden';
 | |
|     } else {
 | |
|       subtitle += ' • Schedule';
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (ecobee_thermostat.settings.hvacMode === 'auto') {
 | |
|     subtitle += ' • ' + heat + ' - ' + cool;
 | |
|   } else if (
 | |
|     ecobee_thermostat.settings.hvacMode === 'heat' ||
 | |
|     ecobee_thermostat.settings.hvacMode === 'auxHeatOnly'
 | |
|   ) {
 | |
|     subtitle += ' • ' + heat;
 | |
|   } else if (
 | |
|     ecobee_thermostat.settings.hvacMode === 'cool'
 | |
|   ) {
 | |
|     subtitle += ' • ' + cool;
 | |
|   }
 | |
| 
 | |
|   const last_synced_on_m =
 | |
|     moment.utc(beestat.user.get().sync_status.thermostat).local();
 | |
| 
 | |
|   const data_as_of_m =
 | |
|     moment.utc(ecobee_thermostat.runtime.lastStatusModified).local();
 | |
| 
 | |
|   // Include the date if the sync gets more than 3 hours behind.
 | |
|   let format;
 | |
|   if (moment().diff(data_as_of_m, 'minutes') > 180) {
 | |
|     format = 'MMM Do [at] h:mm a';
 | |
|   } else {
 | |
|     format = 'h:mm a';
 | |
|   }
 | |
| 
 | |
|   subtitle +=
 | |
|     '<br/><span title="Last sync: ' +
 | |
|     last_synced_on_m.format(format) +
 | |
|     '" style="color: ' +
 | |
|     beestat.style.color.gray.dark +
 | |
|     '">As of ' +
 | |
|     data_as_of_m.format(format) +
 | |
|     '</span>';
 | |
| 
 | |
|   return subtitle;
 | |
| };
 |