1
0
mirror of https://github.com/beestat/app.git synced 2025-07-09 03:04:07 -04:00

Added settings for temperature, distance, and area.

This commit is contained in:
Jon Ziebell 2022-09-03 23:16:08 -04:00
parent 3bfb9f9ef5
commit 8237467e8b
33 changed files with 523 additions and 111 deletions

73
js/beestat/area.js Normal file
View File

@ -0,0 +1,73 @@
/**
* Format a area in a number of different ways.
*
* @param {object} args Instructions on how to format:
* area (required) - area to work with
* output_area_unit (optional, default ft) - Output area unit; default matches setting.
* convert (optional, default true) - Whether or not to convert to Celcius if necessary
* round (optional, default 1) - Number of decimal points to round to
* units (optional, default false) - Whether or not to include units in the result
* type (optional, default number) - Type of value to return (string|number)
*
* @return {string} The formatted area.
*/
beestat.area = function(args) {
// Allow passing a single argument of area for convenience.
if (typeof args !== 'object' || args === null) {
args = {
'area': args
};
}
const input_area_unit = 'in²';
var output_area_unit = beestat.default_value(
args.output_area_unit,
beestat.setting('units.area')
);
var round = beestat.default_value(args.round, 1);
var units = beestat.default_value(args.units, false);
var type = beestat.default_value(args.type, 'number');
var area = parseFloat(args.area);
// Check for invalid values.
if (isNaN(area) === true || isFinite(area) === false) {
return null;
}
const conversion_factors = {
'in²': {
'ft²': 0.00694444,
'm²': 0.00064516
}
};
// Convert if necessary and asked for.
if (input_area_unit !== output_area_unit) {
area *= conversion_factors[input_area_unit][output_area_unit];
}
/*
* Get to the appropriate number of decimal points. This will turn the number
* into a string. Then do a couple silly operations to fix -0.02 from showing
* up as -0.0 in string form.
*/
area = area.toFixed(round);
area = parseFloat(area);
area = area.toFixed(round);
/*
* Convert the previous string back to a number if requested. Format matters
* because HighCharts doesn't accept strings in some cases.
*/
if (type === 'number' && units === false) {
area = Number(area);
}
// Append units if asked for.
if (units === true) {
area = Number(area).toLocaleString() + ' ' + output_area_unit;
}
return area;
};

View File

