diff --git a/css/dashboard.css b/css/dashboard.css index c592152..8c54cc7 100644 --- a/css/dashboard.css +++ b/css/dashboard.css @@ -282,16 +282,17 @@ input[type=range]{ -webkit-appearance: none; background:transparent; margin-top: 5px; + --background: #37474f; } input[type=range]::-webkit-slider-runnable-track { - background: #37474f; + background: var(--background); height: 5px; border-radius: 5px; } input[type=range]::-moz-range-track { - background: #37474f; + background: var(--background); height: 5px; border-radius: 5px; } diff --git a/js/component/card/floor_plan_editor.js b/js/component/card/floor_plan_editor.js index 279bf4e..793b4ae 100644 --- a/js/component/card/floor_plan_editor.js +++ b/js/component/card/floor_plan_editor.js @@ -578,7 +578,7 @@ beestat.component.card.floor_plan_editor.prototype.decorate_info_pane_room_ = fu const sensors = {}; Object.values(beestat.cache.thermostat).forEach(function(thermostat) { const thermostat_sensors = Object.values(beestat.cache.sensor).filter(function(sensor) { - return sensor.thermostat_id === self.thermostat_id_; + return sensor.thermostat_id === thermostat.thermostat_id; }) .sort(function(a, b) { return a.name.localeCompare(b.name, 'en', {'sensitivity': 'base'}); diff --git a/js/component/card/three_d.js b/js/component/card/three_d.js index 6ae3688..69949cb 100644 --- a/js/component/card/three_d.js +++ b/js/component/card/three_d.js @@ -340,6 +340,10 @@ beestat.component.card.three_d.prototype.decorate_drawing_pane_ = function(paren this.get_data_() ); + this.scene_.addEventListener('change_active_room', function() { + self.update_hud_(); + }); + // Set the initial date. // if (this.has_data_() === true) { this.update_scene_(); @@ -428,6 +432,19 @@ beestat.component.card.three_d.prototype.decorate_drawing_pane_ = function(paren beestat.component.card.three_d.prototype.decorate_controls_ = function(parent) { const self = this; + const active_room = this.scene_.get_active_room_(); + let thermostat_ids; + if ( + active_room !== null && + active_room.sensor_id !== undefined + ) { + thermostat_ids = [beestat.cache.sensor[active_room.sensor_id].thermostat_id]; + } else { + thermostat_ids = Object.keys( + beestat.floor_plan.get_thermostat_ids_map(this.floor_plan_id_) + ); + } + if (parent !== undefined) { this.controls_container_ = parent; } @@ -441,6 +458,8 @@ beestat.component.card.three_d.prototype.decorate_controls_ = function(parent) { const range = new beestat.component.input.range(); const time_container = document.createElement('div'); + range.set_background(this.get_chart_gradient_(thermostat_ids)); + const container = document.createElement('div'); Object.assign(container.style, { 'display': 'flex', @@ -555,6 +574,108 @@ beestat.component.card.three_d.prototype.decorate_controls_ = function(parent) { }); }; +/** + * Get a CSS linear gradient style that represents the runtime chart. + * + * @param {array} thermostat_ids Which thermostat_ids to include data from. + * + * @return {string} + */ +beestat.component.card.three_d.prototype.get_chart_gradient_ = function(thermostat_ids) { + const data = this.get_data_(); + + const background_color_rgb = beestat.style.hex_to_rgb(beestat.style.color.bluegray.base); + const background_color = 'rgba(' + background_color_rgb.r + ',' + background_color_rgb.g + ',' + background_color_rgb.b + ',1)'; + + let current_color = background_color; + const gradient = [ + { + 'color': current_color, + 'position': 0 + } + ]; + + const date_m = moment(); + for (let i = 0; i < 287; i++) { + const minute_of_day = i * 5; + date_m.hours(Math.floor(minute_of_day / 60)); + date_m.minutes(Math.floor(minute_of_day % 60)); + const time = date_m.format('HH:mm'); + + let red = 0; + let green = 0; + let blue = 0; + let alpha = 0; + let count = 0; + + let this_color = background_color; + [ + 'fan', + 'compressor_heat_1', + 'compressor_heat_2', + 'auxiliary_heat_1', + 'auxiliary_heat_2', + 'compressor_cool_1', + 'compressor_cool_2' + ].forEach(function(series_code) { + thermostat_ids.forEach(function(thermostat_id) { + if ( + data.series[series_code][thermostat_id] !== undefined && + data.series[series_code][thermostat_id][time] !== undefined && + data.series[series_code][thermostat_id][time] > 0 + ) { + // Only resets these if there is data to override it. + if (count > 0) { + red = 0; + green = 0; + blue = 0; + alpha = 0; + count = 0; + } + + const color = beestat.style.hex_to_rgb(beestat.series[series_code].color); + red += color.r; + green += color.g; + blue += color.b; + alpha += data.series[series_code][thermostat_id][time] / thermostat_ids.length; + count++; + } + }); + + let rgb; + if (count > 0) { + rgb = { + 'r': red / count, + 'g': green / count, + 'b': blue / count + }; + alpha /= count; + this_color = 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + alpha + ')'; + } else { + this_color = background_color; + } + }); + + if (this_color !== current_color) { + gradient.push({ + 'color': this_color, + 'position': Math.round(i / 288 * 100 * 10) / 10 + }); + current_color = this_color; + } + } + + let gradient_string = ['90deg']; + + for (let i = 0; i < gradient.length; i++) { + const start = gradient[i].position + '%'; + const end = gradient[i + 1] !== undefined ? gradient[i + 1].position + '%' : '100%'; + gradient_string.push(gradient[i].color + ' ' + start + ' ' + end); + } + + return 'linear-gradient(' + gradient_string.join(', ') + ')'; +}; + /** * Watermark. * @@ -592,7 +713,7 @@ beestat.component.card.three_d.prototype.decorate_toolbar_ = function(parent) { // Add room tile_group.add_tile(new beestat.component.tile() .set_icon('label_off') - .set_title('Labels') + .set_title('Toggle Labels') .set_text_color(beestat.style.color.gray.light) .set_background_color(beestat.style.color.bluegray.base) .set_background_hover_color(beestat.style.color.bluegray.light) @@ -611,7 +732,7 @@ beestat.component.card.three_d.prototype.decorate_toolbar_ = function(parent) { // Add room tile_group.add_tile(new beestat.component.tile() .set_icon('restart_off') - .set_title('Auto-Rotate') + .set_title('Toggle Auto-Rotate') .set_text_color(beestat.style.color.gray.light) .set_background_color(beestat.style.color.bluegray.base) .set_background_hover_color(beestat.style.color.bluegray.light) diff --git a/js/component/input/range.js b/js/component/input/range.js index 42ef90e..5b23e38 100644 --- a/js/component/input/range.js +++ b/js/component/input/range.js @@ -79,3 +79,16 @@ beestat.component.input.range.prototype.set_max = function(max) { return this; }; + +/** + * Set the background value of the range input. + * + * @param {string} background + * + * @return {beestat.component.input.range} This. + */ +beestat.component.input.range.prototype.set_background = function(background) { + this.input_.style.setProperty('--background', background); + + return this; +}; diff --git a/js/component/scene.js b/js/component/scene.js index e3218bb..36ccbbf 100644 --- a/js/component/scene.js +++ b/js/component/scene.js @@ -223,12 +223,8 @@ beestat.component.scene.prototype.mouseup_handler_ = function() { window.removeEventListener('touchend', this.mouseup_handler_); if (this.dragged_ === false) { - // Clear any active state - // if (this.active_mesh_ !== undefined) { - // this.active_mesh_.userData.outline.visible = false; - // } - this.active_mesh_ = this.intersected_mesh_; + this.dispatchEvent('change_active_room'); this.update_(); } }; @@ -364,14 +360,14 @@ beestat.component.scene.prototype.update_ = function() { // Set the color of each room floor_plan.data.groups.forEach(function(group) { group.rooms.forEach(function(room) { - const value_sprite = self.rooms_[room.room_id].userData.sprites.value; - const icon_sprite = self.rooms_[room.room_id].userData.sprites.icon; + const value_sprite = self.meshes_[room.room_id].userData.sprites.value; + const icon_sprite = self.meshes_[room.room_id].userData.sprites.icon; // Room outline - if (self.rooms_[room.room_id] === self.active_mesh_) { - self.rooms_[room.room_id].userData.outline.visible = true; + if (self.meshes_[room.room_id] === self.active_mesh_) { + self.meshes_[room.room_id].userData.outline.visible = true; } else { - self.rooms_[room.room_id].userData.outline.visible = false; + self.meshes_[room.room_id].userData.outline.visible = false; } let color; @@ -458,7 +454,7 @@ beestat.component.scene.prototype.update_ = function() { // Labels if ( self.labels_ === true || - self.rooms_[room.room_id] === self.active_mesh_ + self.meshes_[room.room_id] === self.active_mesh_ ) { switch (self.data_type_) { case 'temperature': @@ -494,7 +490,7 @@ beestat.component.scene.prototype.update_ = function() { icon_sprite.material = self.get_blank_label_material_(); } - self.rooms_[room.room_id].material.color.setHex(color.replace('#', '0x')); + self.meshes_[room.room_id].material.color.setHex(color.replace('#', '0x')); }); }); @@ -568,13 +564,14 @@ beestat.component.scene.prototype.add_room_ = function(layer, group, room) { mesh.translateY(room.y); // Store a reference to the mesh representing each room. - if (this.rooms_ === undefined) { - this.rooms_ = {}; + if (this.meshes_ === undefined) { + this.meshes_ = {}; } - // TODO Do I need both these? - this.rooms_[room.room_id] = mesh; + // Allow me to go from room -> mesh and mesh -> room + this.meshes_[room.room_id] = mesh; // mesh.userData.room_id = room.room_id; + mesh.userData.room = room; layer.add(mesh); @@ -982,3 +979,16 @@ beestat.component.scene.prototype.dispose = function() { window.cancelAnimationFrame(this.animation_frame_); beestat.component.prototype.dispose.apply(this, arguments); }; + +/** + * Get the currently active room. + * + * @return {object} + */ +beestat.component.scene.prototype.get_active_room_ = function() { + if (this.active_mesh_ !== undefined) { + return this.active_mesh_.userData.room; + } + + return null; +};