diff --git a/api/ecobee_thermostat.php b/api/ecobee_thermostat.php index b2c7d55..6c786f4 100644 --- a/api/ecobee_thermostat.php +++ b/api/ecobee_thermostat.php @@ -211,6 +211,7 @@ class ecobee_thermostat extends cora\crud { $attributes['property'] = $this->get_property($thermostat, $ecobee_thermostat); $attributes['filters'] = $this->get_filters($thermostat, $ecobee_thermostat); $attributes['json_alerts'] = $this->get_alerts($thermostat, $ecobee_thermostat); + $attributes['weather'] = $this->get_weather($thermostat, $ecobee_thermostat); $detected_system_type = $this->get_detected_system_type($thermostat, $ecobee_thermostat); if($thermostat['system_type'] === null) { @@ -719,4 +720,145 @@ class ecobee_thermostat extends cora\crud { return $detected_system_type; } + /** + * Get the current weather status. + * + * @param array $thermostat + * @param array $ecobee_thermostat + * + * @return array + */ + private function get_weather($thermostat, $ecobee_thermostat) { + $weather = [ + 'station' => null, + 'dew_point' => null, + 'barometric_pressure' => null, + 'humidity_relative' => null, + 'temperature_high' => null, + 'temperature_low' => null, + 'temperature' => null, + 'wind_bearing' => null, + 'wind_speed' => null, + 'condition' => null + ]; + + if(isset($ecobee_thermostat['json_weather']['weatherStation']) === true) { + $weather['station'] = $ecobee_thermostat['json_weather']['weatherStation']; + } + + if( + isset($ecobee_thermostat['json_weather']['forecasts']) === true && + isset($ecobee_thermostat['json_weather']['forecasts'][0]) === true + ) { + $ecobee_weather = $ecobee_thermostat['json_weather']['forecasts'][0]; + + if(isset($ecobee_weather['dewpoint']) === true) { + $weather['dew_point'] = ($ecobee_weather['dewpoint'] / 10); + } + + // Returned in MB (divide by 33.864 to get inHg) + if(isset($ecobee_weather['pressure']) === true) { + $weather['barometric_pressure'] = $ecobee_weather['pressure']; + } + + if(isset($ecobee_weather['relativeHumidity']) === true) { + $weather['humidity_relative'] = $ecobee_weather['relativeHumidity']; + } + + if(isset($ecobee_weather['tempHigh']) === true) { + $weather['temperature_high'] = ($ecobee_weather['tempHigh'] / 10); + } + + if(isset($ecobee_weather['tempLow']) === true) { + $weather['temperature_low'] = ($ecobee_weather['tempLow'] / 10); + } + + if(isset($ecobee_weather['temperature']) === true) { + $weather['temperature'] = ($ecobee_weather['temperature'] / 10); + } + + if(isset($ecobee_weather['windBearing']) === true) { + $weather['wind_bearing'] = $ecobee_weather['windBearing']; + } + + // mph + if(isset($ecobee_weather['windSpeed']) === true) { + $weather['wind_speed'] = $ecobee_weather['windSpeed']; + } + + if(isset($ecobee_weather['weatherSymbol']) === true) { + switch($ecobee_weather['weatherSymbol']) { + case 0: + $weather['condition'] = 'sunny'; + break; + case 1: + $weather['condition'] = 'few_clouds'; + break; + case 2: + $weather['condition'] = 'partly_cloudy'; + break; + case 3: + $weather['condition'] = 'mostly_cloudy'; + break; + case 4: + $weather['condition'] = 'overcast'; + break; + case 5: + $weather['condition'] = 'drizzle'; + break; + case 6: + $weather['condition'] = 'rain'; + break; + case 7: + $weather['condition'] = 'freezing_rain'; + break; + case 8: + $weather['condition'] = 'showers'; + break; + case 9: + $weather['condition'] = 'hail'; + break; + case 10: + $weather['condition'] = 'snow'; + break; + case 11: + $weather['condition'] = 'flurries'; + break; + case 12: + $weather['condition'] = 'freezing_snow'; + break; + case 13: + $weather['condition'] = 'blizzard'; + break; + case 14: + $weather['condition'] = 'pellets'; + break; + case 15: + $weather['condition'] = 'thunderstorm'; + break; + case 16: + $weather['condition'] = 'windy'; + break; + case 17: + $weather['condition'] = 'tornado'; + break; + case 18: + $weather['condition'] = 'fog'; + break; + case 19: + $weather['condition'] = 'haze'; + break; + case 20: + $weather['condition'] = 'smoke'; + break; + case 21: + $weather['condition'] = 'dust'; + break; + } + } + } + + return $weather; + } + } diff --git a/api/thermostat.php b/api/thermostat.php index 4486246..b631aab 100644 --- a/api/thermostat.php +++ b/api/thermostat.php @@ -33,6 +33,9 @@ class thermostat extends cora\crud { ], 'system_type' => [ 'type' => 'json' + ], + 'weather' => [ + 'type' => 'json' ] ]; diff --git a/css/dashboard.css b/css/dashboard.css index 2b50238..5311b45 100644 --- a/css/dashboard.css +++ b/css/dashboard.css @@ -380,6 +380,19 @@ a.inverted:active { .icon.zigbee:before { content: "\FD1D"; } .icon.basket_fill:before { content: "\F077"; } .icon.basket_unfill:before { content: "\F078"; } +.icon.weather_sunny:before { content: "\F599"; } +.icon.weather_partly_cloudy:before { content: "\F595"; } +.icon.weather_cloudy:before { content: "\F590"; } +.icon.weather_pouring:before { content: "\F596"; } +.icon.weather_hail:before { content: "\F592"; } +.icon.weather_snowy:before { content: "\F598"; } +.icon.weather_snowy_heavy:before { content: "\FF53"; } +.icon.weather_lightning_rainy:before { content: "\F67D"; } +.icon.weather_windy:before { content: "\F59D"; } +.icon.weather_fog:before { content: "\F591"; } +.icon.weather_hazy:before { content: "\FF4D"; } +.icon.weather_tornado:before { content: "\FF55"; } +.icon.cloud_question:before { content: "\FA38"; } .icon.f16:before { font-size: 16px; } .icon.f24:before { font-size: 24px; } diff --git a/font/material_icon/material_icon.eot b/font/material_icon/material_icon.eot index 601bd68..71224e5 100644 Binary files a/font/material_icon/material_icon.eot and b/font/material_icon/material_icon.eot differ diff --git a/font/material_icon/material_icon.svg b/font/material_icon/material_icon.svg deleted file mode 100644 index 9807d07..0000000 --- a/font/material_icon/material_icon.svg +++ /dev/null @@ -1,10491 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/font/material_icon/material_icon.ttf b/font/material_icon/material_icon.ttf index 31a801b..569f59e 100644 Binary files a/font/material_icon/material_icon.ttf and b/font/material_icon/material_icon.ttf differ diff --git a/font/material_icon/material_icon.woff b/font/material_icon/material_icon.woff index 8615de3..71825c0 100644 Binary files a/font/material_icon/material_icon.woff and b/font/material_icon/material_icon.woff differ diff --git a/font/material_icon/material_icon.woff2 b/font/material_icon/material_icon.woff2 index 2371b5e..582536a 100644 Binary files a/font/material_icon/material_icon.woff2 and b/font/material_icon/material_icon.woff2 differ diff --git a/js/component/card/system.js b/js/component/card/system.js index 0bd2cfc..fd69ba1 100644 --- a/js/component/card/system.js +++ b/js/component/card/system.js @@ -15,6 +15,7 @@ beestat.extend(beestat.component.card.system, beestat.component.card); beestat.component.card.system.prototype.decorate_contents_ = function(parent) { this.decorate_circle_(parent); + this.decorate_weather_(parent); this.decorate_equipment_(parent); this.decorate_climate_(parent); }; @@ -78,6 +79,72 @@ beestat.component.card.system.prototype.decorate_circle_ = function(parent) { ); }; +beestat.component.card.system.prototype.decorate_weather_ = function(parent) { + var thermostat = beestat.cache.thermostat[beestat.setting('thermostat_id')]; + + var temperature = beestat.temperature(thermostat.weather.temperature); + var temperature_whole = Math.floor(temperature); + + var circle = $.createElement('div') + .style({ + 'padding': (beestat.style.size.gutter / 2), + 'border-radius': '50%', + 'background': beestat.style.color.bluegray.light, + 'height': '66px', + 'width': '66px', + 'text-align': 'center', + 'text-shadow': '1px 1px 1px rgba(0, 0, 0, 0.2)', + 'position': 'absolute', + 'top': '90px', + 'left': '50%', + 'margin-left': '40px', + 'cursor': 'pointer', + 'transition': 'background 200ms ease' + }); + parent.appendChild(circle); + + circle + .addEventListener('mouseover', function() { + circle.style('background', beestat.style.color.gray.dark); + }) + .addEventListener('mouseout', function() { + circle.style('background', beestat.style.color.bluegray.light); + }) + .addEventListener('click', function() { + (new beestat.component.modal.weather()).render(); + }); + + var temperature_container = $.createElement('div'); + circle.appendChild(temperature_container); + + var temperature_whole_container = $.createElement('span') + .style({ + 'font-size': '22px', + 'font-weight': beestat.style.font_weight.light + }) + .innerHTML(temperature_whole); + temperature_container.appendChild(temperature_whole_container); + + var humidity_container = $.createElement('div') + .style({ + 'display': 'inline-flex', + 'align-items': 'center' + }); + circle.appendChild(humidity_container); + + (new beestat.component.icon('water_percent') + .set_size(16) + ).render(humidity_container); + + humidity_container.appendChild( + $.createElement('span') + .innerHTML(thermostat.weather.humidity_relative + '%') + .style({ + 'font-size': '10px' + }) + ); +}; + /** * Decorate the running equipment list on the bottom left. * diff --git a/js/component/modal/weather.js b/js/component/modal/weather.js new file mode 100644 index 0000000..2592f8b --- /dev/null +++ b/js/component/modal/weather.js @@ -0,0 +1,245 @@ +/** + * Current weather. + */ +beestat.component.modal.weather = function() { + var self = this; + + beestat.dispatcher.addEventListener( + 'cache.thermostat', + function() { + self.rerender(); + } + ); + + beestat.component.modal.apply(this, arguments); +}; +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); + + var tr; + var td; + + var table = $.createElement('table'); + + tr = $.createElement('tr'); + table.appendChild(tr); + + td = $.createElement('td') + .setAttribute('rowspan', '2') + .style({ + 'padding-right': beestat.style.size.gutter + }); + (new beestat.component.icon(icon)) + .set_size(64) + .set_color(icon_color) + .render(td); + tr.appendChild(td); + + td = $.createElement('td'); + td.appendChild( + $.createElement('span') + .innerText( + beestat.temperature({ + 'round': 0, + 'units': true, + 'temperature': thermostat.weather.temperature + }) + ) + .style({ + 'font-size': '24px' + }) + ); + td.appendChild( + $.createElement('span') + .innerText(condition) + .style({ + 'font-size': '18px', + 'padding-left': (beestat.style.size.gutter / 2) + }) + ); + tr.appendChild(td); + + tr = $.createElement('tr').style('color', beestat.style.color.gray.base); + table.appendChild(tr); + + td = $.createElement('td'); + // Low + td.appendChild($.createElement('span').innerText('Low: ')); + td.appendChild( + $.createElement('span') + .innerText( + beestat.temperature({ + 'round': 0, + 'units': false, + 'temperature': thermostat.weather.temperature_low + }) + ) + .style({ + 'padding-right': (beestat.style.size.gutter / 2) + }) + ); + // High + td.appendChild($.createElement('span').innerText('High: ')); + td.appendChild( + $.createElement('span') + .innerText( + beestat.temperature({ + 'round': 0, + 'units': false, + 'temperature': thermostat.weather.temperature_high + }) + ) + ); + + tr.appendChild(td); + + parent.appendChild(table); + + var container = $.createElement('div') + .style({ + 'display': 'grid', + 'grid-template-columns': 'repeat(auto-fill, minmax(120px, 1fr))', + 'margin': '0 0 16px -16px' + }); + parent.appendChild(container); + + var bearings = [ + 'N', + 'NNE', + 'NE', + 'ENE', + 'E', + 'ESE', + 'SE', + 'SSE', + 'S', + 'SSW', + 'SW', + 'WSW', + 'W', + 'WNW', + 'NW', + 'NNW' + ]; + + var fields = [ + { + 'name': 'Humidity', + 'value': thermostat.weather.humidity_relative + '%' + }, + { + 'name': 'Dew Point', + 'value': beestat.temperature({ + 'round': 0, + 'units': true, + 'temperature': thermostat.weather.dew_point + }) + }, + { + 'name': 'Wind', + 'value': thermostat.weather.wind_speed === 0 + ? '0mph' + : thermostat.weather.wind_speed + 'mph ' + bearings[Math.floor(((thermostat.weather.wind_bearing / 22.5) + 0.5) % 16)] + }, + { + 'name': 'Pressure', + 'value': thermostat.weather.barometric_pressure + 'mb' + }, + { + 'name': 'Station', + 'value': thermostat.weather.station + } + ]; + + fields.forEach(function(field) { + var div = $.createElement('div') + .style({ + 'padding': '16px 0 0 16px' + }); + container.appendChild(div); + + div.appendChild($.createElement('div') + .style({ + 'font-weight': beestat.style.font_weight.bold, + 'margin-bottom': (beestat.style.size.gutter / 4) + }) + .innerHTML(field.name)); + div.appendChild($.createElement('div').innerHTML(field.value)); + }); +}; + +beestat.component.modal.weather.prototype.get_title_ = function() { + return 'Weather'; +}; diff --git a/js/js.php b/js/js.php index 966393c..3c69041 100644 --- a/js/js.php +++ b/js/js.php @@ -83,6 +83,7 @@ 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;