@ -36,7 +36,7 @@ beestat.comparisons.get_attributes = function() {
};
}
// Always a 1000sqft size delta on both sides (total 2000 sqft).
// Always a 1000ft² size delta on both sides (total 2000 ft²).
if (thermostat.property.square_feet !== null) {
var property_square_feet_delta = 1000;
var min_property_square_feet = Math.max(

84
js/beestat/distance.js Normal file
View File

@ -0,0 +1,84 @@
/**
* Format a distance in a number of different ways.
*
* @param {object} args Instructions on how to format:
* distance (required) - distance to work with
* output_distance_unit (optional, default ft) - Output distance unit; default matches setting.
* convert (optional, default true) - Whether or not to convert to Celcius if necessary
* round (optional, default 1) - Number of decimal points to round to
* units (optional, default false) - Whether or not to include units in the result
* type (optional, default number) - Type of value to return (string|number)
*
* @return {string} The formatted distance.
*/
beestat.distance = function(args) {
// Allow passing a single argument of distance for convenience.
if (typeof args !== 'object' || args === null) {
args = {
'distance': args
};
}
var input_distance_unit = beestat.default_value(
args.input_distance_unit,
'in'
);
var output_distance_unit = beestat.default_value(
args.output_distance_unit,
beestat.setting('units.distance')
);
var round = beestat.default_value(args.round, 1);
var units = beestat.default_value(args.units, false);
var type = beestat.default_value(args.type, 'number');
var distance = parseFloat(args.distance);
// Check for invalid values.
if (isNaN(distance) === true || isFinite(distance) === false) {
return null;
}
const conversion_factors = {
'in': {
'ft': 0.0833,
'm': 0.0254
},
'm': {
'in': 39.3701,
'ft': 3.28084
},
'ft': {
'm': 0.3048,
'in': 12
}
};
// Convert if necessary and asked for.
if (input_distance_unit !== output_distance_unit) {
distance *= conversion_factors[input_distance_unit][output_distance_unit];
}
/*
* Get to the appropriate number of decimal points. This will turn the number
* into a string. Then do a couple silly operations to fix -0.02 from showing
* up as -0.0 in string form.
*/
distance = distance.toFixed(round);
distance = parseFloat(distance);
distance = distance.toFixed(round);
/*
* Convert the previous string back to a number if requested. Format matters
* because HighCharts doesn't accept strings in some cases.
*/
if (type === 'number' && units === false) {
distance = Number(distance);
}
// Append units if asked for.
if (units === true) {
distance = Number(distance).toLocaleString() + ' ' + output_distance_unit;
}
return distance;
};

View File

@ -5,7 +5,7 @@ beestat.floor_plan = {};
*
* @param {number} floor_plan_id
*
* @return {number} The area of the floor plan in sqft.
* @return {number} The area of the floor plan as in².
*/
beestat.floor_plan.get_area = function(floor_plan_id) {
const floor_plan = beestat.cache.floor_plan[floor_plan_id];
@ -24,7 +24,7 @@ beestat.floor_plan.get_area = function(floor_plan_id) {
* @param {object} group The group.
* @param {boolean} round Whether or not to round the result.
*
* @return {number} Area of the group in sqft.
* @return {number} Area of the group as in².
*/
beestat.floor_plan.get_area_group = function(group, round = true) {
let area = 0;
@ -46,10 +46,10 @@ beestat.floor_plan.get_area_group = function(group, round = true) {
* @param {object} room The room.
* @param {boolean} round Whether or not to round the result.
*
* @return {number} Area of the room in sqft.
* @return {number} Area of the room as in².
*/
beestat.floor_plan.get_area_room = function(room, round = true) {
let area = Math.abs(ClipperLib.Clipper.Area(room.points) / 144);
let area = Math.abs(ClipperLib.Clipper.Area(room.points));
if (round === true) {
return Math.round(area);

View File

@ -62,8 +62,6 @@ beestat.setting = function(argument_1, opt_value, opt_callback) {
'comparison_region': 'global',
'comparison_property_type': 'similar',
'temperature_unit': '°F',
'first_run': true,
'thermostat.#.profile.ignore_solar_gain': false,

View File

@ -6,7 +6,7 @@
* @param {object} args Instructions on how to format:
* temperature (required) - Temperature to work with
* input_temperature_unit (optional, default °F) - Input temperature unit
* output_temperature_unit (optional, default °F|°C) - Input temperature unit; default matches setting.
* output_temperature_unit (optional, default current setting) - Output temperature unit; default matches setting.
* convert (optional, default true) - Whether or not to convert to Celcius if necessary
* delta (optional, default false) - Whether or not the convert action is for a delta instead of a normal value
* round (optional, default 1) - Number of decimal points to round to
@ -29,7 +29,7 @@ beestat.temperature = function(args) {
);
var output_temperature_unit = beestat.default_value(
args.output_temperature_unit,
beestat.setting('temperature_unit')
beestat.setting('units.temperature')
);
var delta = beestat.default_value(args.delta, false);
var round = beestat.default_value(args.round, 1);

View File

@ -299,7 +299,7 @@ beestat.component.card.comparison_settings.prototype.decorate_detail_ = function
}
if (comparison_attributes.property_square_feet !== undefined) {
strings.push(this.get_comparison_string_(comparison_attributes.property_square_feet, 'sqft'));
strings.push(this.get_comparison_string_(comparison_attributes.property_square_feet, 'ft²'));
} else {
strings.push('Any square footage');
}

View File

@ -331,7 +331,7 @@ beestat.component.card.floor_plan_editor.prototype.decorate_info_pane_floor_ = f
.set_label('Floor Name')
.set_placeholder('Unnamed Floor')
.set_width('100%')
.set_maxlength('50')
.set_maxlength(50)
.set_requirements({
'required': true
})
@ -354,26 +354,41 @@ beestat.component.card.floor_plan_editor.prototype.decorate_info_pane_floor_ = f
div = $.createElement('div');
grid.appendChild(div);
const elevation_input = new beestat.component.input.text()
.set_label('Elevation (feet)')
.set_placeholder(this.state_.active_group.elevation / 12)
.set_value(this.state_.active_group.elevation / 12 || '')
.set_label('Elevation (' + beestat.setting('units.distance') + ')')
.set_placeholder(beestat.distance({
'distance': this.state_.active_group.elevation,
'round': 2
}))
.set_value(beestat.distance({
'distance': this.state_.active_group.elevation,
'round': 2
}) || '')
.set_width('100%')
.set_maxlength('5')
.set_maxlength(5)
.set_requirements({
'type': 'integer',
'min_value': -50,
'max_value': 50,
'type': 'decimal',
'min_value': beestat.distance(-600),
'max_value': beestat.distance(600),
'required': true
})
.set_transform({
'type': 'round',
'decimals': 2
})
.render(div);
elevation_input.addEventListener('change', function() {
if (elevation_input.meets_requirements() === true) {
self.state_.active_group.elevation = elevation_input.get_value() * 12;
self.state_.active_group.elevation = beestat.distance({
'distance': elevation_input.get_value(),
'input_distance_unit': beestat.setting('units.distance'),
'output_distance_unit': 'in',
'round': 2
});
self.update_floor_plan_();
self.rerender();
} else {
elevation_input.set_value(self.state_.active_group.elevation / 12, false);
elevation_input.set_value(beestat.distance(self.state_.active_group.elevation), false);
new beestat.component.modal.floor_plan_elevation_help().render();
}
});
@ -382,21 +397,36 @@ beestat.component.card.floor_plan_editor.prototype.decorate_info_pane_floor_ = f
div = $.createElement('div');
grid.appendChild(div);
const height_input = new beestat.component.input.text()
.set_label('Ceiling Height (feet)')
.set_placeholder(this.state_.active_group.height / 12)
.set_value(this.state_.active_group.height / 12 || '')
.set_label('Ceiling Height (' + beestat.setting('units.distance') + ')')
.set_placeholder(beestat.distance({
'distance': this.state_.active_group.height,
'round': 2
}))
.set_value(beestat.distance({
'distance': this.state_.active_group.height,
'round': 2
}) || '')
.set_width('100%')
.set_maxlength('4')
.set_maxlength(5)
.set_requirements({
'type': 'integer',
'min_value': 1,
'type': 'decimal',
'min_value': beestat.distance(60),
'required': true
})
.set_transform({
'type': 'round',
'decimals': 2
})
.render(div);
height_input.addEventListener('change', function() {
if (height_input.meets_requirements() === true) {
self.state_.active_group.height = height_input.get_value() * 12;
self.state_.active_group.height = beestat.distance({
'distance': height_input.get_value(),
'input_distance_unit': beestat.setting('units.distance'),
'output_distance_unit': 'in',
'round': 2
});
self.update_floor_plan_();
} else {
height_input.set_value(self.state_.active_group.height, false);
@ -433,7 +463,7 @@ beestat.component.card.floor_plan_editor.prototype.decorate_info_pane_room_ = fu
.set_label('Room Name')
.set_placeholder('Unnamed Room')
.set_width('100%')
.set_maxlength('50')
.set_maxlength(50)
.set_requirements({
'required': true
})
@ -456,21 +486,36 @@ beestat.component.card.floor_plan_editor.prototype.decorate_info_pane_room_ = fu
div = $.createElement('div');
grid.appendChild(div);
const elevation_input = new beestat.component.input.text()
.set_label('Elevation (feet)')
.set_placeholder(this.state_.active_group.elevation / 12)
.set_value(this.state_.active_room_entity.get_room().elevation / 12 || '')
.set_label('Elevation (' + beestat.setting('units.distance') + ')')
.set_placeholder(beestat.distance({
'distance': this.state_.active_group.elevation,
'round': 2
}))
.set_value(beestat.distance({
'distance': this.state_.active_room_entity.get_room().elevation,
'round': 2
}) || '')
.set_width('100%')
.set_maxlength('5')
.set_maxlength(5)
.set_requirements({
'min_value': -50,
'max_value': 50,
'type': 'integer'
'type': 'decimal',
'min_value': beestat.distance(-600),
'max_value': beestat.distance(600)
})
.set_transform({
'type': 'round',
'decimals': 2
})
.render(div);
elevation_input.addEventListener('change', function() {
if (elevation_input.meets_requirements() === true) {
self.state_.active_room_entity.get_room().elevation = elevation_input.get_value() * 12;
self.state_.active_room_entity.get_room().elevation = beestat.distance({
'distance': elevation_input.get_value(),
'input_distance_unit': beestat.setting('units.distance'),
'output_distance_unit': 'in',
'round': 2
});
self.update_floor_plan_();
self.rerender();
} else {
@ -483,20 +528,35 @@ beestat.component.card.floor_plan_editor.prototype.decorate_info_pane_room_ = fu
div = $.createElement('div');
grid.appendChild(div);
const height_input = new beestat.component.input.text()
.set_label('Ceiling Height (feet)')
.set_placeholder(this.state_.active_group.height / 12)
.set_value(this.state_.active_room_entity.get_room().height / 12 || '')
.set_label('Ceiling Height (' + beestat.setting('units.distance') + ')')
.set_placeholder(beestat.distance({
'distance': this.state_.active_group.height,
'round': 2
}))
.set_value(beestat.distance({
'distance': this.state_.active_room_entity.get_room().height,
'round': 2
}) || '')
.set_width('100%')
.set_maxlength('4')
.set_maxlength(5)
.set_requirements({
'type': 'integer',
'min_value': 1
'type': 'decimal',
'min_value': beestat.distance(60)
})
.set_transform({
'type': 'round',
'decimals': 2
})
.render(div);
height_input.addEventListener('change', function() {
if (height_input.meets_requirements() === true) {
self.state_.active_room_entity.get_room().height = height_input.get_value() * 12;
self.state_.active_room_entity.get_room().height = beestat.distance({
'distance': height_input.get_value(),
'input_distance_unit': beestat.setting('units.distance'),
'output_distance_unit': 'in',
'round': 2
});
self.update_floor_plan_();
} else {
height_input.set_value('', false);

View File

@ -186,7 +186,7 @@ beestat.component.card.my_home.prototype.decorate_property_ = function(parent) {
.set_background_color(beestat.style.color.purple.base)
.set_text_color('#fff')
.set_icon('view_quilt')
.set_text(Number(thermostat.property.square_feet).toLocaleString() + ' sqft'));
.set_text(Number(thermostat.property.square_feet).toLocaleString() + ' ft²'));
}
if (thermostat.property.age !== null) {

View File

@ -16,6 +16,73 @@ beestat.component.card.settings.prototype.decorate_contents_ = function(parent)
beestat.setting('thermostat_id')
];
/**
* Units
*/
parent.appendChild(
$.createElement('p')
.style('font-weight', '400')
.innerText('Units')
);
// Temperature
parent.appendChild(
$.createElement('p')
.innerText('Temperature')
);
const temperature_radio_group = new beestat.component.radio_group()
.set_arrangement('horizontal');
[
'°F',
'°C'
].forEach(function(temperature_unit) {
temperature_radio_group.add_radio(
new beestat.component.input.radio()
.set_label(temperature_unit)
.set_value(temperature_unit)
.set_checked(beestat.setting('units.temperature') === temperature_unit)
);
});
temperature_radio_group.addEventListener('change', function() {
beestat.setting('units.temperature', temperature_radio_group.get_value());
});
temperature_radio_group.render(parent);
// Distance
parent.appendChild(
$.createElement('p')
.innerText('Distance / Area')
);
const distance_radio_group = new beestat.component.radio_group()
.set_arrangement('horizontal');
[
'ft',
'm'
].forEach(function(distance_unit) {
distance_radio_group.add_radio(
new beestat.component.input.radio()
.set_label(distance_unit + ' / ' + distance_unit + '²')
.set_value(distance_unit)
.set_checked(beestat.setting('units.distance') === distance_unit)
);
});
distance_radio_group.addEventListener('change', function() {
beestat.setting({
'units.distance': distance_radio_group.get_value(),
'units.area': distance_radio_group.get_value() + '²'
});
});
distance_radio_group.render(parent);
/**
* Thermosat Summary
*/
parent.appendChild(
$.createElement('p')
.style('font-weight', '400')
@ -58,6 +125,9 @@ beestat.component.card.settings.prototype.decorate_contents_ = function(parent)
);
});
/**
* Temperature Profiles
*/
parent.appendChild(
$.createElement('p')
.style({

View File

@ -144,7 +144,7 @@ beestat.component.card.temperature_profiles.prototype.get_data_ = function() {
*/
var increment;
var fixed;
if (beestat.setting('temperature_unit') === '°F') {
if (beestat.setting('units.temperature') === '°F') {
increment = 1;
fixed = 0;
} else {

View File

@ -625,7 +625,7 @@ beestat.component.card.three_d.prototype.decorate_legend_ = function(parent) {
if (beestat.setting('visualize.data_type') === 'temperature') {
min = beestat.temperature(min);
max = beestat.temperature(max);
units = beestat.setting('temperature_unit');
units = beestat.setting('units.temperature');
} else {
min *= 100;
max *= 100;

View File

@ -176,6 +176,10 @@ beestat.component.card.visualize_settings.prototype.decorate_heat_map_type_ = fu
'type': type,
'required': true
})
.set_transform({
'type': 'round',
'decimals': 1
})
.set_value(
beestat.temperature(beestat.setting(
'visualize.heat_map_absolute.' + beestat.setting('visualize.data_type') + '.min'
@ -184,15 +188,11 @@ beestat.component.card.visualize_settings.prototype.decorate_heat_map_type_ = fu
.set_width(50);
min.addEventListener('change', function() {
if (min.meets_requirements() === true) {
// Round to one decimal.
const value = Math.round(min.get_value() * 10) / 10;
min.set_value(value, false);
beestat.setting(
'visualize.heat_map_absolute.' + beestat.setting('visualize.data_type') + '.min',
beestat.temperature({
'temperature': value,
'input_temperature_unit': beestat.setting('temperature_unit'),
'temperature': min.get_value(),
'input_temperature_unit': beestat.setting('units.temperature'),
'output_temperature_unit': '°F'
})
);
@ -230,7 +230,7 @@ beestat.component.card.visualize_settings.prototype.decorate_heat_map_type_ = fu
'visualize.heat_map_absolute.' + beestat.setting('visualize.data_type') + '.max',
beestat.temperature({
'temperature': max.get_value(),
'input_temperature_unit': beestat.setting('temperature_unit'),
'input_temperature_unit': beestat.setting('units.temperature'),
'output_temperature_unit': '°F'
})
);
@ -268,7 +268,7 @@ beestat.component.card.visualize_settings.prototype.decorate_heat_map_type_ = fu
span = document.createElement('span');
switch (beestat.setting('visualize.data_type')) {
case 'temperature':
span.innerText = beestat.setting('temperature_unit');
span.innerText = beestat.setting('units.temperature');
break;
case 'occupancy':
span.innerText = '%';

View File

@ -118,7 +118,7 @@ beestat.component.chart.runtime_sensor_detail_temperature.prototype.get_options_
'labels': {
'style': {'color': beestat.style.color.gray.base},
'formatter': function() {
return this.value + beestat.setting('temperature_unit');
return this.value + beestat.setting('units.temperature');
}
}
}
@ -230,7 +230,7 @@ beestat.component.chart.runtime_sensor_detail_temperature.prototype.get_options_
} else {
value = beestat.temperature({
'temperature': point.value,
'input_temperature_unit': beestat.setting('temperature_unit'),
'input_temperature_unit': beestat.setting('units.temperature'),
'units': true
});
point_value = point.value;

View File

@ -131,7 +131,7 @@ beestat.component.chart.runtime_thermostat_detail_temperature.prototype.get_opti
'labels': {
'style': {'color': beestat.style.color.gray.base},
'formatter': function() {
return this.value + beestat.setting('temperature_unit');
return this.value + beestat.setting('units.temperature');
}
}
},
@ -288,7 +288,7 @@ beestat.component.chart.runtime_thermostat_detail_temperature.prototype.get_opti
value = beestat.temperature({
'temperature': value,
'input_temperature_unit': beestat.setting('temperature_unit'),
'input_temperature_unit': beestat.setting('units.temperature'),
'units': true
});
} else if (point.series_code.includes('humidity') === true) {

View File

@ -187,7 +187,7 @@ beestat.component.chart.runtime_thermostat_summary.prototype.get_options_yAxis_
'color': beestat.style.color.gray.base
},
'formatter': function() {
return this.value + beestat.setting('temperature_unit');
return this.value + beestat.setting('units.temperature');
}
}
}
@ -234,14 +234,14 @@ beestat.component.chart.runtime_thermostat_summary.prototype.get_options_tooltip
) {
value = beestat.temperature({
'temperature': values.min_outdoor_temperature,
'input_temperature_unit': beestat.setting('temperature_unit'),
'input_temperature_unit': beestat.setting('units.temperature'),
'units': true,
'round': 0
});
value += ' to ';
value += beestat.temperature({
'temperature': values.max_outdoor_temperature,
'input_temperature_unit': beestat.setting('temperature_unit'),
'input_temperature_unit': beestat.setting('units.temperature'),
'units': true,
'round': 0
});
@ -252,7 +252,7 @@ beestat.component.chart.runtime_thermostat_summary.prototype.get_options_tooltip
color = point.series.color;
value = beestat.temperature({
'temperature': values.avg_outdoor_temperature,
'input_temperature_unit': beestat.setting('temperature_unit'),
'input_temperature_unit': beestat.setting('units.temperature'),
'units': true,
'round': 0
});

View File

@ -17,7 +17,7 @@ beestat.extend(beestat.component.chart.temperature_profiles, beestat.component.c
*/
beestat.component.chart.temperature_profiles.prototype.get_options_xAxis_labels_formatter_ = function() {
return function() {
return this.value + beestat.setting('temperature_unit');
return this.value + beestat.setting('units.temperature');
};
};
@ -261,7 +261,7 @@ beestat.component.chart.temperature_profiles.prototype.get_options_yAxis_ = func
'labels': {
'style': {'color': beestat.style.color.gray.base},
'formatter': function() {
return this.value + beestat.setting('temperature_unit');
return this.value + beestat.setting('units.temperature');
}
},
'min': y_min,
@ -296,7 +296,7 @@ beestat.component.chart.temperature_profiles.prototype.get_options_tooltip_forma
var value = beestat.temperature({
'temperature': point.y,
'units': true,
'input_temperature_unit': beestat.setting('temperature_unit'),
'input_temperature_unit': beestat.setting('units.temperature'),
'delta': true,
'type': 'string'
}) + ' / h';
@ -321,7 +321,7 @@ beestat.component.chart.temperature_profiles.prototype.get_options_tooltip_forma
'temperature': this.x,
'round': 0,
'units': true,
'input_temperature_unit': beestat.setting('temperature_unit')
'input_temperature_unit': beestat.setting('units.temperature')
}),
sections
);
@ -380,7 +380,7 @@ beestat.component.chart.temperature_profiles.prototype.get_options_xAxis_ = func
'useHTML': true,
'text': 'Now: ' + beestat.temperature({
'temperature': this.data_.metadata.chart.outdoor_temperature,
'input_temperature_unit': beestat.setting('temperature_unit'),
'input_temperature_unit': beestat.setting('units.temperature'),
'units': true,
'round': 0
})

View File

@ -657,14 +657,20 @@ beestat.component.floor_plan.prototype.update_infobox = function() {
if (this.state_.active_room_entity !== undefined) {
parts.push(this.state_.active_room_entity.get_room().name || 'Unnamed Room');
parts.push(
beestat.floor_plan.get_area_room(this.state_.active_room_entity.get_room())
.toLocaleString() + ' sqft'
beestat.area({
'area': beestat.floor_plan.get_area_room(this.state_.active_room_entity.get_room()),
'round': 0,
'units': true
})
);
} else {
parts.push(this.state_.active_group.name || 'Unnamed Floor');
parts.push(
beestat.floor_plan.get_area_group(this.state_.active_group)
.toLocaleString() + ' sqft'
beestat.area({
'area': beestat.floor_plan.get_area_group(this.state_.active_group),
'round': 0,
'units': true
})
);
}
this.infobox_container_.innerText(parts.join(' • '));

View File

@ -46,9 +46,6 @@ beestat.component.floor_plan_entity.wall.prototype.decorate_line_ = function(par
this.path_.addEventListener('mousedown', function() {
self.dispatchEvent('mousedown');
});
// this.path_.addEventListener('touchstart', function() {
// self.dispatchEvent('mousedown');
// });
this.decorate_text_(parent);
@ -236,14 +233,24 @@ beestat.component.floor_plan_entity.wall.prototype.update_text_ = function() {
this.text_.style.fontSize = '11px';
}
const length_feet = Math.floor(length / 12);
const length_inches = length % 12;
let length_string;
if (beestat.setting('units.distance') === 'ft') {
const length_feet = Math.floor(length / 12);
const length_inches = length % 12;
let length_parts = [];
length_parts.push(length_feet + '\'');
length_parts.push(length_inches + '"');
let length_parts = [];
length_parts.push(length_feet + '\'');
length_parts.push(length_inches + '"');
length_string = length_parts.join(' ');
} else {
length_string = beestat.distance({
'distance': length,
'units': true,
'round': 2
});
}
const length_string = length_parts.join(' ');
this.text_path_.textContent = length_string;
};

View File

@ -80,7 +80,7 @@ beestat.component.input.prototype.meets_requirements = function() {
this.requirements_.regexp = /^-?\d+$/;
break;
case 'decimal':
this.requirements_.regexp = /^-?\d+(?:\.\d+)?$/;
this.requirements_.regexp = /^-?\d*(?:\.\d+)?$/;
break;
}

View File

@ -36,7 +36,7 @@ beestat.component.input.checkbox.prototype.decorate_ = function(parent) {
const span = document.createElement('span');
span.style.cursor = 'pointer';
span.style.marginLeft = (beestat.style.size.gutter / 2) + 'px';
span.style.paddingLeft = (beestat.style.size.gutter / 4) + 'px';
span.innerText = this.label_;
span.addEventListener('click', function() {
self.input_.click();

View File

@ -37,7 +37,7 @@ beestat.component.input.radio.prototype.decorate_ = function(parent) {
const span = document.createElement('span');
span.style.cursor = 'pointer';
span.style.marginLeft = (beestat.style.size.gutter / 2) + 'px';
span.style.paddingLeft = (beestat.style.size.gutter / 4) + 'px';
span.innerText = this.label_;
span.addEventListener('click', function() {
self.input_.click();

View File

@ -19,6 +19,22 @@ beestat.component.input.text = function() {
});
this.input_.addEventListener('change', function() {
if (
self.transform_ !== undefined &&
self.meets_requirements() === true
) {
switch (self.transform_.type) {
case 'round':
// If the value is a number, then round it.
if (new RegExp(/^[\d\.]+$/).test(self.input_.value) === true) {
self.input_.value = Math.round(
self.input_.value * 10 ** self.transform_.decimals
) / 10 ** self.transform_.decimals;
}
break;
}
}
self.dispatchEvent('change');
});
@ -212,3 +228,16 @@ beestat.component.input.text.prototype.set_maxlength = function(maxlength) {
return this;
};
/**
* Set the auto format properties.
*
* @param {object} transform
*
* @return {beestat.component.input.text} This.
*/
beestat.component.input.text.prototype.set_transform = function(transform) {
this.transform_ = transform;
return this;
};

View File

@ -20,7 +20,7 @@ beestat.component.metric.balance_point.prototype.is_temperature_ = true;
* @return {string} The units for this metric.
*/
beestat.component.metric.balance_point.prototype.get_units_ = function() {
return beestat.setting('temperature_unit');
return beestat.setting('units.temperature');
};
/**

View File

@ -22,7 +22,7 @@ beestat.component.metric.setback.prototype.is_temperature_delta_ = true;
* @return {string} The units for this metric.
*/
beestat.component.metric.setback.prototype.get_units_ = function() {
return beestat.setting('temperature_unit');
return beestat.setting('units.temperature');
};
/**

View File

@ -20,7 +20,7 @@ beestat.component.metric.setpoint.prototype.is_temperature_ = true;
* @return {string} The units for this metric.
*/
beestat.component.metric.setpoint.prototype.get_units_ = function() {
return beestat.setting('temperature_unit');
return beestat.setting('units.temperature');
};
/**

View File

@ -92,27 +92,34 @@ beestat.component.modal.create_floor_plan.prototype.decorate_contents_ = functio
}
// Ceiling height
(new beestat.component.title('How tall are your ceilings (feet)?')).render(parent);
(new beestat.component.title('How tall are your ceilings (' + beestat.setting('units.distance') + ')?')).render(parent);
const height_input = new beestat.component.input.text()
const ceiling_height_input = new beestat.component.input.text()
.set_icon('arrow_expand_vertical')
.set_maxlength(2)
.set_maxlength(5)
.set_transform({
'type': 'round',
'decimals': 2
})
.set_requirements({
'min_value': 1,
'type': 'integer',
'min_value': beestat.distance(60),
'type': 'decimal',
'required': true
})
.render(parent);
height_input.addEventListener('change', function() {
self.state_.height = height_input.get_value();
self.state_.error.height = !height_input.meets_requirements();
ceiling_height_input.addEventListener('change', function() {
self.state_.ceiling_height = ceiling_height_input.get_value();
self.state_.error.height = !ceiling_height_input.meets_requirements();
});
if (self.state_.height !== undefined) {
height_input.set_value(self.state_.height);
if (self.state_.ceiling_height !== undefined) {
ceiling_height_input.set_value(self.state_.ceiling_height);
} else if (self.state_.error.height !== true) {
height_input.set_value(8);
ceiling_height_input.set_value(beestat.distance({
'distance': 96,
'round': 2
}));
}
// Address
@ -203,6 +210,13 @@ beestat.component.modal.create_floor_plan.prototype.get_buttons_ = function() {
return;
}
const ceiling_height = beestat.distance({
'distance': self.state_.ceiling_height,
'input_distance_unit': beestat.setting('units.distance'),
'output_distance_unit': 'in',
'round': 2
});
const attributes = {
'name': self.state_.name
};
@ -212,7 +226,7 @@ beestat.component.modal.create_floor_plan.prototype.get_buttons_ = function() {
attributes.data = {
'groups': []
};
let elevation = (self.state_.basement === true) ? (self.state_.height * -12) : 0;
let elevation = (self.state_.basement === true) ? (ceiling_height * -1) : 0;
let floor = (self.state_.basement === true) ? 0 : 1;
const ordinals = [
'First',
@ -229,12 +243,12 @@ beestat.component.modal.create_floor_plan.prototype.get_buttons_ = function() {
attributes.data.groups.push({
'name': floor === 0 ? 'Basement' : (ordinals[floor - 1] + ' Floor'),
'elevation': elevation,
'height': self.state_.height * 12,
'height': ceiling_height,
'rooms': []
});
floor++;
elevation += (self.state_.height * 12);
elevation += (ceiling_height);
}
new beestat.api()

View File

@ -3,7 +3,7 @@
*/
beestat.component.radio_group = function() {
this.radios_ = [];
this.name_ = Math.random();
this.name_ = window.crypto.randomUUID();
beestat.component.apply(this, arguments);
};
beestat.extend(beestat.component.radio_group, beestat.component);
@ -16,33 +16,42 @@ beestat.extend(beestat.component.radio_group, beestat.component);
beestat.component.radio_group.prototype.decorate_ = function(parent) {
const self = this;
const container = $.createElement('div');
container.style('margin-bottom', beestat.style.size.gutter);
// Outer container
const container = document.createElement('div');
if (this.arrangement_ === 'horizontal') {
Object.assign(container.style, {
'display': 'flex',
'grid-gap': `${beestat.style.size.gutter}px`
});
}
parent.appendChild(container);
// Radios
this.radios_.forEach(function(radio) {
radio.set_name('name', this.name_);
radio.set_name(self.name_);
radio.addEventListener('change', function() {
self.value_ = radio.get_value();
self.dispatchEvent('change');
});
radio.render(container);
radio.render($(container));
});
parent.appendChild(container);
};
/**
* Add a radio to this group.
*
* @param {beestat.component.radio} radio The radio to add.
*
* @return {beestat.component.radio_group}
*/
beestat.component.radio_group.prototype.add_radio = function(radio) {
this.radios_.push(radio);
if (this.rendered_ === true) {
this.rerender();
}
return this;
};
/**
@ -69,3 +78,16 @@ beestat.component.radio_group.prototype.get_value = function() {
return null;
};
/**
* Set the arrangement of the radio buttons in the group.
*
* @param {string} arrangement horizontal|vertical
*
* @return {beestat.component.radio_group}
*/
beestat.component.radio_group.prototype.set_arrangement = function(arrangement) {
this.arrangement_ = arrangement;
return this;
};

View File

@ -2,6 +2,7 @@
* A tile representing a floor plan.
*
* @param {integer} floor_plan_id
*
*/
beestat.component.tile.floor_plan = function(floor_plan_id) {
this.floor_plan_id_ = floor_plan_id;
@ -30,7 +31,13 @@ beestat.component.tile.floor_plan.prototype.get_text_ = function() {
const line_2_parts = [];
let floor_count = floor_plan.data.groups.length;
line_2_parts.push(floor_count + (floor_count === 1 ? ' Floor' : ' Floors'));
line_2_parts.push(beestat.floor_plan.get_area(this.floor_plan_id_).toLocaleString() + ' sqft');
line_2_parts.push(
beestat.area({
'area': beestat.floor_plan.get_area(this.floor_plan_id_),
'round': 0,
'units': true
})
);
return [
floor_plan.name,

View File

@ -28,7 +28,13 @@ beestat.component.tile.floor_plan_group.prototype.get_text_ = function() {
const line_2_parts = [];
let room_count = this.floor_plan_group_.rooms.length;
line_2_parts.push(room_count + (room_count === 1 ? ' Room' : ' Rooms'));
line_2_parts.push(beestat.floor_plan.get_area_group(this.floor_plan_group_).toLocaleString() + ' sqft');
line_2_parts.push(
beestat.area({
'area': beestat.floor_plan.get_area_group(this.floor_plan_group_),
'round': 0,
'units': true
})
);
return [
this.floor_plan_group_.name,

View File

@ -16,6 +16,7 @@ if($setting->get('environment') === 'dev' || $setting->get('environment') === 'd
echo '<script src="/js/lib/threejs/threejs.js"></script>' . PHP_EOL;
echo '<script src="/js/lib/suncalc/suncalc.js"></script>' . PHP_EOL;
echo '<script src="/js/lib/clipper/clipper.js"></script>' . PHP_EOL;
echo '<script src="/js/lib/polylabel/polylabel.js"></script>' . PHP_EOL;
// Beestat
echo '<script src="/js/beestat.js"></script>' . PHP_EOL;
@ -28,6 +29,8 @@ if($setting->get('environment') === 'dev' || $setting->get('environment') === 'd
echo '<script src="/js/beestat/api.js"></script>' . PHP_EOL;
echo '<script src="/js/beestat/error.js"></script>' . PHP_EOL;
echo '<script src="/js/beestat/temperature.js"></script>' . PHP_EOL;
echo '<script src="/js/beestat/distance.js"></script>' . PHP_EOL;
echo '<script src="/js/beestat/area.js"></script>' . PHP_EOL;
echo '<script src="/js/beestat/time.js"></script>' . PHP_EOL;
echo '<script src="/js/beestat/setting.js"></script>' . PHP_EOL;
echo '<script src="/js/beestat/poll.js"></script>' . PHP_EOL;

View File

@ -170,8 +170,38 @@ beestat.layer.load.prototype.decorate_ = function(parent) {
document.title = 'beestat';
}
// Set the active temperature unit.
beestat.setting('temperature_unit', thermostat.temperature_unit);
// Set the temperature unit if it hasn't been set before.
if (beestat.setting('units.temperature') === undefined) {
beestat.setting('units.temperature', thermostat.temperature_unit);
}
// Set the distance/area units if they hasn't been set before.
const imperial_countries = [
'USA',
'CAN'
];
if (
beestat.setting('units.distance') === undefined &&
thermostat.address_id !== null &&
beestat.address.is_valid(thermostat.address_id) === true
) {
const address = beestat.cache.address[thermostat.address_id];
beestat.setting(
'units.distance',
imperial_countries.includes(address.normalized.components.country_iso_3) === true ? 'ft' : 'm'
);
}
if (
beestat.setting('units.area') === undefined &&
thermostat.address_id !== null &&
beestat.address.is_valid(thermostat.address_id) === true
) {
const address = beestat.cache.address[thermostat.address_id];
beestat.setting(
'units.area',
imperial_countries.includes(address.normalized.components.country_iso_3) === true ? 'ft²' : 'm²'
);
}
// Rename series if there are multiple stages.
if (beestat.thermostat.get_stages(thermostat.thermostat_id, 'heat') > 1) {

View File

@ -0,0 +1,3 @@
/* eslint-disable */
!function(a){"object"==typeof exports&&"undefined"!=typeof module?module.exports=a():"function"==typeof define&&define.amd?define([],a):("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).polylabel=a()}(function(){return(function d(e,f,b){function c(a,k){if(!f[a]){if(!e[a]){var i="function"==typeof require&&require;if(!k&&i)return i(a,!0);if(g)return g(a,!0);var j=new Error("Cannot find module '"+a+"'");throw j.code="MODULE_NOT_FOUND",j}var h=f[a]={exports:{}};e[a][0].call(h.exports,function(b){return c(e[a][1][b]||b)},h,h.exports,d,e,f,b)}return f[a].exports}for(var g="function"==typeof require&&require,a=0;a<b.length;a++)c(b[a]);return c})({1:[function(a,b,c){"use strict";var d=a("tinyqueue");function e(a,b){return b.max-a.max}function f(a,b,c,d){this.x=a,this.y=b,this.h=c,this.d=g(a,b,d),this.max=this.d+this.h*Math.SQRT2}function g(j,b,k){for(var d=!1,e=1/0,f=0;f<k.length;f++)for(var g=k[f],i=0,l=g.length,m=l-1;i<l;m=i++){var a=g[i],c=g[m];a[1]>b!=c[1]>b&&j<(c[0]-a[0])*(b-a[1])/(c[1]-a[1])+a[0]&&(d=!d),e=Math.min(e,h(j,b,a,c))}return(d?1:-1)*Math.sqrt(e)}function h(g,h,i,e){var c=i[0],d=i[1],a=e[0]-c,b=e[1]-d;if(0!==a||0!==b){var f=((g-c)*a+(h-d)*b)/(a*a+b*b);f>1?(c=e[0],d=e[1]):f>0&&(c+=a*f,d+=b*f)}return(a=g-c)*a+(b=h-d)*b}b.exports=function(c,o,t){o=o||1;for(var k,l,m,n,i=0;i<c[0].length;i++){var g=c[0][i];(!i||g[0]<k)&&(k=g[0]),(!i||g[1]<l)&&(l=g[1]),(!i||g[0]>m)&&(m=g[0]),(!i||g[1]>n)&&(n=g[1])}for(var p=Math.min(m-k,n-l),a=p/2,h=new d(null,e),q=k;q<m;q+=p)for(var r=l;r<n;r+=p)h.push(new f(q+a,r+a,a,c));for(var j=function m(h){for(var c=0,i=0,j=0,d=h[0],e=0,k=d.length,l=k-1;e<k;l=e++){var a=d[e],b=d[l],g=a[0]*b[1]-b[0]*a[1];i+=(a[0]+b[0])*g,j+=(a[1]+b[1])*g,c+=3*g}return new f(i/c,j/c,0,h)}(c),s=h.length;h.length;){var b=h.pop();b.d>j.d&&(j=b,t&&console.log("found best %d after %d probes",Math.round(1e4*b.d)/1e4,s)),b.max-j.d<=o||(a=b.h/2,h.push(new f(b.x-a,b.y-a,a,c)),h.push(new f(b.x+a,b.y-a,a,c)),h.push(new f(b.x-a,b.y+a,a,c)),h.push(new f(b.x+a,b.y+a,a,c)),s+=4)}return t&&(console.log("num probes: "+s),console.log("best distance: "+j.d)),[j.x,j.y]}},{tinyqueue:2}],2:[function(c,b,d){"use strict";function a(b,d){if(!(this instanceof a))return new a(b,d);if(this.data=b||[],this.length=this.data.length,this.compare=d||e,b)for(var c=Math.floor(this.length/2);c>=0;c--)this._down(c)}function e(a,b){return a<b?-1:a>b?1:0}function f(a,b,c){var d=a[b];a[b]=a[c],a[c]=d}b.exports=a,a.prototype={push:function(a){this.data.push(a),this.length++,this._up(this.length-1)},pop:function(){var a=this.data[0];return this.data[0]=this.data[this.length-1],this.length--,this.data.pop(),this._down(0),a},peek:function(){return this.data[0]},_up:function(a){for(var b=this.data,d=this.compare;a>0;){var c=Math.floor((a-1)/2);if(0>d(b[a],b[c]))f(b,c,a),a=c;else break}},_down:function(b){for(var c=this.data,g=this.compare,h=this.length;;){var d=2*b+1,e=d+1,a=b;if(d<h&&0>g(c[d],c[a])&&(a=d),e<h&&0>g(c[e],c[a])&&(a=e),a===b)return;f(c,a,b),b=a}}}},{}]},{},[1])(1)})