1
0
mirror of https://github.com/beestat/app.git synced 2025-05-24 02:14:03 -04:00

Added 3D viewer to Visualize

Added 3D viewer to Visualize
This commit is contained in:
Jon Ziebell 2022-08-18 21:40:35 -04:00
parent 7464630fb5
commit c3891ffff8
42 changed files with 1788 additions and 528 deletions

View File

@ -417,7 +417,9 @@ input[type=radio] {
.icon.alarm_snooze:before { content: "\F068E"; }
.icon.alpha_b:before { content: "\F0AEF"; }
.icon.alpha_b_box:before { content: "\F0B09"; }
.icon.arrow_expand_horizontal:before { content: "\F084E"; }
.icon.arrow_expand_vertical:before { content: "\F084F"; }
.icon.arrow_horizontal_lock:before { content: "\F115B"; }
.icon.arrow_left:before { content: "\F004D"; }
.icon.basket_fill:before { content: "\F0077"; }
.icon.basket_unfill:before { content: "\F0078"; }
@ -429,7 +431,8 @@ input[type=radio] {
.icon.calendar:before { content: "\F00ED"; }
.icon.calendar_alert:before { content: "\F0A31"; }
.icon.calendar_edit:before { content: "\F08A7"; }
.icon.calendar_range:before { content: "\F0679"; }
.icon.calendar_month:before { content: "\F0E17"; }
.icon.calendar_week:before { content: "\F0A33"; }
.icon.cancel:before { content: "\F073A"; }
.icon.card_plus_outline:before { content: "\F1200"; }
.icon.card_remove_outline:before { content: "\F1605"; }
@ -453,6 +456,7 @@ input[type=radio] {
.icon.eye_circle:before { content: "\F0B94"; }
.icon.eye_off:before { content: "\F0209"; }
.icon.fan:before { content: "\F0210"; }
.icon.fast_forward:before { content: "\F0211"; }
.icon.fire:before { content: "\F0238"; }
.icon.floor_plan:before { content: "\F0821"; }
.icon.google_play:before { content: "\F02BC"; }
@ -501,7 +505,9 @@ input[type=radio] {
.icon.numeric_9_box:before { content: "\F03BC"; }
.icon.open_in_new:before { content: "\F03CC"; }
.icon.patreon:before { content: "\F0882"; }
.icon.pause:before { content: "\F03E4"; }
.icon.pencil:before { content: "\F03EB"; }
.icon.play:before { content: "\F040A"; }
.icon.plus:before { content: "\F0415"; }
.icon.pound:before { content: "\F0423"; }
.icon.redo:before { content: "\F044E"; }
@ -509,6 +515,7 @@ input[type=radio] {
.icon.resistor:before { content: "\F0B44"; }
.icon.snowflake:before { content: "\F0717"; }
.icon.swap_horizontal:before { content: "\F04E1"; }
.icon.thermometer:before { content: "\F050F"; }
.icon.thermostat:before { content: "\F0393"; }
.icon.thumb_up:before { content: "\F0513"; }
.icon.tune:before { content: "\F062E"; }

View File

@ -87,6 +87,7 @@ window.addEventListener('resize', rocket.throttle(100, function() {
// First run
var $ = rocket.extend(rocket.$, rocket);
$.ready(function() {
moment.suppressDeprecationWarnings = true;
if (window.environment === 'live') {
Sentry.init({
'dsn': 'https://af9fd2cf6cda49dcb93dcaf02fe39fc6@sentry.io/3736982',

View File

@ -57,3 +57,84 @@ beestat.floor_plan.get_area_room = function(room, round = true) {
return area;
};
beestat.floor_plan.get_bounding_box = function(floor_plan_id) {
const floor_plan = beestat.cache.floor_plan[floor_plan_id];
let min_x = Infinity;
let max_x = -Infinity;
let min_y = Infinity;
let max_y = -Infinity;
floor_plan.data.groups.forEach(function(group) {
const bounding_box = beestat.floor_plan.get_bounding_box_group(group);
min_x = Math.min(bounding_box.left, min_x);
max_x = Math.max(bounding_box.right, max_x);
min_y = Math.min(bounding_box.top, min_y);
max_y = Math.max(bounding_box.bottom, max_y);
});
return {
'width': max_x - min_x,
'height': max_y - min_y,
'left': min_x,
'top': min_y,
'right': max_x,
'bottom': max_y,
'x': min_x,
'y': min_y
};
};
beestat.floor_plan.get_bounding_box_group = function(group) {
let min_x = Infinity;
let max_x = -Infinity;
let min_y = Infinity;
let max_y = -Infinity;
group.rooms.forEach(function(room) {
const bounding_box = beestat.floor_plan.get_bounding_box_room(room);
min_x = Math.min(bounding_box.left, min_x);
max_x = Math.max(bounding_box.right, max_x);
min_y = Math.min(bounding_box.top, min_y);
max_y = Math.max(bounding_box.bottom, max_y);
});
return {
'width': max_x - min_x,
'height': max_y - min_y,
'left': min_x,
'top': min_y,
'right': max_x,
'bottom': max_y,
'x': min_x,
'y': min_y
};
};
beestat.floor_plan.get_bounding_box_room = function(room) {
let min_x = Infinity;
let max_x = -Infinity;
let min_y = Infinity;
let max_y = -Infinity;
room.points.forEach(function(point) {
min_x = Math.min(room.x + point.x, min_x);
max_x = Math.max(room.x + point.x, max_x);
min_y = Math.min(room.y + point.y, min_y);
max_y = Math.max(room.y + point.y, max_y);
});
return {
'width': max_x - min_x,
'height': max_y - min_y,
'left': min_x,
'top': min_y,
'right': max_x,
'bottom': max_y,
'x': min_x,
'y': min_y
};
};

View File

@ -4,7 +4,7 @@ beestat.runtime_sensor = {};
* Get a bunch of data for the current runtime_sensor rows. Includes basically
* everything you need to make a cool chart.
*
* @param {number} thermostat_id The thermostat_id to get data for.
* @param {number} sensor_ids The sensor_ids to get data for.
* @param {object} range Range settings.
* @param {string} key The key to pull the data from inside
* beestat.cache.data. This exists because runtime_sensor data exists in
@ -12,7 +12,7 @@ beestat.runtime_sensor = {};
*
* @return {object} The data.
*/
beestat.runtime_sensor.get_data = function(thermostat_id, range, key) {
beestat.runtime_sensor.get_data = function(sensor_ids, range, key) {
var data = {
'x': [],
'series': {},
@ -51,30 +51,28 @@ beestat.runtime_sensor.get_data = function(thermostat_id, range, key) {
beestat.style.color.lightblue.dark
];
// Duration objects. These are passed by reference into the metadata.
var durations = {};
var series_codes = [];
// Get and sort all the sensors.
var sensors = beestat.sensor.get_sorted();
data.metadata.sensors = sensors;
data.metadata.sensors = [];
// Set up the series_codes.
const sensor_series_colors = {};
sensors.forEach(function(sensor, i) {
if (sensor.thermostat_id === thermostat_id) {
series_codes.push('temperature_' + sensor.sensor_id);
series_codes.push('occupancy_' + sensor.sensor_id);
sensor_ids.forEach(function(sensor_id, i) {
const sensor = beestat.cache.sensor[sensor_id];
sensor_series_colors[sensor.sensor_id] = colors[i];
series_codes.push('temperature_' + sensor.sensor_id);
series_codes.push('occupancy_' + sensor.sensor_id);
if (sensor.type === 'thermostat') {
series_codes.push('air_quality_' + sensor.sensor_id);
series_codes.push('voc_concentration_' + sensor.sensor_id);
series_codes.push('co2_concentration_' + sensor.sensor_id);
}
sensor_series_colors[sensor.sensor_id] = colors[i];
if (sensor.type === 'thermostat') {
series_codes.push('air_quality_' + sensor.sensor_id);
series_codes.push('voc_concentration_' + sensor.sensor_id);
series_codes.push('co2_concentration_' + sensor.sensor_id);
}
data.metadata.sensors.push(sensor);
});
series_codes.push('dummy');
@ -87,7 +85,6 @@ beestat.runtime_sensor.get_data = function(thermostat_id, range, key) {
data.series[series_code] = [];
data.metadata.series[series_code] = {
'active': false,
'durations': {},
'data': {}
};
if (series_code === 'dummy') {
@ -103,8 +100,6 @@ beestat.runtime_sensor.get_data = function(thermostat_id, range, key) {
data.metadata.series[series_code].name = beestat.cache.sensor[sensor_id].name;
data.metadata.series[series_code].color = sensor_series_colors[sensor_id];
}
durations[series_code] = {'seconds': 0};
});
var begin_m;
@ -141,7 +136,7 @@ beestat.runtime_sensor.get_data = function(thermostat_id, range, key) {
data.metadata.series.dummy.active = true;
if (runtime_sensors[current_m.valueOf()] !== undefined) {
sensors.forEach(function(sensor, j) {
data.metadata.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);
@ -177,7 +172,7 @@ beestat.runtime_sensor.get_data = function(thermostat_id, range, key) {
if (runtime_sensor.occupancy === true) {
let swimlane_properties =
beestat.component.chart.runtime_sensor_detail_occupancy.get_swimlane_properties(
sensors.length,
data.metadata.sensors.length,
j
);
@ -188,7 +183,7 @@ beestat.runtime_sensor.get_data = function(thermostat_id, range, key) {
if (sequential['occupancy_' + runtime_sensor.sensor_id] > 0) {
let swimlane_properties =
beestat.component.chart.runtime_sensor_detail_occupancy.get_swimlane_properties(
sensors.length,
data.metadata.sensors.length,
j
);
@ -201,16 +196,14 @@ beestat.runtime_sensor.get_data = function(thermostat_id, range, key) {
}
});
} else {
sensors.forEach(function(sensor) {
if (sensor.thermostat_id === thermostat_id) {
data.series['temperature_' + sensor.sensor_id].push(null);
data.series['occupancy_' + sensor.sensor_id].push(null);
data.metadata.sensors.forEach(function(sensor) {
data.series['temperature_' + sensor.sensor_id].push(null);
data.series['occupancy_' + sensor.sensor_id].push(null);
if (sensor.type === 'thermostat') {
data.series['air_quality_' + sensor.sensor_id].push(null);
data.series['voc_concentration_' + sensor.sensor_id].push(null);
data.series['co2_concentration_' + sensor.sensor_id].push(null);
}
if (sensor.type === 'thermostat') {
data.series['air_quality_' + sensor.sensor_id].push(null);
data.series['voc_concentration_' + sensor.sensor_id].push(null);
data.series['co2_concentration_' + sensor.sensor_id].push(null);
}
});
}

View File

@ -1,24 +0,0 @@
beestat.sensor = {};
/**
* Get a sorted list of all sensors attached to the current thermostat.
*
* @param {number} thermostat_id Thermostat to get this list for.
*
* @return {array} The sensors.
*/
beestat.sensor.get_sorted = function(thermostat_id) {
// Get and sort all the sensors.
const sensors = [];
Object.values(beestat.cache.sensor).forEach(function(sensor) {
if (sensor.thermostat_id === (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;
};

View File

@ -68,7 +68,21 @@ beestat.setting = function(argument_1, opt_value, opt_callback) {
'thermostat.#.profile.ignore_solar_gain': false,
'floor_plan_id': null
'visualize.data_type': 'temperature',
'visualize.range_type': 'dynamic',
'visualize.range_dynamic': 7,
'visualize.range_static.begin': moment()
.subtract(3, 'day')
.format('MM/DD/YYYY'),
'visualize.range_static.end': moment()
.format('MM/DD/YYYY'),
'visualize.floor_plan_id': null,
'visualize.heat_map_type': 'relative',
'visualize.heat_map_absolute.temperature.min': 70,
'visualize.heat_map_absolute.temperature.max': 80,
'visualize.heat_map_absolute.occupancy.min': 0,
'visualize.heat_map_absolute.occupancy.max': 100
};
// Figure out what we're trying to do.

View File

@ -339,3 +339,61 @@ beestat.series.co2_concentration = {
'name': 'CO₂',
'color': beestat.style.color.blue.base
};
/**
* Generate a number of colors between two points.
*
* @param {Object} colors Array of colors in RGB.
* @param {number} steps Number of colors to generate.
*
* @see http://forums.codeguru.com/showthread.php?259953-Code-to-create-Color-Gradient-programatically&s=4710043a327ee6059da1f8433ad1e5d2&p=795289#post795289
*
* @return {Array.<Object>} RGB color array
*/
beestat.style.generate_gradient = function(colors, steps) {
const gradient_count = colors.length - 1;
let gradient = [];
for (let j = 0; j < gradient_count; j++) {
gradient = gradient.concat(this.generate_gradient_(
colors[j],
colors[j + 1],
steps
));
}
return gradient;
};
beestat.style.generate_gradient_ = function(begin, end, steps) {
var gradient = [];
for (var i = 0; i < steps; i++) {
var n = i / (steps - 1);
gradient.push({
'r': Math.round(begin.r * (1 - n) + end.r * n),
'g': Math.round(begin.g * (1 - n) + end.g * n),
'b': Math.round(begin.b * (1 - n) + end.b * n)
});
}
return gradient;
};
/**
* Convert a hex string to RGB components.
*
* @param {string} hex
*
* @return {object} RGB
*/
beestat.style.hex_to_rgb = function(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
'r': parseInt(result[1], 16),
'g': parseInt(result[2], 16),
'b': parseInt(result[3], 16)
} : null;
};
beestat.style.rgb_to_hex = function(rgb) {
return "#" + ((1 << 24) + (rgb.r << 16) + (rgb.g << 8) + rgb.b).toString(16).slice(1);
}

View File

@ -9,33 +9,10 @@ beestat.extend(beestat.component.card, beestat.component);
beestat.component.card.prototype.box_shadow_ = true;
/**
* [get_class_name_recursive_ description]
* Decorate
*
* @param {[type]} parent [description]
* @param {[type]} opt_prefix [description]
*
* @return {[type]} [description]
* @param {rocket.Elements} parent
*/
beestat.component.card.prototype.get_class_name_recursive_ = function(parent, opt_prefix) {
for (var i in parent) {
if (
(parent[i]) &&
(parent[i].prototype) &&
(this instanceof parent[i])
) {
var name = opt_prefix ? rocket.clone(opt_prefix) : [];
name.push(i);
if (parent[i] === this.constructor) {
return name;
}
name = this.get_class_name_recursive_(parent[i], name);
if (name) {
return name;
}
}
}
};
beestat.component.card.prototype.decorate_ = function(parent) {
this.hide_loading_();

View File

@ -25,8 +25,6 @@ beestat.component.card.air_quality_detail = function(thermostat_id) {
beestat.dispatcher.addEventListener(
[
'setting.air_quality_detail_range_type',
'setting.air_quality_detail_range_dynamic',
'cache.data.air_quality_detail__runtime_thermostat',
'cache.data.air_quality_detail__runtime_sensor'
],
@ -79,8 +77,6 @@ beestat.component.card.air_quality_detail.prototype.decorate_contents_ = functio
chart_container.appendChild($.createElement('p').innerText('CO₂ Concentration (ppm)'));
this.charts_.co2_concentration.render(chart_container);
// this.charts_.x_axis.render(chart_container);
// Sync extremes and crosshair.
Object.values(this.charts_).forEach(function(source_chart) {
Object.values(self.charts_).forEach(function(target_chart) {
@ -149,7 +145,7 @@ beestat.component.card.air_quality_detail.prototype.decorate_contents_ = functio
}
var api_call = new beestat.api();
beestat.sensor.get_sorted().forEach(function(sensor) {
Object.values(beestat.cache.sensor).forEach(function(sensor) {
if (sensor.thermostat_id === self.thermostat_id_) {
api_call.add_call(
'runtime_sensor',
@ -347,6 +343,8 @@ beestat.component.card.air_quality_detail.prototype.has_data_ = function() {
* @return {object} The data.
*/
beestat.component.card.air_quality_detail.prototype.get_data_ = function(force) {
const self = this;
if (this.data_ === undefined || force === true) {
var range = {
'type': beestat.setting('air_quality_detail_range_type'),
@ -356,7 +354,10 @@ beestat.component.card.air_quality_detail.prototype.get_data_ = function(force)
};
var sensor_data = beestat.runtime_sensor.get_data(
this.thermostat_id_,
Object.values(beestat.cache.sensor).filter(function(sensor) {
return sensor.thermostat_id === self.thermostat_id_;
})
.map(sensor => sensor.sensor_id),
range,
'air_quality_detail__runtime_sensor'
);

View File

@ -115,7 +115,7 @@ beestat.component.card.air_quality_summary.prototype.decorate_contents_ = functi
}
var api_call = new beestat.api();
beestat.sensor.get_sorted().forEach(function(sensor) {
Object.values(beestat.cache.sensor).forEach(function(sensor) {
if (
sensor.thermostat_id === self.thermostat_id_ &&
sensor.type === 'thermostat'

View File

@ -151,7 +151,7 @@ beestat.component.card.comparison_settings.prototype.decorate_region_ = function
var color = beestat.style.color.green.base;
var button_group = new beestat.component.tile_group();
var tile_group = new beestat.component.tile_group();
regions.forEach(function(region) {
var button = new beestat.component.tile()
.set_background_hover_color(color)
@ -192,9 +192,9 @@ beestat.component.card.comparison_settings.prototype.decorate_region_ = function
});
}
button_group.add_button(button);
tile_group.add_tile(button);
});
button_group.render(parent);
tile_group.render(parent);
};
/**
@ -233,7 +233,7 @@ beestat.component.card.comparison_settings.prototype.decorate_property_ = functi
var color = beestat.style.color.purple.base;
var button_group = new beestat.component.tile_group();
var tile_group = new beestat.component.tile_group();
property_types.forEach(function(property_type) {
var button = new beestat.component.tile()
.set_background_hover_color(color)
@ -274,9 +274,9 @@ beestat.component.card.comparison_settings.prototype.decorate_property_ = functi
});
}
button_group.add_button(button);
tile_group.add_tile(button);
});
button_group.render(parent);
tile_group.render(parent);
};
beestat.component.card.comparison_settings.prototype.decorate_detail_ = function(parent) {

View File

@ -8,7 +8,7 @@ beestat.component.card.floor_plan_editor = function(thermostat_id) {
this.thermostat_id_ = thermostat_id;
var change_function = beestat.debounce(function() {
const change_function = beestat.debounce(function() {
// todo replace these with (if entity set active false?)
delete self.state_.active_group;
@ -21,10 +21,7 @@ beestat.component.card.floor_plan_editor = function(thermostat_id) {
}, 10);
beestat.dispatcher.addEventListener(
[
'setting.floor_plan_id',
'cache.floor_plan'
],
'setting.visualize.floor_plan_id',
change_function
);
@ -71,7 +68,7 @@ beestat.component.card.floor_plan_editor.prototype.decorate_contents_ = function
});
center_container.appendChild(get_started_button);
} else {
const floor_plan = beestat.cache.floor_plan[beestat.setting('floor_plan_id')];
const floor_plan = beestat.cache.floor_plan[beestat.setting('visualize.floor_plan_id')];
// Set group ids if they are not set.
floor_plan.data.groups.forEach(function(group) {
@ -138,7 +135,7 @@ beestat.component.card.floor_plan_editor.prototype.decorate_drawing_pane_ = func
// Create and render a new SVG component.
this.floor_plan_ = new beestat.component.floor_plan(
beestat.setting('floor_plan_id'),
beestat.setting('visualize.floor_plan_id'),
this.state_
);
@ -467,9 +464,13 @@ 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 = beestat.sensor.get_sorted(
thermostat.thermostat_id
);
const thermostat_sensors = Object.values(beestat.cache.sensor).filter(function(sensor) {
return sensor.thermostat_id === self.thermostat_id_;
})
.sort(function(a, b) {
return a.name.localeCompare(b.name, 'en', {'sensitivity': 'base'});
});
sensors[thermostat.thermostat_id] = thermostat_sensors;
});
@ -526,20 +527,6 @@ beestat.component.card.floor_plan_editor.prototype.get_title_ = function() {
return 'Floor Plan';
};
/**
* Get the subtitle of the card.
*
* @return {string} Subtitle
*/
beestat.component.card.floor_plan_editor.prototype.get_subtitle_ = function() {
if (beestat.setting('floor_plan_id') !== null) {
const floor_plan = beestat.cache.floor_plan[beestat.setting('floor_plan_id')];
return floor_plan.name;
}
return null;
};
/**
* Update the floor plan in the database. This is throttled so the update can
* only run so fast.
@ -547,6 +534,9 @@ beestat.component.card.floor_plan_editor.prototype.get_subtitle_ = function() {
beestat.component.card.floor_plan_editor.prototype.update_floor_plan_ = function() {
const self = this;
// Fake this event since the cache is being directly modified.
beestat.dispatcher.dispatchEvent('cache.floor_plan');
window.clearTimeout(this.update_timeout_);
this.update_timeout_ = window.setTimeout(function() {
new beestat.api()
@ -555,8 +545,8 @@ beestat.component.card.floor_plan_editor.prototype.update_floor_plan_ = function
'update',
{
'attributes': {
'floor_plan_id': beestat.setting('floor_plan_id'),
'data': self.get_floor_plan_data_(beestat.setting('floor_plan_id'))
'floor_plan_id': beestat.setting('visualize.floor_plan_id'),
'data': self.get_floor_plan_data_(beestat.setting('visualize.floor_plan_id'))
}
},
'update_floor_plan'
@ -566,22 +556,14 @@ beestat.component.card.floor_plan_editor.prototype.update_floor_plan_ = function
};
/**
* Get floor plan data with UUIDs stripped.
* Get cloned floor plan data.
*
* @param {number} floor_plan_id Floor plan ID
*
* @return {object} The modified floor plan data.
*/
beestat.component.card.floor_plan_editor.prototype.get_floor_plan_data_ = function(floor_plan_id) {
const floor_plan = beestat.cache.floor_plan[floor_plan_id];
const data = beestat.clone(floor_plan.data);
data.groups.forEach(function(group) {
delete group.group_id;
group.rooms.forEach(function(room) {
delete room.room_id;
});
});
return data;
return beestat.clone(beestat.cache.floor_plan[floor_plan_id].data);
};
/**
@ -592,7 +574,7 @@ beestat.component.card.floor_plan_editor.prototype.get_floor_plan_data_ = functi
beestat.component.card.floor_plan_editor.prototype.decorate_top_right_ = function(parent) {
const self = this;
var menu = (new beestat.component.menu()).render(parent);
const menu = (new beestat.component.menu()).render(parent);
if (window.is_demo === false) {
menu.add_menu_item(new beestat.component.menu_item()
@ -604,33 +586,24 @@ beestat.component.card.floor_plan_editor.prototype.decorate_top_right_ = functio
).render();
}));
if (Object.keys(beestat.cache.floor_plan).length > 1) {
menu.add_menu_item(new beestat.component.menu_item()
.set_text('Switch')
.set_icon('swap_horizontal')
.set_callback(function() {
(new beestat.component.modal.change_floor_plan()).render();
}));
}
if (beestat.setting('floor_plan_id') !== null) {
if (beestat.setting('visualize.floor_plan_id') !== null) {
menu.add_menu_item(new beestat.component.menu_item()
.set_text('Edit')
.set_icon('pencil')
.set_callback(function() {
new beestat.component.modal.update_floor_plan(
beestat.setting('floor_plan_id')
beestat.setting('visualize.floor_plan_id')
).render();
}));
}
if (beestat.setting('floor_plan_id') !== null) {
if (beestat.setting('visualize.floor_plan_id') !== null) {
menu.add_menu_item(new beestat.component.menu_item()
.set_text('Delete')
.set_icon('delete')
.set_callback(function() {
new beestat.component.modal.delete_floor_plan(
beestat.setting('floor_plan_id')
beestat.setting('visualize.floor_plan_id')
).render();
}));
}

View File

@ -67,27 +67,27 @@ beestat.component.card.my_home.prototype.decorate_system_type_ = function(parent
);
const cool_stages_string = cool_stages > 1 ? ' (2 Stage)' : '';
var button_group = new beestat.component.tile_group();
button_group.add_button(new beestat.component.tile()
var tile_group = new beestat.component.tile_group();
tile_group.add_tile(new beestat.component.tile()
.set_type('pill')
.set_background_color(beestat.series.compressor_heat_1.color)
.set_text_color('#fff')
.set_icon('fire')
.set_text(heat.charAt(0).toUpperCase() + heat.slice(1) + heat_stages_string));
button_group.add_button(new beestat.component.tile()
tile_group.add_tile(new beestat.component.tile()
.set_type('pill')
.set_background_color(beestat.series.auxiliary_heat_1.color)
.set_text_color('#fff')
.set_icon('fire')
.set_text(auxiliary_heat.charAt(0).toUpperCase() + auxiliary_heat.slice(1)));
button_group.add_button(new beestat.component.tile()
tile_group.add_tile(new beestat.component.tile()
.set_type('pill')
.set_background_color(beestat.series.compressor_cool_1.color)
.set_text_color('#fff')
.set_icon('snowflake')
.set_text(cool.charAt(0).toUpperCase() + cool.slice(1) + cool_stages_string));
button_group.render(parent);
tile_group.render(parent);
};
/**
@ -117,7 +117,7 @@ beestat.component.card.my_home.prototype.decorate_region_ = function(parent) {
region = null;
}
var button_group = new beestat.component.tile_group();
var tile_group = new beestat.component.tile_group();
if (region !== null) {
var button = new beestat.component.tile()
.set_type('pill')
@ -125,16 +125,16 @@ beestat.component.card.my_home.prototype.decorate_region_ = function(parent) {
.set_text_color('#fff')
.set_icon('map_marker')
.set_text(region);
button_group.add_button(button);
tile_group.add_tile(button);
} else {
button_group.add_button(new beestat.component.tile()
tile_group.add_tile(new beestat.component.tile()
.set_type('pill')
.set_background_color(beestat.style.color.gray.dark)
.set_text_color('#fff')
.set_icon('border_none_variant')
.set_text('No Data'));
}
button_group.render(parent);
tile_group.render(parent);
};
/**
@ -147,10 +147,12 @@ beestat.component.card.my_home.prototype.decorate_property_ = function(parent) {
(new beestat.component.title('Property')).render(parent);
var button_group = new beestat.component.tile_group();
var tile_group = new beestat.component.tile_group();
let has_data = false;
if (thermostat.property.structure_type !== null) {
button_group.add_button(new beestat.component.tile()
has_data = true;
tile_group.add_tile(new beestat.component.tile()
.set_type('pill')
.set_background_color(beestat.style.color.purple.base)
.set_text_color('#fff')
@ -167,7 +169,8 @@ beestat.component.card.my_home.prototype.decorate_property_ = function(parent) {
thermostat.property.structure_type === 'semi-detached'
)
) {
button_group.add_button(new beestat.component.tile()
has_data = true;
tile_group.add_tile(new beestat.component.tile()
.set_type('pill')
.set_background_color(beestat.style.color.purple.base)
.set_text_color('#fff')
@ -177,7 +180,8 @@ beestat.component.card.my_home.prototype.decorate_property_ = function(parent) {
}
if (thermostat.property.square_feet !== null) {
button_group.add_button(new beestat.component.tile()
has_data = true;
tile_group.add_tile(new beestat.component.tile()
.set_type('pill')
.set_background_color(beestat.style.color.purple.base)
.set_text_color('#fff')
@ -186,7 +190,8 @@ beestat.component.card.my_home.prototype.decorate_property_ = function(parent) {
}
if (thermostat.property.age !== null) {
button_group.add_button(new beestat.component.tile()
has_data = true;
tile_group.add_tile(new beestat.component.tile()
.set_type('pill')
.set_background_color(beestat.style.color.purple.base)
.set_text_color('#fff')
@ -194,8 +199,8 @@ beestat.component.card.my_home.prototype.decorate_property_ = function(parent) {
.set_text(thermostat.property.age + ' Years'));
}
if (button_group.get_buttons().length === 0) {
button_group.add_button(new beestat.component.tile()
if (has_data === false) {
tile_group.add_tile(new beestat.component.tile()
.set_type('pill')
.set_background_color(beestat.style.color.gray.dark)
.set_text_color('#fff')
@ -203,7 +208,7 @@ beestat.component.card.my_home.prototype.decorate_property_ = function(parent) {
.set_text('No Data'));
}
button_group.render(parent);
tile_group.render(parent);
};
/**

View File

@ -60,6 +60,7 @@ beestat.component.card.patreon.prototype.get_title_ = function() {
beestat.component.card.patreon.prototype.decorate_top_right_ = function(parent) {
new beestat.component.tile()
.set_type('pill')
.set_shadow(false)
.set_icon('close')
.set_text_color('#fff')
.set_background_hover_color(beestat.style.color.green.light)

View File

@ -25,8 +25,6 @@ beestat.component.card.runtime_sensor_detail = function(thermostat_id) {
beestat.dispatcher.addEventListener(
[
'setting.runtime_sensor_detail_range_type',
'setting.runtime_sensor_detail_range_dynamic',
'cache.data.runtime_sensor_detail__runtime_thermostat',
'cache.data.runtime_sensor_detail__runtime_sensor'
],
@ -149,7 +147,7 @@ beestat.component.card.runtime_sensor_detail.prototype.decorate_contents_ = func
}
var api_call = new beestat.api();
beestat.sensor.get_sorted().forEach(function(sensor) {
Object.values(beestat.cache.sensor).forEach(function(sensor) {
if (sensor.thermostat_id === self.thermostat_id_) {
api_call.add_call(
'runtime_sensor',
@ -354,6 +352,8 @@ beestat.component.card.runtime_sensor_detail.prototype.has_data_ = function() {
* @return {object} The data.
*/
beestat.component.card.runtime_sensor_detail.prototype.get_data_ = function(force) {
const self = this;
if (this.data_ === undefined || force === true) {
var range = {
'type': beestat.setting('runtime_sensor_detail_range_type'),
@ -363,7 +363,10 @@ beestat.component.card.runtime_sensor_detail.prototype.get_data_ = function(forc
};
var sensor_data = beestat.runtime_sensor.get_data(
this.thermostat_id_,
Object.values(beestat.cache.sensor).filter(function(sensor) {
return sensor.thermostat_id === self.thermostat_id_;
})
.map(sensor => sensor.sensor_id),
range,
'runtime_sensor_detail__runtime_sensor'
);

View File

@ -25,8 +25,6 @@ beestat.component.card.runtime_thermostat_detail = function(thermostat_id) {
beestat.dispatcher.addEventListener(
[
'setting.runtime_thermostat_detail_range_type',
'setting.runtime_thermostat_detail_range_dynamic',
'cache.data.runtime_thermostat_detail__runtime_thermostat',
'cache.thermostat'
],

View File

@ -509,7 +509,7 @@ beestat.component.card.runtime_thermostat_summary.prototype.decorate_top_right_
if (beestat.thermostat.get_sync_progress(this.thermostat_id_) !== null) {
menu.add_menu_item(new beestat.component.menu_item()
.set_text('Past 3 Months')
.set_icon('calendar_range')
.set_icon('calendar_month')
.set_callback(function() {
if (
beestat.setting('runtime_thermostat_summary_time_count') !== 3 ||
@ -526,7 +526,7 @@ beestat.component.card.runtime_thermostat_summary.prototype.decorate_top_right_
menu.add_menu_item(new beestat.component.menu_item()
.set_text('Past 12 Months')
.set_icon('calendar_range')
.set_icon('calendar_month')
.set_callback(function() {
if (
beestat.setting('runtime_thermostat_summary_time_count') !== 12 ||
@ -543,7 +543,7 @@ beestat.component.card.runtime_thermostat_summary.prototype.decorate_top_right_
menu.add_menu_item(new beestat.component.menu_item()
.set_text('All Time')
.set_icon('calendar_range')
.set_icon('calendar_month')
.set_callback(function() {
if (
beestat.setting('runtime_thermostat_summary_time_count') !== 0 ||

View File

@ -25,13 +25,19 @@ beestat.component.card.sensors.prototype.decorate_contents_ = function(parent) {
var sensors = [];
var internal_sensor;
beestat.sensor.get_sorted().forEach(function(sensor) {
if (sensor.thermostat_id === beestat.setting('thermostat_id')) {
if (sensor.type === 'thermostat') {
internal_sensor = sensor;
} else {
sensors.push(sensor);
}
const thermostat_sensors = Object.values(beestat.cache.sensor).filter(function(sensor) {
return sensor.thermostat_id === beestat.setting('thermostat_id');
})
.sort(function(a, b) {
return a.name.localeCompare(b.name, 'en', {'sensitivity': 'base'});
});
thermostat_sensors.forEach(function(sensor) {
if (sensor.type === 'thermostat') {
internal_sensor = sensor;
} else {
sensors.push(sensor);
}
});

View File

@ -0,0 +1,595 @@
/**
* 3D View
*/
beestat.component.card.three_d = function() {
const self = this;
// Things that update the scene that don't require a rerender.
// TODO these probably need moved to the layer instead of here
beestat.dispatcher.addEventListener(
[
'setting.visualize.data_type',
'setting.visualize.heat_map_type',
'setting.visualize.heat_map_absolute.temperature.min',
'setting.visualize.heat_map_absolute.temperature.max',
'setting.visualize.heat_map_absolute.occupancy.min',
'setting.visualize.heat_map_absolute.occupancy.max'
], self.update_scene_.bind(this));
beestat.dispatcher.addEventListener('cache.floor_plan', function() {
self.scene_.rerender();
});
/*
* When a setting is changed clear all of the data. Then rerender which will
* trigger the loading state. Also do this when the cache changes.
*
* Debounce so that multiple setting changes don't re-trigger the same
* event. This fires on the trailing edge so that all changes are accounted
* for when rerendering.
*/
const change_function = beestat.debounce(function() {
self.get_data_(true);
self.rerender();
}, 10);
beestat.dispatcher.addEventListener(
[
'cache.data.three_d__runtime_sensor'
],
change_function
);
beestat.component.card.apply(this, arguments);
};
beestat.extend(beestat.component.card.three_d, beestat.component.card);
/**
* Decorate
*
* @param {rocket.Elements} parent
*/
beestat.component.card.three_d.prototype.decorate_ = function(parent) {
this.hide_loading_();
this.parent_ = parent;
/*
* Unfortunate but necessary to get the card to fill the height of the flex
* container. Everything leading up to the card has to be 100% height.
*/
parent.style('height', '100%');
this.contents_ = $.createElement('div')
.style({
'height': '100%',
'background': beestat.style.color.bluegray.base,
'border-radius': beestat.style.size.border_radius
});
if (this.box_shadow_ === true) {
this.contents_.style('box-shadow', '0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24)');
}
parent.appendChild(this.contents_);
this.decorate_contents_(this.contents_);
};
/**
* Decorate
*
* @param {rocket.Elements} parent
*/
beestat.component.card.three_d.prototype.decorate_contents_ = function(parent) {
const drawing_pane_container = document.createElement('div');
parent.appendChild(drawing_pane_container);
this.decorate_drawing_pane_(drawing_pane_container);
// Decorate everything.
const controls_container = document.createElement('div');
Object.assign(controls_container.style, {
'position': 'absolute',
// 'margin': 'auto',
'top': `${beestat.style.size.gutter}px`,
'left': '50%',
'width': '300px',
'margin-left': '-150px',
'background': beestat.style.color.bluegray.base,
'padding': `${beestat.style.size.gutter / 2}px`,
'border-radius': `${beestat.style.size.border_radius}px`
});
parent.appendChild(controls_container);
this.decorate_controls_(controls_container);
// var thermostat = beestat.cache.thermostat[this.thermostat_id_];
let required_begin;
let required_end;
if (beestat.setting('visualize.range_type') === 'dynamic') {
required_begin = moment()
.subtract(
beestat.setting('visualize.range_dynamic'),
'day'
)
.hour(0)
.minute(0)
.second(0);
required_end = required_begin
.clone()
.hour(23)
.minute(59)
.second(59);
} else {
required_begin = moment(
beestat.setting('visualize.range_static_begin') + ' 00:00:00'
);
required_end = moment(
beestat.setting('visualize.range_static_end') + ' 23:59:59'
);
}
// Don't go before there's data.
/* required_begin = moment.max(
required_begin,
moment.utc(thermostat.data_begin)
);*/
// Don't go after now.
/* required_end = moment.min(
required_end,
moment().subtract(1, 'hour')
);*/
/**
* If the needed data exists in the database and the runtime_sensor
* cache is empty, then query the data. If the needed data does not exist in
* the database, check every 2 seconds until it does.
*/
// TODO somewhat problematic because I need to check if data is synced from multiple thermostats now
// if (beestat.thermostat.data_synced(this.thermostat_id_, required_begin, required_end) === true) {
const sensor_ids = Object.keys(this.get_sensor_ids_map_());
if (sensor_ids.length > 0) {
if (true) {
if (beestat.cache.data.three_d__runtime_sensor === undefined) {
// console.log('data is undefined need to load it');
this.show_loading_('Fetching');
var value;
var operator;
// var value = [
// required_begin.format(),
// required_end.format()
// ];
// var operator = 'between';
if (beestat.setting('visualize.range_type') === 'dynamic') {
value = required_begin.format();
operator = '>=';
} else {
value = [
required_begin.format(),
required_end.format()
];
operator = 'between';
}
const sensor_ids = Object.keys(this.get_sensor_ids_map_());
// if (sensor_ids.length > 0) {
const api_call = new beestat.api();
sensor_ids.forEach(function(sensor_id) {
api_call.add_call(
'runtime_sensor',
'read',
{
'attributes': {
'sensor_id': sensor_id,
'timestamp': {
'value': value,
'operator': operator
}
}
},
'runtime_sensor_' + sensor_id
);
});
api_call.set_callback(function(response) {
var runtime_sensors = [];
for (var alias in response) {
var r = response[alias];
runtime_sensors = runtime_sensors.concat(r);
}
beestat.cache.set('data.three_d__runtime_sensor', runtime_sensors);
});
api_call.send();
// }
} else if (this.has_data_() === false) {
console.info('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');
window.setTimeout(function() {
new beestat.api()
.add_call(
'thermostat',
'read_id',
{
'attributes': {
'inactive': 0
}
},
'thermostat'
)
.set_callback(function(response) {
beestat.cache.set('thermostat', response);
self.rerender();
})
.send();
}, 2000);
}
}
};
/**
* Decorate the drawing pane.
*
* @param {HTMLDivElement} parent
*/
beestat.component.card.three_d.prototype.decorate_drawing_pane_ = function(parent) {
const self = this;
// Create the scene
this.scene_ = new beestat.component.scene(
beestat.setting('visualize.floor_plan_id'),
this.get_data_()
);
// Set the initial date.
if (this.has_data_() === true) {
this.update_scene_();
this.scene_.render($(parent));
if (beestat.setting('visualize.range_type') === 'dynamic') {
this.date_m_ = moment()
.subtract(
beestat.setting('visualize.range_dynamic'),
'day'
)
.hour(0)
.minute(0)
.second(0);
} else {
this.date_m_ = moment(
beestat.setting('visualize.range_static_begin') + ' 00:00:00'
);
}
this.scene_.set_date(this.date_m_);
} else {
this.scene_.render($(parent));
}
// Manage width of the scene.
setTimeout(function() {
if (parent.getBoundingClientRect().width > 0) {
self.scene_.set_width(parent.getBoundingClientRect().width);
}
}, 0);
beestat.dispatcher.removeEventListener('resize.three_d');
beestat.dispatcher.addEventListener('resize.three_d', function() {
self.scene_.set_width(parent.getBoundingClientRect().width);
});
};
/**
* Decorate the playback controls.
*
* @param {HTMLDivElement} parent
*/
beestat.component.card.three_d.prototype.decorate_controls_ = function(parent) {
const self = this;
window.clearInterval(self.interval_);
// Hoisting
const range = new beestat.component.input.range();
const right_container = document.createElement('div');
const container = document.createElement('div');
Object.assign(container.style, {
'display': 'flex',
'align-items': 'center'
});
parent.appendChild(container);
const left_container = document.createElement('div');
container.appendChild(left_container);
const play_tile = new beestat.component.tile()
.set_icon('play')
.set_shadow(false)
.set_text_hover_color(beestat.style.color.green.base)
.render($(left_container));
play_tile.addEventListener('click', function() {
if (self.interval_ === undefined) {
play_tile
.set_icon('pause')
.set_text_hover_color(beestat.style.color.red.base);
self.interval_ = window.setInterval(function() {
self.date_m_.add(5, 'minutes');
self.scene_.set_date(self.date_m_);
range.set_value(
((self.date_m_.hours() * 60) + self.date_m_.minutes()) / 1440 * 288
);
right_container.innerText = self.date_m_.format('h:mm a');
}, 100);
} else {
play_tile
.set_icon('play')
.set_text_hover_color(beestat.style.color.green.base);
window.clearInterval(self.interval_);
delete self.interval_;
}
});
const center_container = document.createElement('div');
Object.assign(center_container.style, {
'flex-grow': '1'
});
container.appendChild(center_container);
range
.set_min(0)
.set_max(287)
.set_value(0)
.render($(center_container));
right_container.innerText = '12:00 am';
Object.assign(right_container.style, {
'width': '70px',
'text-align': 'right'
});
container.appendChild(right_container);
range.addEventListener('input', function() {
play_tile
.set_icon('play')
.set_text_hover_color(beestat.style.color.green.base);
window.clearInterval(self.interval_);
delete self.interval_;
const minute_of_day = range.get_value() * 5;
self.date_m_.hours(Math.floor(minute_of_day / 60));
self.date_m_.minutes(Math.floor(minute_of_day % 60));
right_container.innerText = self.date_m_.format('h:mm a');
self.scene_.set_date(self.date_m_);
});
};
/**
* 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.three_d.prototype.get_data_ = function(force) {
const self = this;
if (this.data_ === undefined || force === true) {
const sensor_ids_map = this.get_sensor_ids_map_();
this.data_ = {
'metadata': {
'series': {
'temperature': {
'min': Infinity,
'max': -Infinity
},
'occupancy': {
'min': Infinity,
'max': -Infinity
}
}
},
'series': {
'temperature': {},
'occupancy': {}
}
};
if (beestat.cache.data.three_d__runtime_sensor !== undefined) {
// Add to data
beestat.cache.data.three_d__runtime_sensor.forEach(function(runtime_sensor) {
if (
sensor_ids_map[runtime_sensor.sensor_id] !== undefined &&
runtime_sensor.temperature !== null &&
runtime_sensor.occupancy !== null
) {
const timestamp_m = moment(runtime_sensor.timestamp);
const time = timestamp_m.format('HH:mm');
// Temperature
if (self.data_.series.temperature[runtime_sensor.sensor_id] === undefined) {
self.data_.series.temperature[runtime_sensor.sensor_id] = {};
}
if (self.data_.series.temperature[runtime_sensor.sensor_id][time] === undefined) {
self.data_.series.temperature[runtime_sensor.sensor_id][time] = [];
}
self.data_.series.temperature[runtime_sensor.sensor_id][time].push(runtime_sensor.temperature);
// Occupancy
if (self.data_.series.occupancy[runtime_sensor.sensor_id] === undefined) {
self.data_.series.occupancy[runtime_sensor.sensor_id] = {};
}
if (self.data_.series.occupancy[runtime_sensor.sensor_id][time] === undefined) {
self.data_.series.occupancy[runtime_sensor.sensor_id][time] = [];
}
self.data_.series.occupancy[runtime_sensor.sensor_id][time].push(runtime_sensor.occupancy === true ? 1 : 0);
}
});
// Average data
for (let key in this.data_.series) {
for (let sensor_id in this.data_.series[key]) {
for (let time in this.data_.series[key][sensor_id]) {
this.data_.series[key][sensor_id][time] = this.data_.series[key][sensor_id][time].reduce(function(a, b) {
return a + b;
}) / this.data_.series[key][sensor_id][time].length;
// Set min/max
this.data_.metadata.series[key].min = Math.min(
this.data_.series[key][sensor_id][time],
this.data_.metadata.series[key].min
);
this.data_.metadata.series[key].max = Math.max(
this.data_.series[key][sensor_id][time],
this.data_.metadata.series[key].max
);
}
}
}
}
}
// console.log(this.data_);
return this.data_;
};
/**
* 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.three_d.prototype.has_data_ = function() {
const data = this.get_data_();
for (let key in data.series) {
for (let sensor_id in data.series[key]) {
if (Object.keys(data.series[key][sensor_id]).length > 0) {
return true;
}
}
}
return false;
};
/**
* Get the title of the card.
*
* @return {string} The title.
*/
beestat.component.card.three_d.prototype.get_title_ = function() {
return '3D View';
};
/**
* Decorate the menu.
*
* @param {rocket.Elements} parent
*/
beestat.component.card.three_d.prototype.decorate_top_right_ = function(parent) {
const menu = (new beestat.component.menu()).render(parent);
menu.add_menu_item(new beestat.component.menu_item()
.set_text('Help')
.set_icon('help_circle')
.set_callback(function() {
// TODO
// window.open('https://doc.beestat.io/???');
}));
};
/**
* Update the scene with current settings. Anything that doesn't require
* re-rendering can go here.
*/
beestat.component.card.three_d.prototype.update_scene_ = function() {
this.scene_.set_data_type(beestat.setting('visualize.data_type'));
switch (beestat.setting('visualize.heat_map_type')) {
case 'relative':
this.scene_.set_heat_map_min(
this.data_.metadata.series[beestat.setting('visualize.data_type')].min
);
this.scene_.set_heat_map_max(
this.data_.metadata.series[beestat.setting('visualize.data_type')].max
);
break;
case 'absolute':
this.scene_.set_heat_map_min(
beestat.setting(
'visualize.heat_map_absolute.' +
beestat.setting('visualize.data_type') +
'.min'
) / (beestat.setting('visualize.data_type') === 'occupancy' ? 100 : 1)
);
this.scene_.set_heat_map_max(
beestat.setting(
'visualize.heat_map_absolute.' +
beestat.setting('visualize.data_type') +
'.max'
) / (beestat.setting('visualize.data_type') === 'occupancy' ? 100 : 1)
);
break;
}
};
/**
* Set the floor_plan_id.
*
* @param {number} floor_plan_id
*
* @return {beestat.component.card.three_d}
*/
beestat.component.card.three_d.prototype.set_floor_plan_id = function(floor_plan_id) {
this.floor_plan_id_ = floor_plan_id;
if (this.rendered_ === true) {
this.rerender();
}
return this;
};
/**
* Get an object of all the sensor_ids included in the current floor plan. Key
* is sensor_id, value is true.
*
* @return {object}
*/
beestat.component.card.three_d.prototype.get_sensor_ids_map_ = function() {
const floor_plan = beestat.cache.floor_plan[this.floor_plan_id_];
const sensor_ids_map = [];
floor_plan.data.groups.forEach(function(group) {
group.rooms.forEach(function(room) {
if (room.sensor_id !== undefined) {
sensor_ids_map[room.sensor_id] = true;
}
});
});
return sensor_ids_map;
};

View File

@ -1,42 +0,0 @@
/**
* Visualize
*
* @param {number} thermostat_id The thermostat_id this card is displaying
* data for.
*/
beestat.component.card.visualize = function(thermostat_id) {
this.thermostat_id_ = thermostat_id;
beestat.component.card.apply(this, arguments);
};
beestat.extend(beestat.component.card.visualize, beestat.component.card);
beestat.component.card.visualize.prototype.decorate_contents_ = function(parent) {
const scene = new beestat.component.scene(this.thermostat_id_);
scene.render(parent);
let date = new Date('2022-06-27 11:30:00');
scene.set_date(date);
// let date = new Date('2024-04-08 08:00:00'); // Total solar eclipse
// scene.set_date(date);
// setInterval(function() {
// date = new Date(date.getTime() + (60000 * 1));
// scene.set_date(date);
// }, 100);
//
beestat.dispatcher.addEventListener('cache.floor_plan', function() {
scene.rerender();
// todo get scene to remember date, camera position, etc on rerender
scene.set_date(date);
});
};
/**
* Get the title of the card.
*
* @return {string} The title.
*/
beestat.component.card.visualize.prototype.get_title_ = function() {
return 'Visualize';
};

View File

@ -0,0 +1,359 @@
/**
* Visualize settings.
*/
beestat.component.card.visualize_settings = function() {
const self = this;
beestat.dispatcher.addEventListener('cache.floor_plan', function() {
self.rerender();
});
beestat.component.card.apply(this, arguments);
};
beestat.extend(beestat.component.card.visualize_settings, beestat.component.card);
/**
* Decorate
*
* @param {rocket.Elements} parent
*/
beestat.component.card.visualize_settings.prototype.decorate_contents_ = function(parent) {
const grid_1 = document.createElement('div');
Object.assign(grid_1.style, {
'display': 'grid',
'grid-template-columns': 'repeat(auto-fit, minmax(min(350px, 100%), 1fr))',
'grid-gap': `${beestat.style.size.gutter}px`,
'margin-bottom': `${beestat.style.size.gutter}px`
});
parent.appendChild(grid_1);
const type_container = document.createElement('div');
this.decorate_data_type_(type_container);
grid_1.appendChild(type_container);
const time_period_container = document.createElement('div');
this.decorate_time_period_(time_period_container);
grid_1.appendChild(time_period_container);
const grid_2 = document.createElement('div');
Object.assign(grid_2.style, {
'display': 'grid',
'grid-template-columns': 'repeat(auto-fit, minmax(min(350px, 100%), 1fr))',
'grid-gap': `${beestat.style.size.gutter}px`
});
parent.appendChild(grid_2);
const heat_map_type_container = document.createElement('div');
this.decorate_heat_map_type_(heat_map_type_container);
grid_2.appendChild(heat_map_type_container);
const floor_plan_container = document.createElement('div');
this.decorate_floor_plan_(floor_plan_container);
grid_2.appendChild(floor_plan_container);
};
/**
* Decorate the type options.
*
* @param {HTMLDivElement} parent
*/
beestat.component.card.visualize_settings.prototype.decorate_data_type_ = function(parent) {
const self = this;
(new beestat.component.title('Data Type')).render($(parent));
const types = [
{
'code': 'temperature',
'name': 'Temperature',
'icon': 'thermometer'
},
{
'code': 'occupancy',
'name': 'Occupancy',
'icon': 'eye'
}
];
const color = beestat.style.color.green.base;
const tile_group = new beestat.component.tile_group();
types.forEach(function(type) {
const tile = new beestat.component.tile()
.set_background_hover_color(color)
.set_text_color('#fff')
.set_icon(type.icon)
.set_text(type.name);
if (beestat.setting('visualize.data_type') === type.code) {
tile.set_background_color(color);
} else {
tile
.set_background_color(beestat.style.color.bluegray.light)
.addEventListener('click', function() {
beestat.setting('visualize.data_type', type.code);
self.rerender();
});
}
tile_group.add_tile(tile);
});
tile_group.render($(parent));
};
/**
* Decorate the type options.
*
* @param {HTMLDivElement} parent
*/
beestat.component.card.visualize_settings.prototype.decorate_heat_map_type_ = function(parent) {
const self = this;
(new beestat.component.title('Heat Map Type')).render($(parent));
const types = [
{
'code': 'relative',
'name': 'Relative',
'icon': 'arrow_expand_horizontal'
},
{
'code': 'absolute',
'name': 'Absolute',
'icon': 'arrow_horizontal_lock'
}
];
const color = beestat.style.color.orange.base;
const tile_group = new beestat.component.tile_group();
types.forEach(function(type) {
const tile = new beestat.component.tile()
.set_background_hover_color(color)
.set_text_color('#fff')
.set_icon(type.icon)
.set_text(type.name);
if (beestat.setting('visualize.heat_map_type') === type.code) {
tile.set_background_color(color);
} else {
tile
.set_background_color(beestat.style.color.bluegray.light)
.addEventListener('click', function() {
beestat.setting('visualize.heat_map_type', type.code);
self.rerender();
});
}
tile_group.add_tile(tile);
});
tile_group.render($(parent));
if (beestat.setting('visualize.heat_map_type') === 'absolute') {
const min_max_container = document.createElement('div');
min_max_container.style.marginTop = `${beestat.style.size.gutter}px`;
parent.appendChild(min_max_container);
const min = new beestat.component.input.text()
.set_value(beestat.setting(
'visualize.heat_map_absolute.' + beestat.setting('visualize.data_type') + '.min')
)
.set_width(50);
min.addEventListener('change', function() {
beestat.setting('visualize.heat_map_absolute.' + beestat.setting('visualize.data_type') + '.min', min.get_value());
});
const max = new beestat.component.input.text()
.set_value(beestat.setting(
'visualize.heat_map_absolute.' + beestat.setting('visualize.data_type') + '.max')
)
.set_width(50);
max.addEventListener('change', function() {
beestat.setting('visualize.heat_map_absolute.' + beestat.setting('visualize.data_type') + '.max', max.get_value());
});
let span;
span = document.createElement('span');
span.style.display = 'inline-block';
min.render($(span));
parent.appendChild(span);
span = document.createElement('span');
span.innerText = 'to';
Object.assign(span.style, {
'display': 'inline-block',
'margin-left': `${beestat.style.size.gutter}px`,
'margin-right': `${beestat.style.size.gutter}px`
});
parent.appendChild(span);
span = document.createElement('span');
span.style.display = 'inline-block';
max.render($(span));
parent.appendChild(span);
span = document.createElement('span');
switch (beestat.setting('visualize.data_type')) {
case 'temperature':
span.innerText = beestat.setting('temperature_unit');
break;
case 'occupancy':
span.innerText = '%';
break;
}
Object.assign(span.style, {
'display': 'inline-block',
'margin-left': `${beestat.style.size.gutter}px`
});
parent.appendChild(span);
}
};
/**
* Decorate the type options.
*
* @param {HTMLDivElement} parent
*/
beestat.component.card.visualize_settings.prototype.decorate_time_period_ = function(parent) {
const self = this;
(new beestat.component.title('Time Period')).render($(parent));
const color = beestat.style.color.purple.base;
const tile_group = new beestat.component.tile_group();
// Current Day
const day_tile = new beestat.component.tile()
.set_background_hover_color(color)
.set_text_color('#fff')
.set_icon('calendar')
.set_text('Today');
if (
beestat.setting('visualize.range_type') === 'dynamic' &&
beestat.setting('visualize.range_dynamic') === 0
) {
day_tile.set_background_color(color);
} else {
day_tile
.set_background_color(beestat.style.color.bluegray.light)
.addEventListener('click', function() {
beestat.cache.delete('data.three_d__runtime_sensor');
beestat.setting('visualize.range_type', 'dynamic');
beestat.setting('visualize.range_dynamic', 0);
self.rerender();
});
}
tile_group.add_tile(day_tile);
// Current Week
const week_tile = new beestat.component.tile()
.set_background_hover_color(color)
.set_text_color('#fff')
.set_icon('calendar_week')
.set_text('Last 7 Days');
if (
beestat.setting('visualize.range_type') === 'dynamic' &&
beestat.setting('visualize.range_dynamic') === 7
) {
week_tile.set_background_color(color);
} else {
week_tile
.set_background_color(beestat.style.color.bluegray.light)
.addEventListener('click', function() {
beestat.cache.delete('data.three_d__runtime_sensor');
beestat.setting('visualize.range_type', 'dynamic');
beestat.setting('visualize.range_dynamic', 7);
self.rerender();
});
}
tile_group.add_tile(week_tile);
// Custom
/* const custom_tile = new beestat.component.tile()
.set_background_hover_color(color)
.set_text_color('#fff')
.set_icon('calendar_edit')
.set_text('Custom');
if (
beestat.setting('visualize.range_type') === 'static'
) {
custom_tile.set_background_color(color);
} else {
custom_tile
.set_background_color(beestat.style.color.bluegray.light)
.addEventListener('click', function() {
// TODO MODAL
beestat.setting('visualize.range_type', 'static');
self.rerender();
});
}
tile_group.add_tile(custom_tile);*/
tile_group.render($(parent));
};
/**
* Decorate the floor plan options.
*
* @param {HTMLDivElement} parent
*/
beestat.component.card.visualize_settings.prototype.decorate_floor_plan_ = function(parent) {
const self = this;
(new beestat.component.title('Floor Plan')).render($(parent));
var sorted_floor_plans = $.values(beestat.cache.floor_plan)
.sort(function(a, b) {
return a.name > b.name;
});
const tile_group = new beestat.component.tile_group();
sorted_floor_plans.forEach(function(floor_plan) {
const tile = new beestat.component.tile.floor_plan(floor_plan.floor_plan_id)
.set_text_color('#fff')
.set_display('block');
if (floor_plan.floor_plan_id === beestat.setting('visualize.floor_plan_id')) {
tile.set_background_color(beestat.style.color.lightblue.base);
} else {
tile
.set_background_color(beestat.style.color.bluegray.light)
.set_background_hover_color(beestat.style.color.lightblue.base)
.addEventListener('click', function() {
beestat.setting('visualize.floor_plan_id', floor_plan.floor_plan_id);
self.rerender();
});
}
tile_group.add_tile(tile);
});
tile_group.render($(parent));
};
/**
* Get the title of the card.
*
* @return {string} The title of the card.
*/
beestat.component.card.visualize_settings.prototype.get_title_ = function() {
return 'Visualize Settings';
};
/**
* Decorate the menu.
*
* @param {rocket.Elements} parent
*/
beestat.component.card.visualize_settings.prototype.decorate_top_right_ = function(parent) {
var menu = (new beestat.component.menu()).render(parent);
menu.add_menu_item(new beestat.component.menu_item()
.set_text('Help')
.set_icon('help_circle')
.set_callback(function() {
// TODO
// window.open('https://doc.beestat.io/596040eadd014928830b4d1d54692761');
}));
};

View File

@ -404,24 +404,25 @@ beestat.component.floor_plan.prototype.dispose = function() {
beestat.component.floor_plan.prototype.update_toolbar = function() {
const self = this;
if (this.button_group_ !== undefined) {
this.button_group_.dispose();
if (this.tile_group_ !== undefined) {
this.tile_group_.dispose();
}
if (this.button_group_floors_ !== undefined) {
this.button_group_floors_.dispose();
if (this.tile_group_floors_ !== undefined) {
this.tile_group_floors_.dispose();
}
this.button_group_ = new beestat.component.tile_group();
this.tile_group_ = new beestat.component.tile_group();
// Add floor
this.button_group_.add_button(new beestat.component.tile()
this.tile_group_.add_tile(new beestat.component.tile()
.set_icon('layers')
.set_shadow(false)
.set_text_color(beestat.style.color.lightblue.base)
);
// Add room
this.button_group_.add_button(new beestat.component.tile()
this.tile_group_.add_tile(new beestat.component.tile()
.set_icon('card_plus_outline')
.set_title('Add Room [R]')
.set_text_color(beestat.style.color.gray.light)
@ -437,7 +438,7 @@ beestat.component.floor_plan.prototype.update_toolbar = function() {
.set_icon('card_remove_outline')
.set_title('Remove Room [Delete]')
.set_background_color(beestat.style.color.bluegray.base);
this.button_group_.add_button(remove_room_button);
this.tile_group_.add_tile(remove_room_button);
if (this.state_.active_room_entity !== undefined) {
remove_room_button
@ -454,7 +455,7 @@ beestat.component.floor_plan.prototype.update_toolbar = function() {
.set_icon('vector_square_plus')
.set_title('Add Point [Double click]')
.set_background_color(beestat.style.color.bluegray.base);
this.button_group_.add_button(add_point_button);
this.tile_group_.add_tile(add_point_button);
if (this.state_.active_wall_entity !== undefined) {
add_point_button
@ -471,7 +472,7 @@ beestat.component.floor_plan.prototype.update_toolbar = function() {
.set_background_color(beestat.style.color.bluegray.base)
.set_title('Remove Point [Delete]')
.set_icon('vector_square_remove');
this.button_group_.add_button(remove_point_button);
this.tile_group_.add_tile(remove_point_button);
if (
this.state_.active_point_entity !== undefined &&
@ -497,7 +498,7 @@ beestat.component.floor_plan.prototype.update_toolbar = function() {
snapping_title = 'Enable Snapping [S]';
}
this.button_group_.add_button(new beestat.component.tile()
this.tile_group_.add_tile(new beestat.component.tile()
.set_icon(snapping_icon)
.set_title(snapping_title)
.set_text_color(beestat.style.color.gray.light)
@ -511,7 +512,7 @@ beestat.component.floor_plan.prototype.update_toolbar = function() {
.set_icon('undo')
.set_title('Undo [Ctrl+Z]')
.set_background_color(beestat.style.color.bluegray.base);
this.button_group_.add_button(undo_button);
this.tile_group_.add_tile(undo_button);
if (
this.can_undo_() === true
@ -532,7 +533,7 @@ beestat.component.floor_plan.prototype.update_toolbar = function() {
.set_icon('redo')
.set_title('redo [Ctrl+Y]')
.set_background_color(beestat.style.color.bluegray.base);
this.button_group_.add_button(redo_button);
this.tile_group_.add_tile(redo_button);
if (
this.can_redo_() === true
@ -553,7 +554,7 @@ beestat.component.floor_plan.prototype.update_toolbar = function() {
.set_icon('magnify_plus_outline')
.set_title('Zoom In')
.set_background_color(beestat.style.color.bluegray.base);
this.button_group_.add_button(zoom_in_button);
this.tile_group_.add_tile(zoom_in_button);
if (
this.can_zoom_in_() === true
@ -574,7 +575,7 @@ beestat.component.floor_plan.prototype.update_toolbar = function() {
.set_icon('magnify_minus_outline')
.set_title('Zoom out')
.set_background_color(beestat.style.color.bluegray.base);
this.button_group_.add_button(zoom_out_button);
this.tile_group_.add_tile(zoom_out_button);
if (
this.can_zoom_out_() === true
@ -591,10 +592,10 @@ beestat.component.floor_plan.prototype.update_toolbar = function() {
}
// Render
this.button_group_.render(this.toolbar_container_);
this.tile_group_.render(this.toolbar_container_);
// FLOORS
this.button_group_floors_ = new beestat.component.tile_group();
this.tile_group_floors_ = new beestat.component.tile_group();
const floor_plan = beestat.cache.floor_plan[this.floor_plan_id_];
@ -607,6 +608,7 @@ beestat.component.floor_plan.prototype.update_toolbar = function() {
sorted_groups.forEach(function(group) {
const button = new beestat.component.tile()
.set_title(group.name)
.set_shadow(false)
.set_text_hover_color(beestat.style.color.lightblue.light)
.set_text_color(beestat.style.color.lightblue.base);
@ -639,10 +641,10 @@ beestat.component.floor_plan.prototype.update_toolbar = function() {
});
}
self.button_group_floors_.add_button(button);
self.tile_group_floors_.add_tile(button);
});
this.button_group_floors_.render(this.floors_container_);
this.tile_group_floors_.render(this.floors_container_);
};
/**
@ -952,30 +954,33 @@ beestat.component.floor_plan.prototype.get_group_below = function(group) {
* Center the view box on the content. Sets zoom and pan.
*/
beestat.component.floor_plan.prototype.center_content = function() {
const floor_plan = beestat.cache.floor_plan[this.floor_plan_id_];
// const floor_plan = beestat.cache.floor_plan[this.floor_plan_id_];
let min_x = Infinity;
let max_x = -Infinity;
let min_y = Infinity;
let max_y = -Infinity;
// let min_x = Infinity;
// let max_x = -Infinity;
// let min_y = Infinity;
// let max_y = -Infinity;
let has_content = false;
floor_plan.data.groups.forEach(function(group) {
group.rooms.forEach(function(room) {
room.points.forEach(function(point) {
has_content = true;
min_x = Math.min(room.x + point.x, min_x);
max_x = Math.max(room.x + point.x, max_x);
min_y = Math.min(room.y + point.y, min_y);
max_y = Math.max(room.y + point.y, max_y);
});
});
});
// let has_content = false;
// floor_plan.data.groups.forEach(function(group) {
// group.rooms.forEach(function(room) {
// room.points.forEach(function(point) {
// has_content = true;
// min_x = Math.min(room.x + point.x, min_x);
// max_x = Math.max(room.x + point.x, max_x);
// min_y = Math.min(room.y + point.y, min_y);
// max_y = Math.max(room.y + point.y, max_y);
// });
// });
// });
const bounding_box = beestat.floor_plan.get_bounding_box(this.floor_plan_id_);
this.reset_view_box_();
if (has_content === true) {
const width = (max_x - min_x) + 50;
const height = (max_y - min_y) + 50;
// TODO ADD THIS BACK IN
// if (has_content === true) {
const width = (bounding_box.width) + 50;
const height = (bounding_box.height) + 50;
while (
(
this.view_box_.width < width ||
@ -986,14 +991,14 @@ beestat.component.floor_plan.prototype.center_content = function() {
this.zoom_out_();
}
const center_x = (max_x + min_x) / 2;
const center_y = (max_y + min_y) / 2;
const center_x = (bounding_box.right + bounding_box.left) / 2;
const center_y = (bounding_box.bottom + bounding_box.top) / 2;
this.view_box_.x = center_x - (this.view_box_.width / 2);
this.view_box_.y = center_y - (this.view_box_.height / 2);
this.update_view_box_();
}
// }
};
/**
@ -1019,7 +1024,7 @@ beestat.component.floor_plan.prototype.save_buffer = function(clear = true) {
}
this.state_.buffer.push({
'floor_plan': beestat.clone(beestat.cache.floor_plan[beestat.setting('floor_plan_id')]),
'floor_plan': beestat.clone(beestat.cache.floor_plan[beestat.setting('visualize.floor_plan_id')]),
'active_room_entity': this.state_.active_room_entity,
'active_group_id': this.state_.active_group.group_id
});

View File

@ -90,10 +90,11 @@ beestat.component.header.prototype.decorate_ = function(parent) {
});
row.appendChild(column_navigation);
var button_group = new beestat.component.tile_group();
var tile_group = new beestat.component.tile_group();
pages.forEach(function(page) {
var button = new beestat.component.tile()
.set_icon(page.icon)
.set_shadow(false)
.set_text_color(beestat.style.color.bluegray.dark);
if (beestat.width > 800) {
@ -115,10 +116,10 @@ beestat.component.header.prototype.decorate_ = function(parent) {
});
}
button_group.add_button(button);
tile_group.add_tile(button);
});
button_group.render(column_navigation);
tile_group.render(column_navigation);
// Menu

View File

@ -0,0 +1,81 @@
/**
* Range input.
*/
beestat.component.input.range = function() {
const self = this;
this.input_ = document.createElement('input');
this.input_.setAttribute('type', 'range');
this.input_.addEventListener('change', function() {
self.dispatchEvent('change');
});
this.input_.addEventListener('input', function() {
self.dispatchEvent('input');
});
beestat.component.input.apply(this, arguments);
};
beestat.extend(beestat.component.input.range, beestat.component.input);
/**
* Decorate
*
* @param {rocket.Elements} parent
*/
beestat.component.input.range.prototype.decorate_ = function(parent) {
this.input_.style.width = '100%';
parent.appendChild(this.input_);
};
/**
* Set the value in the range field. Do not rerender; it's unnecessary.
*
* @param {string} value
*
* @return {beestat.component.input.range} This.
*/
beestat.component.input.range.prototype.set_value = function(value) {
this.input_.value = value;
this.dispatchEvent('change');
return this;
};
/**
* Get the value of the input.
*
* @return {string}
*/
beestat.component.input.range.prototype.get_value = function() {
return this.input_.value;
};
/**
* Set the min value of the range input.
*
* @param {string} min
*
* @return {beestat.component.input.range} This.
*/
beestat.component.input.range.prototype.set_min = function(min) {
this.input_.setAttribute('min', min);
return this;
};
/**
* Set the max value of the range input.
*
* @param {string} max
*
* @return {beestat.component.input.range} This.
*/
beestat.component.input.range.prototype.set_max = function(max) {
this.input_.setAttribute('max', max);
return this;
};

View File

@ -13,6 +13,7 @@ beestat.component.menu.prototype.decorate_ = function(parent) {
this.icon_ = new beestat.component.tile()
.set_type('pill')
.set_shadow(false)
.set_icon('dots_vertical')
.set_bubble_text(this.bubble_text_)
.set_bubble_color(this.bubble_color_)

View File

@ -208,6 +208,7 @@ beestat.component.modal.prototype.decorate_close_ = function(parent) {
var close = new beestat.component.tile()
.set_type('pill')
.set_shadow(false)
.set_icon('close')
.set_text_color(beestat.style.color.gray.dark)
.set_background_hover_color(beestat.style.color.gray.light)
@ -248,11 +249,11 @@ beestat.component.modal.prototype.decorate_buttons_ = function(parent) {
});
parent.appendChild(container);
var button_group = new beestat.component.tile_group();
var tile_group = new beestat.component.tile_group();
buttons.forEach(function(button) {
button_group.add_button(button);
tile_group.add_tile(button);
});
button_group.render(container);
tile_group.render(container);
}
};

View File

@ -43,9 +43,9 @@ beestat.component.modal.air_quality_detail_custom.prototype.decorate_contents_ =
beestat.component.modal.air_quality_detail_custom.prototype.decorate_range_type_ = function(parent) {
var self = this;
var button_group = new beestat.component.tile_group();
var tile_group = new beestat.component.tile_group();
button_group.add_button(new beestat.component.tile()
tile_group.add_tile(new beestat.component.tile()
.set_background_hover_color(beestat.style.color.lightblue.base)
.set_text_color('#fff')
.set_background_color(
@ -59,7 +59,7 @@ beestat.component.modal.air_quality_detail_custom.prototype.decorate_range_type_
self.rerender();
}));
button_group.add_button(new beestat.component.tile()
tile_group.add_tile(new beestat.component.tile()
.set_background_hover_color(beestat.style.color.lightblue.base)
.set_text_color('#fff')
.set_background_color(
@ -78,7 +78,7 @@ beestat.component.modal.air_quality_detail_custom.prototype.decorate_range_type_
parent.appendChild(row);
var column = $.createElement('div').addClass(['column column_12']);
row.appendChild(column);
button_group.render(column);
tile_group.render(column);
};
/**
@ -298,6 +298,7 @@ beestat.component.modal.air_quality_detail_custom.prototype.get_buttons_ = funct
.set_background_color('#fff')
.set_text_color(beestat.style.color.gray.base)
.set_text_hover_color(beestat.style.color.red.base)
.set_shadow(false)
.set_text('Cancel')
.addEventListener('click', function() {
self.dispose();
@ -332,8 +333,8 @@ beestat.component.modal.air_quality_detail_custom.prototype.get_buttons_ = funct
self.state_.air_quality_detail_range_static_end = temp;
}
beestat.cache.delete('runtime_thermostat');
beestat.cache.delete('runtime_sensor');
beestat.cache.delete('data.air_quality_detail__runtime_thermostat');
beestat.cache.delete('data.air_quality_detail__runtime_sensor');
beestat.setting(
{
'air_quality_detail_range_type': self.state_.air_quality_detail_range_type,

View File

@ -1,66 +0,0 @@
/**
* Change floor plan
*/
beestat.component.modal.change_floor_plan = function() {
beestat.component.modal.apply(this, arguments);
};
beestat.extend(beestat.component.modal.change_floor_plan, beestat.component.modal);
/**
* Decorate
*
* @param {rocket.Elements} parent
*/
beestat.component.modal.change_floor_plan.prototype.decorate_contents_ = function(parent) {
const self = this;
const p = document.createElement('p');
p.innerText = 'You have multiple floor plans; which one would you like to view?';
parent.appendChild(p);
const grid = document.createElement('div');
grid.style.display = 'grid';
grid.style.gridTemplateColumns = 'repeat(auto-fit, minmax(150px, 1fr))';
grid.style.columnGap = beestat.style.size.gutter + 'px';
grid.style.rowGap = beestat.style.size.gutter + 'px';
parent.appendChild(grid);
var sorted_floor_plans = $.values(beestat.cache.floor_plan)
.sort(function(a, b) {
return a.name > b.name;
});
let div;
sorted_floor_plans.forEach(function(floor_plan) {
div = document.createElement('div');
grid.appendChild(div);
const tile = new beestat.component.tile.floor_plan(floor_plan.floor_plan_id)
.set_text_color('#fff')
.set_display('block');
if (floor_plan.floor_plan_id === beestat.setting('floor_plan_id')) {
tile.set_background_color(beestat.style.color.lightblue.base);
} else {
tile
.set_background_color(beestat.style.color.bluegray.base)
.set_background_hover_color(beestat.style.color.lightblue.base)
.addEventListener('click', function() {
beestat.setting('floor_plan_id', floor_plan.floor_plan_id);
self.dispose();
});
}
tile.render($(div));
});
};
/**
* Get title.
*
* @return {string} Title.
*/
beestat.component.modal.change_floor_plan.prototype.get_title_ = function() {
return 'Change Floor Plan';
};

View File

@ -62,7 +62,7 @@ beestat.component.modal.change_system_type.prototype.decorate_contents_ = functi
key
);
let button_group = new beestat.component.tile_group();
let tile_group = new beestat.component.tile_group();
options[key].forEach(function(system_type) {
let text = system_type.charAt(0).toUpperCase() + system_type.slice(1);
if (thermostat.system_type.detected[key].equipment === system_type) {
@ -93,9 +93,9 @@ beestat.component.modal.change_system_type.prototype.decorate_contents_ = functi
button.set_background_color(beestat.style.color.bluegray.base);
}
button_group.add_button(button);
tile_group.add_tile(button);
});
button_group.render(parent);
tile_group.render(parent);
}
};
@ -117,6 +117,7 @@ beestat.component.modal.change_system_type.prototype.get_buttons_ = function() {
.set_background_color('#fff')
.set_text_color(beestat.style.color.gray.base)
.set_text_hover_color(beestat.style.color.red.base)
.set_shadow(false)
.set_text('Cancel')
.addEventListener('click', function() {
self.dispose();

View File

@ -176,6 +176,7 @@ beestat.component.modal.create_floor_plan.prototype.get_buttons_ = function() {
.set_background_color('#fff')
.set_text_color(beestat.style.color.gray.base)
.set_text_hover_color(beestat.style.color.red.base)
.set_shadow(false)
.set_text('Cancel')
.addEventListener('click', function() {
self.dispose();
@ -248,7 +249,7 @@ beestat.component.modal.create_floor_plan.prototype.get_buttons_ = function() {
'floor_plan'
)
.set_callback(function(response) {
beestat.setting('floor_plan_id', response.new_floor_plan.floor_plan_id);
beestat.setting('visualize.floor_plan_id', response.new_floor_plan.floor_plan_id);
beestat.cache.set('floor_plan', response.floor_plan);
})
.send();

View File

@ -47,6 +47,7 @@ beestat.component.modal.delete_floor_plan.prototype.get_buttons_ = function() {
.set_background_color('#fff')
.set_text_color(beestat.style.color.gray.base)
.set_text_hover_color(beestat.style.color.red.base)
.set_shadow(false)
.set_text('Cancel')
.addEventListener('click', function() {
self.dispose();
@ -78,9 +79,9 @@ beestat.component.modal.delete_floor_plan.prototype.get_buttons_ = function() {
console.log('deleted fp');
console.log(response);
if (Object.keys(response.floor_plan).length > 0) {
beestat.setting('floor_plan_id', Object.values(response.floor_plan)[0].floor_plan_id);
beestat.setting('visualize.floor_plan_id', Object.values(response.floor_plan)[0].floor_plan_id);
} else {
beestat.setting('floor_plan_id', null);
beestat.setting('visualize.floor_plan_id', null);
}
beestat.cache.set('floor_plan', response.floor_plan);
})

View File

@ -169,7 +169,7 @@ beestat.component.modal.download_data.prototype.decorate_presets_ = function(par
}
];
var button_group = new beestat.component.tile_group();
var tile_group = new beestat.component.tile_group();
presets.forEach(function(preset) {
preset.button
.set_background_color(beestat.style.color.bluegray.base)
@ -181,7 +181,7 @@ beestat.component.modal.download_data.prototype.decorate_presets_ = function(par
self.state_.range_end = preset.range_end;
self.dispatchEvent('range_change');
});
button_group.add_button(preset.button);
tile_group.add_tile(preset.button);
});
// Highlight the preset if the current date range matches.
@ -198,7 +198,7 @@ beestat.component.modal.download_data.prototype.decorate_presets_ = function(par
});
});
button_group.render(column);
tile_group.render(column);
};
/**
@ -246,6 +246,7 @@ beestat.component.modal.download_data.prototype.get_buttons_ = function() {
.set_background_color('#fff')
.set_text_color(beestat.style.color.gray.base)
.set_text_hover_color(beestat.style.color.red.base)
.set_shadow(false)
.set_text('Cancel')
.addEventListener('click', function() {
self.dispose();

View File

@ -57,6 +57,7 @@ beestat.component.modal.enjoy_beestat.prototype.get_buttons_ = function() {
var hide = new beestat.component.tile()
.set_background_color('#fff')
.set_shadow(false)
.set_text_color(beestat.style.color.gray.base)
.set_text_hover_color(beestat.style.color.bluegray.base)
.set_text('Hide for one month')

View File

@ -43,9 +43,9 @@ beestat.component.modal.runtime_sensor_detail_custom.prototype.decorate_contents
beestat.component.modal.runtime_sensor_detail_custom.prototype.decorate_range_type_ = function(parent) {
var self = this;
var button_group = new beestat.component.tile_group();
var tile_group = new beestat.component.tile_group();
button_group.add_button(new beestat.component.tile()
tile_group.add_tile(new beestat.component.tile()
.set_background_hover_color(beestat.style.color.lightblue.base)
.set_text_color('#fff')
.set_background_color(
@ -59,7 +59,7 @@ beestat.component.modal.runtime_sensor_detail_custom.prototype.decorate_range_ty
self.rerender();
}));
button_group.add_button(new beestat.component.tile()
tile_group.add_tile(new beestat.component.tile()
.set_background_hover_color(beestat.style.color.lightblue.base)
.set_text_color('#fff')
.set_background_color(
@ -78,7 +78,7 @@ beestat.component.modal.runtime_sensor_detail_custom.prototype.decorate_range_ty
parent.appendChild(row);
var column = $.createElement('div').addClass(['column column_12']);
row.appendChild(column);
button_group.render(column);
tile_group.render(column);
};
/**
@ -298,6 +298,7 @@ beestat.component.modal.runtime_sensor_detail_custom.prototype.get_buttons_ = fu
.set_background_color('#fff')
.set_text_color(beestat.style.color.gray.base)
.set_text_hover_color(beestat.style.color.red.base)
.set_shadow(false)
.set_text('Cancel')
.addEventListener('click', function() {
self.dispose();
@ -332,8 +333,8 @@ beestat.component.modal.runtime_sensor_detail_custom.prototype.get_buttons_ = fu
self.state_.runtime_sensor_detail_range_static_end = temp;
}
beestat.cache.delete('runtime_thermostat');
beestat.cache.delete('runtime_sensor');
beestat.cache.delete('data.runtime_sensor_detail__runtime_thermostat');
beestat.cache.delete('data.runtime_sensor_detail__runtime_sensor');
beestat.setting(
{
'runtime_sensor_detail_range_type': self.state_.runtime_sensor_detail_range_type,

View File

@ -43,9 +43,9 @@ beestat.component.modal.runtime_thermostat_detail_custom.prototype.decorate_cont
beestat.component.modal.runtime_thermostat_detail_custom.prototype.decorate_range_type_ = function(parent) {
var self = this;
var button_group = new beestat.component.tile_group();
var tile_group = new beestat.component.tile_group();
button_group.add_button(new beestat.component.tile()
tile_group.add_tile(new beestat.component.tile()
.set_background_hover_color(beestat.style.color.lightblue.base)
.set_text_color('#fff')
.set_background_color(
@ -59,7 +59,7 @@ beestat.component.modal.runtime_thermostat_detail_custom.prototype.decorate_rang
self.rerender();
}));
button_group.add_button(new beestat.component.tile()
tile_group.add_tile(new beestat.component.tile()
.set_background_hover_color(beestat.style.color.lightblue.base)
.set_text_color('#fff')
.set_background_color(
@ -78,7 +78,7 @@ beestat.component.modal.runtime_thermostat_detail_custom.prototype.decorate_rang
parent.appendChild(row);
var column = $.createElement('div').addClass(['column column_12']);
row.appendChild(column);
button_group.render(column);
tile_group.render(column);
};
/**
@ -298,6 +298,7 @@ beestat.component.modal.runtime_thermostat_detail_custom.prototype.get_buttons_
.set_background_color('#fff')
.set_text_color(beestat.style.color.gray.base)
.set_text_hover_color(beestat.style.color.red.base)
.set_shadow(false)
.set_text('Cancel')
.addEventListener('click', function() {
self.dispose();
@ -332,7 +333,7 @@ beestat.component.modal.runtime_thermostat_detail_custom.prototype.get_buttons_
self.state_.runtime_thermostat_detail_range_static_end = temp;
}
beestat.cache.delete('runtime_thermostat');
beestat.cache.delete('data.runtime_thermostat_detail__runtime_thermostat');
beestat.setting(
{
'runtime_thermostat_detail_range_type': self.state_.runtime_thermostat_detail_range_type,

View File

@ -43,13 +43,13 @@ beestat.component.modal.runtime_thermostat_summary_custom.prototype.decorate_con
]
};
var button_groups = {};
var tile_groups = {};
this.selected_buttons_ = {};
for (let key in options) {
let current_type = beestat.setting(key);
let button_group = new beestat.component.tile_group();
let tile_group = new beestat.component.tile_group();
options[key].forEach(function(value) {
let text = value.replace('runtime_thermostat_summary_', '')
.charAt(0)
@ -105,9 +105,9 @@ beestat.component.modal.runtime_thermostat_summary_custom.prototype.decorate_con
button.set_background_color(beestat.style.color.bluegray.base);
}
button_group.add_button(button);
tile_group.add_tile(button);
});
button_groups[key] = button_group;
tile_groups[key] = tile_group;
}
// Display it all
@ -122,13 +122,13 @@ beestat.component.modal.runtime_thermostat_summary_custom.prototype.decorate_con
time_count.render(column);
column = $.createElement('div').addClass(['column column_10']);
row.appendChild(column);
button_groups.runtime_thermostat_summary_time_period.render(column);
tile_groups.runtime_thermostat_summary_time_period.render(column);
(new beestat.component.title('Group By')).render(parent);
row = $.createElement('div').addClass('row');
parent.appendChild(row);
column = $.createElement('div').addClass(['column column_12']);
row.appendChild(column);
button_groups.runtime_thermostat_summary_group_by.render(column);
tile_groups.runtime_thermostat_summary_group_by.render(column);
};
/**
@ -152,6 +152,7 @@ beestat.component.modal.runtime_thermostat_summary_custom.prototype.get_buttons_
.set_background_color('#fff')
.set_text_color(beestat.style.color.gray.base)
.set_text_hover_color(beestat.style.color.red.base)
.set_shadow(false)
.set_text('Cancel')
.addEventListener('click', function() {
self.dispose();

View File

@ -69,8 +69,8 @@ beestat.component.modal.update_floor_plan.prototype.decorate_contents_ = functio
sorted_groups.forEach(function(group) {
new beestat.component.tile.floor_plan_group(group)
.set_background_color(beestat.style.color.bluegray.base)
.set_text_color('#fff')
.set_background_color(beestat.style.color.gray.dark)
.set_shadow(false)
.set_display('block')
.render($(grid));
});
@ -136,6 +136,7 @@ beestat.component.modal.update_floor_plan.prototype.get_buttons_ = function() {
.set_background_color('#fff')
.set_text_color(beestat.style.color.gray.base)
.set_text_hover_color(beestat.style.color.red.base)
.set_shadow(false)
.set_text('Cancel')
.addEventListener('click', function() {
self.dispose();

View File

@ -1,8 +1,12 @@
/**
* Home Scene
*
* @param {number} floor_plan_id The floor plan to render.
* @param {object} data Sensor data.
*/
beestat.component.scene = function(thermostat_id) {
this.thermostat_id_ = thermostat_id;
beestat.component.scene = function(floor_plan_id, data) {
this.floor_plan_id_ = floor_plan_id;
this.data_ = data;
beestat.component.apply(this, arguments);
};
@ -11,13 +15,19 @@ beestat.extend(beestat.component.scene, beestat.component);
beestat.component.scene.sun_light_intensity = 1;
beestat.component.scene.moon_light_intensity = 0.3;
beestat.component.scene.ambient_light_intensity_base = 0.3;
beestat.component.scene.ambient_light_intensity_base = 1.5;
beestat.component.scene.ambient_light_intensity_sky = 0.4;
beestat.component.scene.moon_opacity = 0.9;
beestat.component.scene.turbidity = 10;
beestat.component.scene.rayleigh = 0.5;
beestat.component.scene.mie_coefficient = 0.001;
beestat.component.scene.mie_directional_g = 0.95;
beestat.component.scene.moon_opacity = 0.9;
// beestat.component.scene.turbidity = 14;
// beestat.component.scene.rayleigh = 0.7;
// beestat.component.scene.mie_coefficient = 0.008;
// beestat.component.scene.mie_directional_g = 0.9;
beestat.component.scene.shadow_map_size = 4096;
@ -32,6 +42,20 @@ beestat.component.scene.prototype.rerender = function() {
this.add_floor_plan_();
};
/**
* Set the width of this component.
*
* @param {number} width
*/
beestat.component.scene.prototype.set_width = function(width) {
this.width_ = width;
this.camera_.aspect = this.width_ / this.height_;
this.camera_.updateProjectionMatrix();
this.renderer_.setSize(this.width_, this.height_);
};
/**
* Decorate
*
@ -48,17 +72,21 @@ beestat.component.scene.prototype.decorate_ = function(parent) {
'watcher': false
};
this.width_ = this.state_.scene_width || 800;
this.height_ = 500;
this.add_scene_(parent);
this.add_background_(parent);
this.add_renderer_(parent);
this.add_camera_();
this.add_controls_(parent);
this.add_sky_();
this.add_moon_();
this.add_moon_light_();
// this.add_sky_();
// this.add_moon_();
// this.add_moon_light_();
this.add_sun_light_();
this.add_ambient_light_();
this.add_ground_();
// this.add_ground_();
// this.add_ground_limited_();
this.add_group_();
this.add_floor_plan_();
@ -85,6 +113,7 @@ beestat.component.scene.prototype.decorate_ = function(parent) {
const animate = function() {
requestAnimationFrame(animate);
self.controls_.update();
self.renderer_.render(self.scene_, self.camera_);
};
animate();
@ -134,7 +163,7 @@ beestat.component.scene.prototype.add_renderer_ = function(parent) {
this.renderer_ = new THREE.WebGLRenderer({
'antialias': true
});
this.renderer_.setSize(window.innerWidth / 2.2, window.innerHeight / 2.2);
this.renderer_.setSize(window.innerWidth /1.1, window.innerHeight / 1.1);
this.renderer_.shadowMap.enabled = true;
this.renderer_.shadowMap.autoUpdate = false;
@ -143,7 +172,8 @@ beestat.component.scene.prototype.add_renderer_ = function(parent) {
* https://threejs.org/examples/webgl_shaders_sky.html
*/
this.renderer_.toneMapping = THREE.ACESFilmicToneMapping;
this.renderer_.toneMappingExposure = 0.5;
// this.renderer_.toneMappingExposure = 0.5;
this.renderer_.toneMappingExposure = 0.2;
parent[0].appendChild(this.renderer_.domElement);
};
@ -175,7 +205,11 @@ beestat.component.scene.prototype.add_camera_ = function() {
* @param {rocket.Elements} parent
*/
beestat.component.scene.prototype.add_controls_ = function(parent) {
new THREE.OrbitControls(this.camera_, parent[0]); // eslint-disable-line no-new
this.controls_ = new THREE.OrbitControls(this.camera_, parent[0]);
this.controls_.enableDamping = true;
this.controls_.enablePan = false;
this.controls_.maxDistance = 1000;
this.controls_.minDistance = 400;
};
/**
@ -262,7 +296,8 @@ beestat.component.scene.prototype.add_sun_light_ = function() {
beestat.component.scene.sun_light_intensity
);
this.directional_light_sun_.castShadow = true;
this.directional_light_sun_.shadow.bias = -0.00009;
// this.directional_light_sun_.shadow.bias = -0.00009;
// this.directional_light_sun_.shadow.radius = 32;
this.directional_light_sun_.shadow.mapSize.width = beestat.component.scene.shadow_map_size;
this.directional_light_sun_.shadow.mapSize.height = beestat.component.scene.shadow_map_size;
this.directional_light_sun_.shadow.camera.left = -1000;
@ -311,15 +346,88 @@ beestat.component.scene.prototype.add_ambient_light_ = function() {
/**
* Set the date and thus the position of the sun/moon.
*
* @param {Date} date
*/
beestat.component.scene.prototype.set_date = function(date) {
const thermostat = beestat.cache.thermostat[this.thermostat_id_];
const address = beestat.cache.address[thermostat.address_id];
beestat.component.scene.prototype.update_ = function() {
const self = this;
const floor_plan = beestat.cache.floor_plan[this.floor_plan_id_];
const time = this.date_.format('HH:mm');
// TODO Memoize this
let gradient;
if (self.data_type_ === 'temperature') {
gradient = beestat.style.generate_gradient(
[
beestat.style.hex_to_rgb(beestat.style.color.blue.dark),
beestat.style.hex_to_rgb(beestat.style.color.gray.base),
beestat.style.hex_to_rgb(beestat.style.color.red.dark)
],
100
);
} else if (self.data_type_ === 'occupancy') {
gradient = beestat.style.generate_gradient(
[
beestat.style.hex_to_rgb(beestat.style.color.gray.base),
beestat.style.hex_to_rgb(beestat.style.color.orange.dark)
],
200
);
}
// Set the color of each room
floor_plan.data.groups.forEach(function(group) {
group.rooms.forEach(function(room) {
let color;
if (
room.sensor_id !== undefined &&
self.data_.series[self.data_type_][room.sensor_id] !== undefined &&
self.data_.series[self.data_type_][room.sensor_id][time] !== undefined
) {
const value = self.data_.series[self.data_type_][room.sensor_id][time];
const percentage = Math.min(
1,
Math.max(
0,
(value - self.heat_map_min_) / (self.heat_map_max_ - self.heat_map_min_)
)
);
color = beestat.style.rgb_to_hex(gradient[Math.floor((gradient.length - 1) * percentage)]);
} else {
color = beestat.style.color.gray.dark;
}
self.rooms_[room.room_id].material.color.setHex(color.replace('#', '0x'));
});
});
let address;
if (floor_plan.address_id !== null) { // todo should be undefined?
address = beestat.cache.address[floor_plan.address_id];
} else {
address = {
'normalized': {
'metadata': {
'latitude': 0,
'longitude': 0
}
}
};
}
// After sunset may need to hide the sun light source...or maybe wean it's brightness off or something.
// TODO TEMP TO KEEP LIGHTING CONSISTENT
// const date = new Date('2022-08-16 12:00:00');
// const date = this.date_.toDate();
const date = moment()
.hour(12)
.minute(0)
.second(0)
.toDate();
const sun_object_vector = new THREE.Vector3();
const moon_object_vector = new THREE.Vector3();
@ -386,29 +494,33 @@ beestat.component.scene.prototype.set_date = function(date) {
beestat.component.scene.ambient_light_intensity_sky * Math.sin(sun_position.altitude) * (1 - eclipse_percentage)
);
this.moon_.material.opacity = 0.2;
// this.moon_.material.opacity = 0.2;
/**
* Mess up the sky during an eclipse
*/
// Turn down to 0
this.sky_.material.uniforms.rayleigh.value =
beestat.component.scene.rayleigh * (1 - eclipse_percentage);
if (this.sky_ !== undefined) {
this.sky_.material.uniforms.rayleigh.value =
beestat.component.scene.rayleigh * (1 - eclipse_percentage);
// Turn down to almost 0
this.sky_.material.uniforms.mieCoefficient.value =
Math.max(
0.00001,
beestat.component.scene.mie_coefficient * (1 - eclipse_percentage)
);
// Turn down to almost 0
this.sky_.material.uniforms.mieCoefficient.value =
Math.max(
0.00001,
beestat.component.scene.mie_coefficient * (1 - eclipse_percentage)
);
// Increase to almost 1
this.sky_.material.uniforms.mieDirectionalG.value =
Math.max(
beestat.component.scene.mie_directional_g,
0.9999 * eclipse_percentage
);
// Increase to almost 1
this.sky_.material.uniforms.mieDirectionalG.value =
Math.max(
beestat.component.scene.mie_directional_g,
0.9999 * eclipse_percentage
);
this.sky_.material.uniforms.sunPosition.value.copy(sun_object_vector);
}
/*
* this.renderer_.toneMappingExposure = Math.max(
@ -418,12 +530,23 @@ beestat.component.scene.prototype.set_date = function(date) {
*/
// Set the brightness of the sun
this.directional_light_sun_.intensity =
beestat.component.scene.sun_light_intensity * Math.sin(sun_position.altitude) * (1 - eclipse_percentage);
if (this.directional_light_sun_ !== undefined) {
this.directional_light_sun_.intensity =
beestat.component.scene.sun_light_intensity * Math.sin(sun_position.altitude) * (1 - eclipse_percentage);
this.directional_light_sun_.position.copy(sun_light_vector);
}
// Set the brightness of the moon
this.directional_light_moon_.intensity =
beestat.component.scene.moon_light_intensity * moon_illumination.fraction;
if (this.directional_light_moon_ !== undefined) {
this.directional_light_moon_.intensity =
beestat.component.scene.moon_light_intensity * moon_illumination.fraction;
this.directional_light_moon_.position.copy(moon_light_vector);
}
if (this.moon_ !== undefined) {
this.moon_.position.copy(moon_object_vector);
}
// TODO size of moon based on distance? Might not be worth it haha.
@ -433,12 +556,8 @@ beestat.component.scene.prototype.set_date = function(date) {
*/
// Update the directional light positions
this.directional_light_sun_.position.copy(sun_light_vector);
this.directional_light_moon_.position.copy(moon_light_vector);
// Update the position of the sun and moon in the sky
this.sky_.material.uniforms.sunPosition.value.copy(sun_object_vector);
this.moon_.position.copy(moon_object_vector);
// this.sky2_.material.uniforms.sunPosition.value.copy(moon_object_vector);
// Update shadows
@ -484,8 +603,6 @@ beestat.component.scene.prototype.add_ground_ = function() {
plane.rotation.x += Math.PI / 2;
plane.receiveShadow = true;
this.scene_.add(plane);
if (this.debug_.grid === true) {
@ -502,6 +619,45 @@ beestat.component.scene.prototype.add_ground_ = function() {
this.scene_.add(grid_helper);
}
};
beestat.component.scene.prototype.add_ground_limited_ = function() {
const height = 24;
const bounding_box = beestat.floor_plan.get_bounding_box(this.floor_plan_id_);
const size = Math.max(bounding_box.width, bounding_box.height) + 120;
// const texture = new THREE.TextureLoader().load('img/grass.jpg');
// texture.wrapS = THREE.RepeatWrapping;
// texture.wrapT = THREE.RepeatWrapping;
// texture.repeat.set(1000, 1000);
const geometry = new THREE.BoxGeometry(size, size, height);
const material = new THREE.MeshLambertMaterial({
// 'map': texture,
'color': new THREE.Color(beestat.style.color.green.dark),
'side': THREE.DoubleSide
});
const box = new THREE.Mesh(geometry, material);
box.translateY(height / -2);
box.rotation.x += Math.PI / 2;
// box.receiveShadow = true;
this.scene_.add(box);
if (this.debug_.grid === true) {
const grid_size = 100;
const grid_divisions = grid_size;
const grid_color_center_line = new THREE.Color(beestat.style.color.lightblue.base);
const grid_color_grid = new THREE.Color(beestat.style.color.green.base);
const grid_helper = new THREE.GridHelper(
grid_size,
grid_divisions,
grid_color_center_line,
grid_color_grid
);
this.scene_.add(grid_helper);
}
};
@ -512,11 +668,10 @@ beestat.component.scene.prototype.add_ground_ = function() {
/**
* Add a background.
*/
/*
* beestat.component.scene.prototype.add_background_ = function() {
* this.scene_.background = new THREE.Color(beestat.style.color.bluegray.dark);
* }
*/
beestat.component.scene.prototype.add_background_ = function() {
this.scene_.background = new THREE.Color(beestat.style.color.bluegray.dark);
}
@ -529,97 +684,58 @@ beestat.component.scene.prototype.add_ground_ = function() {
* @param {object} room The room to add.
*/
beestat.component.scene.prototype.add_room_ = function(group, room) {
const color = new THREE.Color(beestat.style.color.blue.base);
const color = beestat.style.color.gray.base;
var clipper_offset = new ClipperLib.ClipperOffset();
clipper_offset.AddPath(room.points, ClipperLib.JoinType.jtMiter, ClipperLib.EndType.etClosedPolygon);
clipper_offset.AddPath(
room.points,
ClipperLib.JoinType.jtSquare,
ClipperLib.EndType.etClosedPolygon
);
var clipper_hole = new ClipperLib.Path();
// var offsetted_paths = new ClipperLib.Path();
clipper_offset.Execute(clipper_hole, -5);
clipper_offset.Execute(clipper_hole, -3);
// Full height
// const extrude_height = (room.height || group.height) - 3;
// Just the floor plan
const extrude_height = 6;
room.height = 12 * 1;
// Create a shape using the points of the room.
const shape = new THREE.Shape();
const first_point = room.points[0];
const first_point = clipper_hole[0].shift();
shape.moveTo(first_point.x, first_point.y);
room.points.forEach(function(point) {
clipper_hole[0].forEach(function(point) {
shape.lineTo(point.x, point.y);
});
// FLOOR
const floor = shape.clone();
const floor_geometry = new THREE.ShapeGeometry(floor);
const floor_material = new THREE.MeshLambertMaterial({
'color': color,
'side': THREE.DoubleSide
});
const floor_mesh = new THREE.Mesh(floor_geometry, floor_material);
floor_mesh.position.z = ((room.elevation || group.elevation) + room.height) * -1;
// Translate the floor_mesh to the room x/y position.
floor_mesh.translateX(room.x);
floor_mesh.translateY(room.y);
floor_mesh.translateZ(room.height - 1);
// floor.rotation.x += Math.PI/2;
// Shadows are neat.
floor_mesh.castShadow = true;
floor_mesh.receiveShadow = true;
// Add the mesh to the group.
this.group_.add(floor_mesh);
// Create a hole
const hole = new THREE.Shape();
const hole_first_point = clipper_hole[0].shift();
hole.moveTo(hole_first_point.x, hole_first_point.y);
clipper_hole[0].forEach(function(point) {
hole.lineTo(point.x, point.y);
});
// Hole
// const hole = shape.clone();
shape.holes.push(hole);
/* var paths = [[{x:30,y:30},{x:130,y:30},{x:130,y:130},{x:30,y:130}],
[{x:60,y:60},{x:60,y:100},{x:100,y:100},{x:100,y:60}]];
var scale = 100;
ClipperLib.JS.ScaleUpPaths(paths, scale);
// Possibly ClipperLib.Clipper.SimplifyPolygons() here
// Possibly ClipperLib.Clipper.CleanPolygons() here
var co = new ClipperLib.ClipperOffset(2, 0.25);
// ClipperLib.EndType = {etOpenSquare: 0, etOpenRound: 1, etOpenButt: 2, etClosedPolygon: 3, etClosedLine : 4 };
co.AddPaths(paths, ClipperLib.JoinType.jtRound, ClipperLib.EndType.etClosedPolygon);
var offsetted_paths = new ClipperLib.Paths();
co.Execute(offsetted_paths, -10 * scale);*/
// Extrude the shape and create the mesh.
const extrude_settings = {
'depth': room.height,
'bevelEnabled': false
'depth': extrude_height,
'bevelEnabled': true,
'bevelThickness': 1,
'bevelSize': 1,
'bevelOffset': 1,
'bevelSegments': 5
};
const geometry = new THREE.ExtrudeGeometry(
shape,
extrude_settings
);
// const material = new THREE.MeshBasicMaterial({
const material = new THREE.MeshPhongMaterial({
// 'color': new THREE.Color(beestat.style.color.red.base)
'color': color
'color': color,
// 'transparent': true,
// 'opacity': 0.5
});
// material.opacity = 0.5;
const mesh = new THREE.Mesh(geometry, material);
mesh.position.z = ((room.elevation || group.elevation) + room.height) * -1;
mesh.position.z = -extrude_height - (room.elevation || group.elevation);
// Translate the mesh to the room x/y position.
mesh.translateX(room.x);
@ -627,10 +743,16 @@ beestat.component.scene.prototype.add_room_ = function(group, room) {
// Shadows are neat.
mesh.castShadow = true;
mesh.receiveShadow = true;
// mesh.receiveShadow = true;
// Add the mesh to the group.
this.group_.add(mesh);
// Store a reference to the mesh representing each room.
if (this.rooms_ === undefined) {
this.rooms_ = {};
}
this.rooms_[room.room_id] = mesh;
};
/**
@ -671,7 +793,16 @@ beestat.component.scene.prototype.update_debug_ = function() {
* Add a group containing all of the extruded geometry.
*/
beestat.component.scene.prototype.add_group_ = function() {
const bounding_box = beestat.floor_plan.get_bounding_box(this.floor_plan_id_);
// this.floor_plan_center_x_ = ;
// this.floor_plan_center_y_ = (bounding_box.bottom + bounding_box.top) / 2;
// this.view_box_.x = center_x - (this.view_box_.width / 2);
// this.view_box_.y = center_y - (this.view_box_.height / 2);
this.group_ = new THREE.Group();
this.group_.translateX((bounding_box.right + bounding_box.left) / -2);
this.group_.translateZ((bounding_box.bottom + bounding_box.top) / -2);
// this.group_.rotation.x = -Math.PI / 2;
//
this.group_.rotation.x = Math.PI / 2;
@ -683,10 +814,79 @@ beestat.component.scene.prototype.add_group_ = function() {
*/
beestat.component.scene.prototype.add_floor_plan_ = function() {
const self = this;
const floor_plan = beestat.cache.floor_plan[1];
const floor_plan = beestat.cache.floor_plan[this.floor_plan_id_];
floor_plan.data.groups.forEach(function(group) {
group.rooms.forEach(function(room) {
self.add_room_(group, room);
});
});
};
/**
* Set the current date.
*
* @param {moment} date
*
* @return {beestat.component.scene}
*/
beestat.component.scene.prototype.set_date = function(date) {
this.date_ = date;
if (this.rendered_ === true) {
this.update_();
}
return this;
};
/**
* Set the type of data this scene is visualizing.
*
* @param {string} data_type temperature|occupancy
*
* @return {beestat.component.scene}
*/
beestat.component.scene.prototype.set_data_type = function(data_type) {
this.data_type_ = data_type;
if (this.rendered_ === true) {
this.update_();
}
return this;
};
/**
* Set the min value of the heat map.
*
* @param {string} heat_map_min
*
* @return {beestat.component.scene}
*/
beestat.component.scene.prototype.set_heat_map_min = function(heat_map_min) {
this.heat_map_min_ = heat_map_min;
if (this.rendered_ === true) {
this.update_();
}
return this;
};
/**
* Set the max value of the heat map.
*
* @param {string} heat_map_max
*
* @return {beestat.component.scene}
*/
beestat.component.scene.prototype.set_heat_map_max = function(heat_map_max) {
this.heat_map_max_ = heat_map_max;
if (this.rendered_ === true) {
this.update_();
}
return this;
};

View File

@ -17,10 +17,11 @@ beestat.component.tile.prototype.decorate_ = function(parent) {
const background_color = this.background_color_ || 'none';
const text_color = this.text_color_ || '#fff';
const tabbable = this.tabbable_ || false;
const shadow = this.shadow_ === undefined ? true : this.shadow_;
const display = this.display_ === 'block' ? 'flex' : 'inline-flex';
let border_radius;
if (this.type_ === 'pill') {
border_radius = (this.get_size_() === 'large' ? 48 : 32);
border_radius = (this.get_size_() === 'large' ? 48 : 36);
} else {
border_radius = beestat.style.size.border_radius;
}
@ -30,7 +31,7 @@ beestat.component.tile.prototype.decorate_ = function(parent) {
Object.assign(this.container_.style, {
'background': background_color,
'border-radius': `${border_radius}px`,
'height': `${(this.get_size_() === 'large' ? 48 : 32)}px`,
'height': `${(this.get_size_() === 'large' ? 48 : 36)}px`,
'display': display,
'align-items': 'center',
'color': text_color,
@ -40,12 +41,18 @@ beestat.component.tile.prototype.decorate_ = function(parent) {
'text-align': 'left'
});
if (shadow === true) {
Object.assign(this.container_.style, {
'box-shadow': '0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24)'
});
}
parent.appendChild(this.container_);
// Padding. Basically for icon only make it a nice square button.
if (this.get_text_() === undefined) {
Object.assign(this.container_.style, {
'width': `${(this.get_size_() === 'large' ? 48 : 32)}px`,
'width': `${(this.get_size_() === 'large' ? 48 : 36)}px`,
'justify-content': 'center'
});
} else {
@ -230,7 +237,8 @@ beestat.component.tile.prototype.get_icon_ = function() {
/**
* Set the text of the button.
*
* @param {string|array} text A single string or array of strings. If an array is passed multiple lines of text will be shown.
* @param {string|array} text A single string or array of strings. If an array
* is passed multiple lines of text will be shown.
*
* @return {beestat.component.tile} This.
*/
@ -268,6 +276,21 @@ beestat.component.tile.prototype.set_background_color = function(background_colo
return this;
};
/**
* Set whether or not there is a shadow. Default true.
*
* @param {boolean} shadow
*
* @return {beestat.component.tile} This.
*/
beestat.component.tile.prototype.set_shadow = function(shadow) {
this.shadow_ = shadow;
if (this.rendered_ === true) {
this.rerender();
}
return this;
};
/**
* Set the text color.
*

View File

@ -2,7 +2,7 @@
* A group of tiles.
*/
beestat.component.tile_group = function() {
this.buttons_ = [];
this.tiles_ = [];
beestat.component.apply(this, arguments);
};
beestat.extend(beestat.component.tile_group, beestat.component);
@ -13,53 +13,37 @@ beestat.extend(beestat.component.tile_group, beestat.component);
* @param {rocket.Elements} parent
*/
beestat.component.tile_group.prototype.decorate_ = function(parent) {
var self = this;
const flex = document.createElement('div');
Object.assign(flex.style, {
'display': 'inline-flex',
'flex-wrap': 'wrap',
'grid-gap': `${beestat.style.size.gutter / 2}px`
});
parent.appendChild(flex);
// Only exists so that there can be spacing between wrapped elements.
var outer_container = $.createElement('div')
.style({
'margin-top': (beestat.style.size.gutter / -2)
});
parent.appendChild(outer_container);
this.buttons_.forEach(function(button, i) {
var container = $.createElement('div').style({
'display': 'inline-block',
'margin-right': (i < self.buttons_.length) ? (beestat.style.size.gutter / 2) : 0,
'margin-top': (beestat.style.size.gutter / 2)
});
button.render(container);
outer_container.appendChild(container);
this.tiles_.forEach(function(tile) {
tile.render($(flex));
});
};
/**
* Add a button to this group.
* Add a tile to this group.
*
* @param {beestat.component.button} button The button to add.
* @param {beestat.component.tile} tile The tile to add.
*/
beestat.component.tile_group.prototype.add_button = function(button) {
this.buttons_.push(button);
beestat.component.tile_group.prototype.add_tile = function(tile) {
this.tiles_.push(tile);
if (this.rendered_ === true) {
this.rerender();
}
};
/**
* Get all of the buttons in this button group.
*
* @return {[beestat.component.button]} The buttons in this group.
*/
beestat.component.tile_group.prototype.get_buttons = function() {
return this.buttons_;
};
/**
* Remove this component from the page. Disposes the buttons first.
* Remove this component from the page. Disposes the tiles first.
*/
beestat.component.tile_group.prototype.dispose = function() {
this.buttons_.forEach(function(button) {
button.dispose();
this.tiles_.forEach(function(tile) {
tile.dispose();
});
beestat.component.prototype.dispose.apply(this, arguments);
};

View File

@ -38,7 +38,6 @@ if($setting->get('environment') === 'dev' || $setting->get('environment') === 'd
echo '<script src="/js/beestat/ecobee.js"></script>' . PHP_EOL;
echo '<script src="/js/beestat/runtime_thermostat.js"></script>' . PHP_EOL;
echo '<script src="/js/beestat/runtime_sensor.js"></script>' . PHP_EOL;
echo '<script src="/js/beestat/sensor.js"></script>' . PHP_EOL;
echo '<script src="/js/beestat/requestor.js"></script>' . PHP_EOL;
echo '<script src="/js/beestat/touch.js"></script>' . PHP_EOL;
echo '<script src="/js/beestat/crypto.js"></script>' . PHP_EOL;
@ -77,7 +76,8 @@ if($setting->get('environment') === 'dev' || $setting->get('environment') === 'd
echo '<script src="/js/component/card/settings.js"></script>' . PHP_EOL;
echo '<script src="/js/component/card/air_quality_detail.js"></script>' . PHP_EOL;
echo '<script src="/js/component/card/air_quality_summary.js"></script>' . PHP_EOL;
echo '<script src="/js/component/card/visualize.js"></script>' . PHP_EOL;
echo '<script src="/js/component/card/three_d.js"></script>' . PHP_EOL;
echo '<script src="/js/component/card/visualize_settings.js"></script>' . PHP_EOL;
echo '<script src="/js/component/card/air_quality_not_supported.js"></script>' . PHP_EOL;
echo '<script src="/js/component/card/floor_plan_editor.js"></script>' . PHP_EOL;
echo '<script src="/js/component/chart.js"></script>' . PHP_EOL;
@ -119,12 +119,12 @@ if($setting->get('environment') === 'dev' || $setting->get('environment') === 'd
echo '<script src="/js/component/modal/create_floor_plan.js"></script>' . PHP_EOL;
echo '<script src="/js/component/modal/update_floor_plan.js"></script>' . PHP_EOL;
echo '<script src="/js/component/modal/delete_floor_plan.js"></script>' . PHP_EOL;
echo '<script src="/js/component/modal/change_floor_plan.js"></script>' . PHP_EOL;
echo '<script src="/js/component/input.js"></script>' . PHP_EOL;
echo '<script src="/js/component/input/text.js"></script>' . PHP_EOL;
echo '<script src="/js/component/input/checkbox.js"></script>' . PHP_EOL;
echo '<script src="/js/component/input/radio.js"></script>' . PHP_EOL;
echo '<script src="/js/component/input/select.js"></script>' . PHP_EOL;
echo '<script src="/js/component/input/range.js"></script>' . PHP_EOL;
echo '<script src="/js/component/tile_group.js"></script>' . PHP_EOL;
echo '<script src="/js/component/tile.js"></script>' . PHP_EOL;
echo '<script src="/js/component/tile/floor_plan.js"></script>' . PHP_EOL;

View File

@ -33,7 +33,21 @@ beestat.layer.visualize.prototype.decorate_ = function(parent) {
cards.push([
{
'card': new beestat.component.card.early_access(),
'card': new beestat.component.card.visualize_settings(),
'size': 12
}
]);
const three_d = new beestat.component.card.three_d()
.set_floor_plan_id(beestat.setting('visualize.floor_plan_id'));
beestat.dispatcher.addEventListener('setting.visualize.floor_plan_id', function() {
three_d.set_floor_plan_id(beestat.setting('visualize.floor_plan_id'));
});
cards.push([
{
'card': three_d,
'size': 12
}
]);