diff --git a/js/beestat/floor_plan.js b/js/beestat/floor_plan.js index 829757a..2860c19 100644 --- a/js/beestat/floor_plan.js +++ b/js/beestat/floor_plan.js @@ -1,5 +1,62 @@ beestat.floor_plan = {}; +/** + * Pending save timeouts keyed by floor_plan_id. + * + * @type {Object} + */ +beestat.floor_plan.data_save_timeouts_ = {}; + +/** + * Queue a floor plan data save, debounced per floor plan. + * + * @param {number} floor_plan_id + * @param {number} delay_ms + */ +beestat.floor_plan.queue_data_save_ = function(floor_plan_id, delay_ms = 300) { + if (floor_plan_id === undefined || floor_plan_id === null || floor_plan_id === '') { + return; + } + + const normalized_floor_plan_id = Number(floor_plan_id); + if (Number.isFinite(normalized_floor_plan_id) !== true || normalized_floor_plan_id <= 0) { + return; + } + + let normalized_delay_ms = Number(delay_ms); + if (Number.isFinite(normalized_delay_ms) !== true || normalized_delay_ms < 0) { + normalized_delay_ms = 0; + } + normalized_delay_ms = Math.round(normalized_delay_ms); + + if (beestat.floor_plan.data_save_timeouts_[normalized_floor_plan_id] !== undefined) { + window.clearTimeout(beestat.floor_plan.data_save_timeouts_[normalized_floor_plan_id]); + } + + beestat.floor_plan.data_save_timeouts_[normalized_floor_plan_id] = window.setTimeout(function() { + delete beestat.floor_plan.data_save_timeouts_[normalized_floor_plan_id]; + + const floor_plan = beestat.cache.floor_plan[normalized_floor_plan_id]; + if (floor_plan === undefined || floor_plan.data === undefined) { + return; + } + + new beestat.api() + .add_call( + 'floor_plan', + 'update', + { + 'attributes': { + 'floor_plan_id': normalized_floor_plan_id, + 'data': beestat.clone(floor_plan.data) + } + }, + 'update_floor_plan' + ) + .send(); + }, normalized_delay_ms); +}; + /** * Get the area of an entire floor plan. * diff --git a/js/beestat/setting.js b/js/beestat/setting.js index 37efafd..b9811fb 100644 --- a/js/beestat/setting.js +++ b/js/beestat/setting.js @@ -76,18 +76,12 @@ beestat.setting = function(argument_1, opt_value, opt_callback) { .format('MM/DD/YYYY'), 'visualize.heat_map_values': 'dynamic', 'visualize.heat_map_static.temperature.min': 70, - 'visualize.heat_map_static.temperature.max': 80, - 'visualize.heat_map_static.occupancy.min': 0, - 'visualize.heat_map_static.occupancy.max': 100, - 'visualize.hide_affiliate': false, - 'visualize.three_d.show_labels': true, - 'visualize.three_d.auto_rotate': false, - 'visualize.three_d.show_walls': false, - 'visualize.three_d.show_roof': false, - 'visualize.three_d.show_environment': true, - 'visualize.three_d.weather_mode': 'sunny', - - 'date_format': 'M/D/YYYY', + 'visualize.heat_map_static.temperature.max': 80, + 'visualize.heat_map_static.occupancy.min': 0, + 'visualize.heat_map_static.occupancy.max': 100, + 'visualize.hide_affiliate': false, + + 'date_format': 'M/D/YYYY', 'units.currency': 'usd', diff --git a/js/beestat/weather.js b/js/beestat/weather.js new file mode 100644 index 0000000..0e50ad2 --- /dev/null +++ b/js/beestat/weather.js @@ -0,0 +1,406 @@ +beestat.weather = {}; + +/** + * Unified weather settings keyed by condition. + * + * Field meanings: + * `condition`: Canonical condition string stored in floor-plan scene data. + * `icon`: MDI icon id used by weather UI controls/modals. + * `icon_color`: UI accent color for the weather icon. + * `cloud_density`: Controls cloud particle count/intensity in the scene. + * `cloud_darkness`: Controls cloud shading/dimming (0 clear -> 2 very dark). + * `rain_density`: Controls rain particle count/intensity. + * `snow_density`: Controls snow particle count/intensity and snow-cover blend. + * `lightning_frequency`: Controls frequency/intensity of lightning effects. + * `wind_speed`: Controls animation strength for wind-affected scene elements. + * + * Density/intensity fields are tuned for roughly 0..2 in current scene logic. + */ +beestat.weather.settings_ = { + 'sunny': { + 'condition': 'sunny', + 'icon': 'weather_sunny', + 'icon_color': beestat.style.color.yellow.base, + 'cloud_density': 0.03, + 'cloud_darkness': 0, + 'rain_density': 0, + 'snow_density': 0, + 'lightning_frequency': 0, + 'wind_speed': 0.4 + }, + 'few_clouds': { + 'condition': 'few_clouds', + 'icon': 'weather_partly_cloudy', + 'icon_color': beestat.style.color.gray.base, + 'cloud_density': 0.18, + 'cloud_darkness': 0, + 'rain_density': 0, + 'snow_density': 0, + 'lightning_frequency': 0, + 'wind_speed': 0.45 + }, + 'partly_cloudy': { + 'condition': 'partly_cloudy', + 'icon': 'weather_partly_cloudy', + 'icon_color': beestat.style.color.gray.base, + 'cloud_density': 0.3, + 'cloud_darkness': 0.1, + 'rain_density': 0, + 'snow_density': 0, + 'lightning_frequency': 0, + 'wind_speed': 0.55 + }, + 'mostly_cloudy': { + 'condition': 'mostly_cloudy', + 'icon': 'weather_cloudy', + 'icon_color': beestat.style.color.gray.base, + 'cloud_density': 0.75, + 'cloud_darkness': 0.45, + 'rain_density': 0, + 'snow_density': 0, + 'lightning_frequency': 0, + 'wind_speed': 0.7 + }, + 'overcast': { + 'condition': 'overcast', + 'icon': 'weather_cloudy', + 'icon_color': beestat.style.color.gray.base, + 'cloud_density': 0.5, + 'cloud_darkness': 0.4, + 'rain_density': 0, + 'snow_density': 0, + 'lightning_frequency': 0, + 'wind_speed': 0.8 + }, + 'drizzle': { + 'condition': 'drizzle', + 'icon': 'weather_pouring', + 'icon_color': beestat.style.color.blue.light, + 'cloud_density': 0.9, + 'cloud_darkness': 0.7, + 'rain_density': 0.35, + 'snow_density': 0, + 'lightning_frequency': 0, + 'wind_speed': 0.75 + }, + 'rain': { + 'condition': 'rain', + 'icon': 'weather_pouring', + 'icon_color': beestat.style.color.blue.light, + 'cloud_density': 1, + 'cloud_darkness': 1, + 'rain_density': 1, + 'snow_density': 0, + 'lightning_frequency': 0, + 'wind_speed': 0.8 + }, + 'showers': { + 'condition': 'showers', + 'icon': 'weather_pouring', + 'icon_color': beestat.style.color.blue.light, + 'cloud_density': 1.2, + 'cloud_darkness': 1.1, + 'rain_density': 1.2, + 'snow_density': 0, + 'lightning_frequency': 0, + 'wind_speed': 1 + }, + 'freezing_rain': { + 'condition': 'freezing_rain', + 'icon': 'weather_hail', + 'icon_color': beestat.style.color.lightblue.base, + 'cloud_density': 1.2, + 'cloud_darkness': 1.2, + 'rain_density': 1.1, + 'snow_density': 0.2, + 'lightning_frequency': 0, + 'wind_speed': 1 + }, + 'hail': { + 'condition': 'hail', + 'icon': 'weather_hail', + 'icon_color': beestat.style.color.lightblue.base, + 'cloud_density': 1.25, + 'cloud_darkness': 1.25, + 'rain_density': 1.2, + 'snow_density': 0.15, + 'lightning_frequency': 0.1, + 'wind_speed': 1.1 + }, + 'pellets': { + 'condition': 'pellets', + 'icon': 'weather_hail', + 'icon_color': beestat.style.color.lightblue.base, + 'cloud_density': 1.25, + 'cloud_darkness': 1.25, + 'rain_density': 1.2, + 'snow_density': 0.15, + 'lightning_frequency': 0.1, + 'wind_speed': 1.1 + }, + 'snow': { + 'condition': 'snow', + 'icon': 'weather_snowy', + 'icon_color': beestat.style.color.lightblue.light, + 'cloud_density': 1, + 'cloud_darkness': 1, + 'rain_density': 0, + 'snow_density': 1, + 'lightning_frequency': 0, + 'wind_speed': 0.4 + }, + 'flurries': { + 'condition': 'flurries', + 'icon': 'weather_snowy', + 'icon_color': beestat.style.color.lightblue.light, + 'cloud_density': 0.85, + 'cloud_darkness': 0.7, + 'rain_density': 0, + 'snow_density': 0.55, + 'lightning_frequency': 0, + 'wind_speed': 0.65 + }, + 'freezing_snow': { + 'condition': 'freezing_snow', + 'icon': 'weather_snowy', + 'icon_color': beestat.style.color.lightblue.light, + 'cloud_density': 1.1, + 'cloud_darkness': 1, + 'rain_density': 0.05, + 'snow_density': 1.1, + 'lightning_frequency': 0, + 'wind_speed': 0.7 + }, + 'blizzard': { + 'condition': 'blizzard', + 'icon': 'weather_snowy_heavy', + 'icon_color': beestat.style.color.lightblue.light, + 'cloud_density': 1.4, + 'cloud_darkness': 1.5, + 'rain_density': 0.1, + 'snow_density': 1.8, + 'lightning_frequency': 0, + 'wind_speed': 1.6 + }, + 'thunderstorm': { + 'condition': 'thunderstorm', + 'icon': 'weather_lightning_rainy', + 'icon_color': beestat.style.color.red.base, + 'cloud_density': 1.5, + 'cloud_darkness': 2, + 'rain_density': 2, + 'snow_density': 0, + 'lightning_frequency': 1, + 'wind_speed': 1.6 + }, + 'windy': { + 'condition': 'windy', + 'icon': 'weather_windy', + 'icon_color': beestat.style.color.gray.base, + 'cloud_density': 0.55, + 'cloud_darkness': 0.3, + 'rain_density': 0, + 'snow_density': 0, + 'lightning_frequency': 0, + 'wind_speed': 1.5 + }, + 'tornado': { + 'condition': 'tornado', + 'icon': 'weather_tornado', + 'icon_color': beestat.style.color.gray.base, + 'cloud_density': 1.35, + 'cloud_darkness': 1.6, + 'rain_density': 1.3, + 'snow_density': 0, + 'lightning_frequency': 0.5, + 'wind_speed': 2 + }, + 'fog': { + 'condition': 'fog', + 'icon': 'weather_fog', + 'icon_color': beestat.style.color.gray.base, + 'cloud_density': 0.6, + 'cloud_darkness': 0.2, + 'rain_density': 0, + 'snow_density': 0, + 'lightning_frequency': 0, + 'wind_speed': 0.25 + }, + 'haze': { + 'condition': 'haze', + 'icon': 'weather_hazy', + 'icon_color': beestat.style.color.gray.base, + 'cloud_density': 0.45, + 'cloud_darkness': 0.35, + 'rain_density': 0, + 'snow_density': 0, + 'lightning_frequency': 0, + 'wind_speed': 0.6 + }, + 'smoke': { + 'condition': 'smoke', + 'icon': 'weather_hazy', + 'icon_color': beestat.style.color.gray.base, + 'cloud_density': 0.45, + 'cloud_darkness': 0.35, + 'rain_density': 0, + 'snow_density': 0, + 'lightning_frequency': 0, + 'wind_speed': 0.6 + }, + 'dust': { + 'condition': 'dust', + 'icon': 'weather_hazy', + 'icon_color': beestat.style.color.gray.base, + 'cloud_density': 0.45, + 'cloud_darkness': 0.35, + 'rain_density': 0, + 'snow_density': 0, + 'lightning_frequency': 0, + 'wind_speed': 0.6 + } +}; + +/** + * Fallback settings for unknown conditions. + * Used when a condition string is missing or unsupported. + */ +beestat.weather.default_settings_ = { + 'condition':'sunny', + 'icon': 'cloud_question', + 'icon_color': beestat.style.color.gray.base, + 'cloud_density': 0.03, + 'cloud_darkness': 0, + 'rain_density': 0, + 'snow_density': 0, + 'lightning_frequency': 0, + 'wind_speed': 0.4 +}; + +/** + * Find weather settings by condition. + * Returns a full weather row; falls back to `default_settings_`. + * + * @param {string} condition + */ +beestat.weather.get_settings_ = function(condition) { + if (beestat.weather.settings_[condition] !== undefined) { + return beestat.weather.settings_[condition]; + } + + return beestat.weather.default_settings_; +}; + +/** + * Get UI icon id for a weather condition. + * + * @param {string} condition + * + * @return {string} + */ +beestat.weather.get_icon = function(condition) { + return beestat.weather.get_settings_(condition).icon; +}; + +/** + * Get UI icon color for a weather condition. + * + * @param {string} condition + * + * @return {string} + */ +beestat.weather.get_icon_color = function(condition) { + return beestat.weather.get_settings_(condition).icon_color; +}; + +/** + * Get cloud density for a condition. + * Higher values increase cloud population and perceived overcast coverage. + * + * @param {string} condition + * + * @return {number} + */ +beestat.weather.get_cloud_density = function(condition) { + return beestat.weather.get_settings_(condition).cloud_density; +}; + +/** + * Get cloud darkness for a condition. + * Higher values darken clouds and dim overall sky illumination. + * + * @param {string} condition + * + * @return {number} + */ +beestat.weather.get_cloud_darkness = function(condition) { + return beestat.weather.get_settings_(condition).cloud_darkness; +}; + +/** + * Get rain density for a condition. + * Higher values increase rain particle count and rainfall intensity. + * + * @param {string} condition + * + * @return {number} + */ +beestat.weather.get_rain_density = function(condition) { + return beestat.weather.get_settings_(condition).rain_density; +}; + +/** + * Get snow density for a condition. + * Higher values increase snow particle count and snow-cover blending. + * + * @param {string} condition + * + * @return {number} + */ +beestat.weather.get_snow_density = function(condition) { + return beestat.weather.get_settings_(condition).snow_density; +}; + +/** + * Get lightning frequency for a condition. + * Higher values produce more frequent/intense lightning events. + * + * @param {string} condition + * + * @return {number} + */ +beestat.weather.get_lightning_frequency = function(condition) { + return beestat.weather.get_settings_(condition).lightning_frequency; +}; + +/** + * Get wind speed for a condition. + * Higher values increase wind-driven animation (trees/precipitation behavior). + * + * @param {string} condition + * + * @return {number} + */ +beestat.weather.get_wind_speed = function(condition) { + return beestat.weather.get_settings_(condition).wind_speed; +}; + +/** + * Convert condition into a title-case label. + * + * @param {string} condition + * + * @return {string} + */ +beestat.weather.get_condition_label = function(condition) { + const value = typeof condition === 'string' && condition.length > 0 + ? condition + : 'unknown'; + + return value + .split('_') + .map(function(part) { + return part.charAt(0).toUpperCase() + part.slice(1); + }) + .join(' '); +}; diff --git a/js/component/card/floor_plan_editor.js b/js/component/card/floor_plan_editor.js index 3ef9adf..deccbbf 100644 --- a/js/component/card/floor_plan_editor.js +++ b/js/component/card/floor_plan_editor.js @@ -2563,27 +2563,11 @@ beestat.component.card.floor_plan_editor.prototype.get_title_ = function() { * only run so fast. */ beestat.component.card.floor_plan_editor.prototype.update_floor_plan_ = function() { - const self = this; + const floor_plan_id = beestat.setting('visualize.floor_plan_id'); // 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() - .add_call( - 'floor_plan', - 'update', - { - 'attributes': { - 'floor_plan_id': beestat.setting('visualize.floor_plan_id'), - 'data': self.get_floor_plan_data_(beestat.setting('visualize.floor_plan_id')) - } - }, - 'update_floor_plan' - ) - .send(); - }, 1000); + beestat.floor_plan.queue_data_save_(floor_plan_id, 1000); }; /** diff --git a/js/component/card/three_d.js b/js/component/card/three_d.js index 3dd974d..c662b70 100644 --- a/js/component/card/three_d.js +++ b/js/component/card/three_d.js @@ -86,7 +86,7 @@ beestat.component.card.three_d = function() { this.scene_settings_values_ = undefined; this.scene_settings_scroll_top_ = 0; this.scene_settings_panel_content_ = undefined; - this.scene_visualize_save_timeout_ = undefined; + this.weather_values_ = ['auto', 'sunny', 'overcast', 'rain', 'thunderstorm', 'snow']; beestat.component.card.apply(this, arguments); }; @@ -124,20 +124,6 @@ beestat.component.card.three_d.rerender_required_scene_settings = { 'light_user_enabled': true }; -/** - * Valid persisted weather values for the 3D scene. - * - * @type {!Array} - */ -beestat.component.card.three_d.weather_values = [ - 'auto', - 'sunny', - 'overcast', - 'rain', - 'thunderstorm', - 'snow' -]; - /** * Decorate * @@ -660,27 +646,7 @@ beestat.component.card.three_d.prototype.get_scene_visualize_state_ = function() * Persist current floor plan data after scene-visualize changes. */ beestat.component.card.three_d.prototype.save_scene_visualize_state_ = function() { - const self = this; - window.clearTimeout(this.scene_visualize_save_timeout_); - this.scene_visualize_save_timeout_ = window.setTimeout(function() { - const floor_plan = beestat.cache.floor_plan[self.floor_plan_id_]; - if (floor_plan === undefined || floor_plan.data === undefined) { - return; - } - new beestat.api() - .add_call( - 'floor_plan', - 'update', - { - 'attributes': { - 'floor_plan_id': self.floor_plan_id_, - 'data': beestat.clone(floor_plan.data) - } - }, - 'update_floor_plan' - ) - .send(); - }, 300); + beestat.floor_plan.queue_data_save_(this.floor_plan_id_, 300); }; /** @@ -724,7 +690,7 @@ beestat.component.card.three_d.prototype.get_weather_ = function() { return 'auto'; } const weather = scene_visualize.weather; - if (beestat.component.card.three_d.weather_values.includes(weather) !== true) { + if (this.weather_values_.includes(weather) !== true) { scene_visualize.weather = 'auto'; this.save_scene_visualize_state_(); return 'auto'; @@ -742,7 +708,7 @@ beestat.component.card.three_d.prototype.set_weather_ = function(weather) { if (scene_visualize === null) { return; } - const normalized_weather = beestat.component.card.three_d.weather_values.includes(weather) + const normalized_weather = this.weather_values_.includes(weather) ? weather : 'auto'; scene_visualize.weather = normalized_weather; @@ -756,82 +722,21 @@ beestat.component.card.three_d.prototype.set_weather_ = function(weather) { */ beestat.component.card.three_d.prototype.get_auto_weather_from_thermostat_ = function() { const thermostat = beestat.cache.thermostat[beestat.setting('thermostat_id')]; - const condition = thermostat?.weather?.condition; - switch (condition) { - case 'sunny': - case 'few_clouds': - case 'partly_cloudy': - case 'mostly_cloudy': - case 'overcast': - case 'drizzle': - case 'rain': - case 'showers': - case 'freezing_rain': - case 'hail': - case 'pellets': - case 'snow': - case 'flurries': - case 'freezing_snow': - case 'blizzard': - case 'thunderstorm': - case 'windy': - case 'tornado': - case 'fog': - case 'haze': - case 'smoke': - case 'dust': - return condition; - default: - return 'sunny'; - } + return beestat.weather.get_settings_(thermostat?.weather?.condition).condition; }; /** - * Get weather icon from weather condition using modal weather icon mapping. + * Resolve selected weather mode into a weather condition. * - * @param {string} condition + * @param {string} weather * * @return {string} */ -beestat.component.card.three_d.prototype.get_weather_icon_from_condition_ = function(condition) { - switch (condition) { - case 'sunny': - return 'weather_sunny'; - case 'few_clouds': - case 'partly_cloudy': - return 'weather_partly_cloudy'; - case 'mostly_cloudy': - case 'overcast': - return 'weather_cloudy'; - case 'drizzle': - case 'rain': - case 'showers': - return 'weather_pouring'; - case 'freezing_rain': - case 'hail': - case 'pellets': - return 'weather_hail'; - case 'snow': - case 'flurries': - case 'freezing_snow': - return 'weather_snowy'; - case 'blizzard': - return 'weather_snowy_heavy'; - case 'thunderstorm': - return 'weather_lightning_rainy'; - case 'windy': - return 'weather_windy'; - case 'tornado': - return 'weather_tornado'; - case 'fog': - return 'weather_fog'; - case 'haze': - case 'smoke': - case 'dust': - return 'weather_hazy'; - default: - return 'cloud_question'; +beestat.component.card.three_d.prototype.get_weather_condition_from_mode_ = function(weather) { + if (weather === 'auto') { + return this.get_auto_weather_from_thermostat_(); } + return beestat.weather.get_settings_(weather).condition; }; /** @@ -842,10 +747,7 @@ beestat.component.card.three_d.prototype.get_weather_icon_from_condition_ = func * @return {string} */ beestat.component.card.three_d.prototype.get_weather_icon_from_mode_ = function(weather) { - const condition = weather === 'auto' - ? this.get_auto_weather_from_thermostat_() - : weather; - return this.get_weather_icon_from_condition_(condition); + return beestat.weather.get_icon(this.get_weather_condition_from_mode_(weather)); }; /** @@ -855,11 +757,11 @@ beestat.component.card.three_d.prototype.get_weather_icon_from_mode_ = function( */ beestat.component.card.three_d.prototype.get_weather_mode_tiles_ = function() { return [ - {'value': 'sunny', 'icon': 'weather_sunny', 'title': 'Weather: Sunny'}, - {'value': 'overcast', 'icon': 'weather_cloudy', 'title': 'Weather: Overcast'}, - {'value': 'rain', 'icon': 'weather_pouring', 'title': 'Weather: Rain'}, - {'value': 'thunderstorm', 'icon': 'weather_lightning_rainy', 'title': 'Weather: Thunderstorm'}, - {'value': 'snow', 'icon': 'weather_snowy', 'title': 'Weather: Snow'} + {'value': 'sunny', 'icon': beestat.weather.get_icon('sunny'), 'title': 'Weather: Sunny'}, + {'value': 'overcast', 'icon': beestat.weather.get_icon('overcast'), 'title': 'Weather: Overcast'}, + {'value': 'rain', 'icon': beestat.weather.get_icon('rain'), 'title': 'Weather: Rain'}, + {'value': 'thunderstorm', 'icon': beestat.weather.get_icon('thunderstorm'), 'title': 'Weather: Thunderstorm'}, + {'value': 'snow', 'icon': beestat.weather.get_icon('snow'), 'title': 'Weather: Snow'} ]; }; @@ -962,186 +864,15 @@ beestat.component.card.three_d.prototype.set_show_group_ = function(group_id, vi * }} */ beestat.component.card.three_d.prototype.get_weather_settings_from_weather_ = function(weather) { - const effective_weather = weather === 'auto' - ? this.get_auto_weather_from_thermostat_() - : weather; - switch (effective_weather) { - case 'few_clouds': - return { - 'cloud_density': 0.18, - 'cloud_darkness': 0, - 'rain_density': 0, - 'snow_density': 0, - 'lightning_frequency': 0, - 'wind_speed': 0.45 - }; - case 'partly_cloudy': - return { - 'cloud_density': 0.3, - 'cloud_darkness': 0.1, - 'rain_density': 0, - 'snow_density': 0, - 'lightning_frequency': 0, - 'wind_speed': 0.55 - }; - case 'mostly_cloudy': - return { - 'cloud_density': 0.75, - 'cloud_darkness': 0.45, - 'rain_density': 0, - 'snow_density': 0, - 'lightning_frequency': 0, - 'wind_speed': 0.7 - }; - case 'drizzle': - return { - 'cloud_density': 0.9, - 'cloud_darkness': 0.7, - 'rain_density': 0.35, - 'snow_density': 0, - 'lightning_frequency': 0, - 'wind_speed': 0.75 - }; - case 'showers': - return { - 'cloud_density': 1.2, - 'cloud_darkness': 1.1, - 'rain_density': 1.2, - 'snow_density': 0, - 'lightning_frequency': 0, - 'wind_speed': 1 - }; - case 'freezing_rain': - return { - 'cloud_density': 1.2, - 'cloud_darkness': 1.2, - 'rain_density': 1.1, - 'snow_density': 0.2, - 'lightning_frequency': 0, - 'wind_speed': 1 - }; - case 'hail': - case 'pellets': - return { - 'cloud_density': 1.25, - 'cloud_darkness': 1.25, - 'rain_density': 1.2, - 'snow_density': 0.15, - 'lightning_frequency': 0.1, - 'wind_speed': 1.1 - }; - case 'flurries': - return { - 'cloud_density': 0.85, - 'cloud_darkness': 0.7, - 'rain_density': 0, - 'snow_density': 0.55, - 'lightning_frequency': 0, - 'wind_speed': 0.65 - }; - case 'freezing_snow': - return { - 'cloud_density': 1.1, - 'cloud_darkness': 1, - 'rain_density': 0.05, - 'snow_density': 1.1, - 'lightning_frequency': 0, - 'wind_speed': 0.7 - }; - case 'blizzard': - return { - 'cloud_density': 1.4, - 'cloud_darkness': 1.5, - 'rain_density': 0.1, - 'snow_density': 1.8, - 'lightning_frequency': 0, - 'wind_speed': 1.6 - }; - case 'thunderstorm': - return { - 'cloud_density': 1.5, - 'cloud_darkness': 2, - 'rain_density': 2, - 'snow_density': 0, - 'lightning_frequency': 1, - 'wind_speed': 1.6 - }; - case 'windy': - return { - 'cloud_density': 0.55, - 'cloud_darkness': 0.3, - 'rain_density': 0, - 'snow_density': 0, - 'lightning_frequency': 0, - 'wind_speed': 1.5 - }; - case 'tornado': - return { - 'cloud_density': 1.35, - 'cloud_darkness': 1.6, - 'rain_density': 1.3, - 'snow_density': 0, - 'lightning_frequency': 0.5, - 'wind_speed': 2 - }; - case 'fog': - return { - 'cloud_density': 0.6, - 'cloud_darkness': 0.2, - 'rain_density': 0, - 'snow_density': 0, - 'lightning_frequency': 0, - 'wind_speed': 0.25 - }; - case 'haze': - case 'smoke': - case 'dust': - return { - 'cloud_density': 0.45, - 'cloud_darkness': 0.35, - 'rain_density': 0, - 'snow_density': 0, - 'lightning_frequency': 0, - 'wind_speed': 0.6 - }; - case 'overcast': - return { - 'cloud_density': 0.5, - 'cloud_darkness': 0.4, - 'rain_density': 0, - 'snow_density': 0, - 'lightning_frequency': 0, - 'wind_speed': 0.8 - }; - case 'rain': - return { - 'cloud_density': 1, - 'cloud_darkness': 1, - 'rain_density': 1, - 'snow_density': 0, - 'lightning_frequency': 0, - 'wind_speed': 0.8 - }; - case 'snow': - return { - 'cloud_density': 1, - 'cloud_darkness': 1, - 'rain_density': 0, - 'snow_density': 1, - 'lightning_frequency': 0, - 'wind_speed': 0.4 - }; - case 'sunny': - default: - return { - 'cloud_density': 0.03, - 'cloud_darkness': 0, - 'rain_density': 0, - 'snow_density': 0, - 'lightning_frequency': 0, - 'wind_speed': 0.4 - }; - } + const condition = this.get_weather_condition_from_mode_(weather); + return { + 'cloud_density': beestat.weather.get_cloud_density(condition), + 'cloud_darkness': beestat.weather.get_cloud_darkness(condition), + 'rain_density': beestat.weather.get_rain_density(condition), + 'snow_density': beestat.weather.get_snow_density(condition), + 'lightning_frequency': beestat.weather.get_lightning_frequency(condition), + 'wind_speed': beestat.weather.get_wind_speed(condition) + }; }; /** @@ -2342,7 +2073,7 @@ beestat.component.card.three_d.prototype.decorate_floors_ = function(parent) { const sorted_groups = Object.values(floor_plan.data.groups) .sort(function(a, b) { - return a.elevation > b.elevation; + return (a.elevation || 0) - (b.elevation || 0); }); let icon_number = 1; @@ -2843,10 +2574,6 @@ beestat.component.card.three_d.prototype.force_dispose_stale_instance_ = functio this.rerender_timeout_id_ = undefined; this.rerender_pending_delay_ms_ = undefined; } - if (this.scene_visualize_save_timeout_ !== undefined) { - window.clearTimeout(this.scene_visualize_save_timeout_); - this.scene_visualize_save_timeout_ = undefined; - } this.rerender_waiting_for_visibility_ = false; if (this.visibility_observer_ !== undefined) { this.visibility_observer_.disconnect(); @@ -2870,10 +2597,6 @@ beestat.component.card.three_d.prototype.dispose = function() { this.rerender_timeout_id_ = undefined; this.rerender_pending_delay_ms_ = undefined; } - if (this.scene_visualize_save_timeout_ !== undefined) { - window.clearTimeout(this.scene_visualize_save_timeout_); - this.scene_visualize_save_timeout_ = undefined; - } this.rerender_waiting_for_visibility_ = false; if (this.visibility_observer_ !== undefined) { this.visibility_observer_.disconnect(); diff --git a/js/component/floor_plan.js b/js/component/floor_plan.js index 7d56b1c..283908c 100644 --- a/js/component/floor_plan.js +++ b/js/component/floor_plan.js @@ -804,7 +804,7 @@ beestat.component.floor_plan.prototype.update_toolbar = function() { const sorted_groups = Object.values(floor_plan.data.groups) .sort(function(a, b) { - return a.elevation > b.elevation; + return (a.elevation || 0) - (b.elevation || 0); }); let icon_number = 1; @@ -1709,7 +1709,7 @@ beestat.component.floor_plan.prototype.get_tree_group_ = function() { const floor_plan = beestat.cache.floor_plan[this.floor_plan_id_]; const sorted_groups = Object.values(floor_plan.data.groups) .sort(function(a, b) { - return a.elevation > b.elevation; + return (a.elevation || 0) - (b.elevation || 0); }); const first_floor = sorted_groups.find(function(group) { diff --git a/js/component/modal/update_floor_plan.js b/js/component/modal/update_floor_plan.js index 2c6de87..1a19d84 100644 --- a/js/component/modal/update_floor_plan.js +++ b/js/component/modal/update_floor_plan.js @@ -67,7 +67,7 @@ beestat.component.modal.update_floor_plan.prototype.decorate_contents_ = functio const sorted_groups = Object.values(floor_plan.data.groups) .sort(function(a, b) { - return a.elevation > b.elevation; + return (a.elevation || 0) - (b.elevation || 0); }); sorted_groups.forEach(function(group) { diff --git a/js/component/modal/weather.js b/js/component/modal/weather.js index 04f3069..d6850e4 100755 --- a/js/component/modal/weather.js +++ b/js/component/modal/weather.js @@ -15,78 +15,12 @@ beestat.component.modal.weather = function() { }; beestat.extend(beestat.component.modal.weather, beestat.component.modal); -beestat.component.modal.weather.prototype.decorate_contents_ = function(parent) { - var thermostat = beestat.cache.thermostat[beestat.setting('thermostat_id')]; - - var icon; - var icon_color; - switch (thermostat.weather.condition) { - case 'sunny': - icon = 'weather_sunny'; - icon_color = beestat.style.color.yellow.base; - break; - case 'few_clouds': - case 'partly_cloudy': - icon = 'weather_partly_cloudy'; - icon_color = beestat.style.color.gray.base; - break; - case 'mostly_cloudy': - case 'overcast': - icon = 'weather_cloudy'; - icon_color = beestat.style.color.gray.base; - break; - case 'drizzle': - case 'rain': - case 'showers': - icon = 'weather_pouring'; - icon_color = beestat.style.color.blue.light; - break; - case 'freezing_rain': - case 'hail': - case 'pellets': - icon_color = beestat.style.color.lightblue.base; - icon = 'weather_hail'; - break; - case 'snow': - case 'flurries': - case 'freezing_snow': - icon_color = beestat.style.color.lightblue.light; - icon = 'weather_snowy'; - break; - case 'blizzard': - icon = 'weather_snowy_heavy'; - icon_color = beestat.style.color.lightblue.light; - break; - case 'thunderstorm': - icon = 'weather_lightning_rainy'; - icon_color = beestat.style.color.red.base; - break; - case 'windy': - icon = 'weather_windy'; - icon_color = beestat.style.color.gray.base; - break; - case 'tornado': - icon = 'weather_tornado'; - icon_color = beestat.style.color.gray.base; - break; - case 'fog': - icon = 'weather_fog'; - icon_color = beestat.style.color.gray.base; - break; - case 'haze': - case 'smoke': - case 'dust': - icon = 'weather_hazy'; - icon_color = beestat.style.color.gray.base; - break; - default: - icon = 'cloud_question'; - icon_color = beestat.style.color.gray.base; - break; - } - - var condition = thermostat.weather.condition.replace('_', ' '); - condition = condition.charAt(0).toUpperCase() + condition.slice(1); +beestat.component.modal.weather.prototype.decorate_contents_ = function(parent) { + var thermostat = beestat.cache.thermostat[beestat.setting('thermostat_id')]; + + var icon = beestat.weather.get_icon(thermostat.weather.condition); + var icon_color = beestat.weather.get_icon_color(thermostat.weather.condition); + var condition = beestat.weather.get_condition_label(thermostat.weather.condition); var tr; var td; diff --git a/js/component/scene/weather.js b/js/component/scene/weather.js index 0ec4894..a4820ae 100644 --- a/js/component/scene/weather.js +++ b/js/component/scene/weather.js @@ -15,206 +15,16 @@ beestat.component.scene.prototype.set_weather = function(weather) { if (floor_plan.data.appearance === undefined) { floor_plan.data.appearance = {}; } - floor_plan.data.appearance.weather = weather; - - // Translate weather presets to scene density values. - let weather_settings; - switch (weather) { - case 'few_clouds': - weather_settings = { - 'cloud_density': 0.18, - 'cloud_darkness': 0, - 'rain_density': 0, - 'snow_density': 0, - 'lightning_frequency': 0, - 'wind_speed': 0.45 - }; - break; - case 'partly_cloudy': - weather_settings = { - 'cloud_density': 0.3, - 'cloud_darkness': 0.1, - 'rain_density': 0, - 'snow_density': 0, - 'lightning_frequency': 0, - 'wind_speed': 0.55 - }; - break; - case 'mostly_cloudy': - weather_settings = { - 'cloud_density': 0.75, - 'cloud_darkness': 0.45, - 'rain_density': 0, - 'snow_density': 0, - 'lightning_frequency': 0, - 'wind_speed': 0.7 - }; - break; - case 'drizzle': - weather_settings = { - 'cloud_density': 0.9, - 'cloud_darkness': 0.7, - 'rain_density': 0.35, - 'snow_density': 0, - 'lightning_frequency': 0, - 'wind_speed': 0.75 - }; - break; - case 'showers': - weather_settings = { - 'cloud_density': 1.2, - 'cloud_darkness': 1.1, - 'rain_density': 1.2, - 'snow_density': 0, - 'lightning_frequency': 0, - 'wind_speed': 1 - }; - break; - case 'freezing_rain': - weather_settings = { - 'cloud_density': 1.2, - 'cloud_darkness': 1.2, - 'rain_density': 1.1, - 'snow_density': 0.2, - 'lightning_frequency': 0, - 'wind_speed': 1 - }; - break; - case 'hail': - case 'pellets': - weather_settings = { - 'cloud_density': 1.25, - 'cloud_darkness': 1.25, - 'rain_density': 1.2, - 'snow_density': 0.15, - 'lightning_frequency': 0.1, - 'wind_speed': 1.1 - }; - break; - case 'flurries': - weather_settings = { - 'cloud_density': 0.85, - 'cloud_darkness': 0.7, - 'rain_density': 0, - 'snow_density': 0.55, - 'lightning_frequency': 0, - 'wind_speed': 0.65 - }; - break; - case 'freezing_snow': - weather_settings = { - 'cloud_density': 1.1, - 'cloud_darkness': 1, - 'rain_density': 0.05, - 'snow_density': 1.1, - 'lightning_frequency': 0, - 'wind_speed': 0.7 - }; - break; - case 'blizzard': - weather_settings = { - 'cloud_density': 1.4, - 'cloud_darkness': 1.5, - 'rain_density': 0.1, - 'snow_density': 1.8, - 'lightning_frequency': 0, - 'wind_speed': 1.6 - }; - break; - case 'windy': - weather_settings = { - 'cloud_density': 0.55, - 'cloud_darkness': 0.3, - 'rain_density': 0, - 'snow_density': 0, - 'lightning_frequency': 0, - 'wind_speed': 1.5 - }; - break; - case 'tornado': - weather_settings = { - 'cloud_density': 1.35, - 'cloud_darkness': 1.6, - 'rain_density': 1.3, - 'snow_density': 0, - 'lightning_frequency': 0.5, - 'wind_speed': 2 - }; - break; - case 'fog': - weather_settings = { - 'cloud_density': 0.6, - 'cloud_darkness': 0.2, - 'rain_density': 0, - 'snow_density': 0, - 'lightning_frequency': 0, - 'wind_speed': 0.25 - }; - break; - case 'haze': - case 'smoke': - case 'dust': - weather_settings = { - 'cloud_density': 0.45, - 'cloud_darkness': 0.35, - 'rain_density': 0, - 'snow_density': 0, - 'lightning_frequency': 0, - 'wind_speed': 0.6 - }; - break; - case 'thunderstorm': - weather_settings = { - 'cloud_density': 1.5, - 'cloud_darkness': 2, - 'rain_density': 2, - 'snow_density': 0, - 'lightning_frequency': 1, - 'wind_speed': 1.6 - }; - break; - case 'snow': - weather_settings = { - 'cloud_density': 1, - 'cloud_darkness': 1, - 'rain_density': 0, - 'snow_density': 1, - 'lightning_frequency': 0, - 'wind_speed': 0.4 - }; - break; - case 'rain': - weather_settings = { - 'cloud_density': 1, - 'cloud_darkness': 1, - 'rain_density': 1, - 'snow_density': 0, - 'lightning_frequency': 0, - 'wind_speed': 0.8 - }; - break; - case 'overcast': - weather_settings = { - 'cloud_density': 0.5, - 'cloud_darkness': 0.4, - 'rain_density': 0, - 'snow_density': 0, - 'lightning_frequency': 0, - 'wind_speed': 0.8 - }; - break; - case 'sunny': - default: - weather_settings = { - 'cloud_density': 0.03, - 'cloud_darkness': 0, - 'rain_density': 0, - 'snow_density': 0, - 'lightning_frequency': 0, - 'wind_speed': 0.4 - }; - break; - } + const normalized_weather = beestat.weather.get_settings_(weather).condition; + floor_plan.data.appearance.weather = normalized_weather; + const weather_settings = { + 'cloud_density': beestat.weather.get_cloud_density(normalized_weather), + 'cloud_darkness': beestat.weather.get_cloud_darkness(normalized_weather), + 'rain_density': beestat.weather.get_rain_density(normalized_weather), + 'snow_density': beestat.weather.get_snow_density(normalized_weather), + 'lightning_frequency': beestat.weather.get_lightning_frequency(normalized_weather), + 'wind_speed': beestat.weather.get_wind_speed(normalized_weather) + }; this.set_scene_settings(weather_settings, { 'rerender': false }); diff --git a/js/js.php b/js/js.php index 85dece1..449b366 100755 --- a/js/js.php +++ b/js/js.php @@ -32,9 +32,10 @@ if($setting->get('environment') === 'dev' || $setting->get('environment') === 'd echo '' . PHP_EOL; echo '' . PHP_EOL; echo '' . PHP_EOL; - echo '' . PHP_EOL; - echo '' . PHP_EOL; - echo '' . PHP_EOL; + echo '' . PHP_EOL; + echo '' . PHP_EOL; + echo '' . PHP_EOL; + echo '' . PHP_EOL; echo '' . PHP_EOL; echo '' . PHP_EOL; echo '' . PHP_EOL;