mirror of
https://github.com/beestat/app.git
synced 2025-05-24 02:14:03 -04:00
Loading now doesn't break if the sync fails, and as a bonus that made it trivial to display whether or not ecobee is down.
This commit is contained in:
parent
deaf81e214
commit
a6564305b0
@ -857,7 +857,7 @@ final class database extends \mysqli {
|
||||
');
|
||||
$row = $result->fetch_assoc();
|
||||
if($row['lock'] !== 1) {
|
||||
throw new \Exception('Could not get lock.', 1209);
|
||||
throw new exception('Could not get lock.', 1209);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,8 @@ class runtime extends cora\api {
|
||||
*
|
||||
* @param int $thermostat_id Optional thermostat_id to sync. If not set will
|
||||
* sync all thermostats attached to this user.
|
||||
*
|
||||
* @return boolean true if the sync ran, false if not.
|
||||
*/
|
||||
public function sync($thermostat_id = null) {
|
||||
// Skip this for the demo
|
||||
@ -42,54 +44,58 @@ class runtime extends cora\api {
|
||||
|
||||
set_time_limit(0);
|
||||
|
||||
if($thermostat_id === null) {
|
||||
$thermostat_ids = array_keys(
|
||||
$this->api(
|
||||
'thermostat',
|
||||
'read_id',
|
||||
[
|
||||
'attributes' => [
|
||||
'inactive' => 0
|
||||
try {
|
||||
if($thermostat_id === null) {
|
||||
$thermostat_ids = array_keys(
|
||||
$this->api(
|
||||
'thermostat',
|
||||
'read_id',
|
||||
[
|
||||
'attributes' => [
|
||||
'inactive' => 0
|
||||
]
|
||||
]
|
||||
]
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$this->user_lock($thermostat_id);
|
||||
$thermostat_ids = [$thermostat_id];
|
||||
}
|
||||
|
||||
foreach($thermostat_ids as $thermostat_id) {
|
||||
// Get a lock to ensure that this is not invoked more than once at a time
|
||||
// per thermostat.
|
||||
$lock_name = 'runtime->sync(' . $thermostat_id . ')';
|
||||
$this->database->get_lock($lock_name);
|
||||
|
||||
$thermostat = $this->api('thermostat', 'get', $thermostat_id);
|
||||
|
||||
if(
|
||||
$thermostat['sync_begin'] === $thermostat['first_connected'] ||
|
||||
(
|
||||
$thermostat['sync_begin'] !== null &&
|
||||
strtotime($thermostat['sync_begin']) <= strtotime('-1 year')
|
||||
)
|
||||
) {
|
||||
$this->sync_forwards($thermostat_id);
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$this->sync_backwards($thermostat_id);
|
||||
$this->user_lock($thermostat_id);
|
||||
$thermostat_ids = [$thermostat_id];
|
||||
}
|
||||
|
||||
// If only syncing one thermostat this will delay the sync of the other
|
||||
// thermostat. Not a huge deal, just FYI.
|
||||
$this->api(
|
||||
'user',
|
||||
'update_sync_status',
|
||||
[
|
||||
'key' => 'runtime'
|
||||
]
|
||||
);
|
||||
foreach($thermostat_ids as $thermostat_id) {
|
||||
// Get a lock to ensure that this is not invoked more than once at a time
|
||||
// per thermostat.
|
||||
$lock_name = 'runtime->sync(' . $thermostat_id . ')';
|
||||
$this->database->get_lock($lock_name);
|
||||
|
||||
$this->database->release_lock($lock_name);
|
||||
$thermostat = $this->api('thermostat', 'get', $thermostat_id);
|
||||
|
||||
if(
|
||||
$thermostat['sync_begin'] === $thermostat['first_connected'] ||
|
||||
(
|
||||
$thermostat['sync_begin'] !== null &&
|
||||
strtotime($thermostat['sync_begin']) <= strtotime('-1 year')
|
||||
)
|
||||
) {
|
||||
$this->sync_forwards($thermostat_id);
|
||||
} else {
|
||||
$this->sync_backwards($thermostat_id);
|
||||
}
|
||||
|
||||
// If only syncing one thermostat this will delay the sync of the other
|
||||
// thermostat. Not a huge deal, just FYI.
|
||||
$this->api(
|
||||
'user',
|
||||
'update_sync_status',
|
||||
[
|
||||
'key' => 'runtime'
|
||||
]
|
||||
);
|
||||
|
||||
$this->database->release_lock($lock_name);
|
||||
}
|
||||
} catch(cora\exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,9 +46,10 @@ class sensor extends cora\crud {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync all sensors connected to this account. Once Nest support is
|
||||
* added this will need to check for all connected accounts and run the
|
||||
* appropriate ones.
|
||||
* Sync all sensors for the current user. If we fail to get a lock, fail
|
||||
* silently (catch the exception) and just return false.
|
||||
*
|
||||
* @return boolean true if the sync ran, false if not.
|
||||
*/
|
||||
public function sync() {
|
||||
// Skip this for the demo
|
||||
@ -56,20 +57,24 @@ class sensor extends cora\crud {
|
||||
return;
|
||||
}
|
||||
|
||||
$lock_name = 'sensor->sync(' . $this->session->get_user_id() . ')';
|
||||
$this->database->get_lock($lock_name);
|
||||
try {
|
||||
$lock_name = 'sensor->sync(' . $this->session->get_user_id() . ')';
|
||||
$this->database->get_lock($lock_name);
|
||||
|
||||
$this->api('ecobee_sensor', 'sync');
|
||||
$this->api('ecobee_sensor', 'sync');
|
||||
|
||||
$this->api(
|
||||
'user',
|
||||
'update_sync_status',
|
||||
[
|
||||
'key' => 'sensor'
|
||||
]
|
||||
);
|
||||
$this->api(
|
||||
'user',
|
||||
'update_sync_status',
|
||||
[
|
||||
'key' => 'sensor'
|
||||
]
|
||||
);
|
||||
|
||||
$this->database->release_lock($lock_name);
|
||||
$this->database->release_lock($lock_name);
|
||||
} catch(cora\exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,26 +22,35 @@ class thermostat extends cora\crud {
|
||||
];
|
||||
|
||||
/**
|
||||
* Sync all thermostats for the current user with their associated service.
|
||||
* Sync all thermostats for the current user. If we fail to get a lock, fail
|
||||
* silently (catch the exception) and just return false.
|
||||
*
|
||||
* @return boolean true if the sync ran, false if not.
|
||||
*/
|
||||
public function sync() {
|
||||
// Skip this for the demo
|
||||
if($this->setting->is_demo() === true) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
$lock_name = 'thermostat->sync(' . $this->session->get_user_id() . ')';
|
||||
$this->database->get_lock($lock_name);
|
||||
try {
|
||||
$lock_name = 'thermostat->sync(' . $this->session->get_user_id() . ')';
|
||||
$this->database->get_lock($lock_name);
|
||||
|
||||
$this->api('ecobee_thermostat', 'sync');
|
||||
$this->api('ecobee_thermostat', 'sync');
|
||||
|
||||
$this->api(
|
||||
'user',
|
||||
'update_sync_status',
|
||||
['key' => 'thermostat']
|
||||
);
|
||||
$this->api(
|
||||
'user',
|
||||
'update_sync_status',
|
||||
['key' => 'thermostat']
|
||||
);
|
||||
|
||||
$this->database->release_lock($lock_name);
|
||||
$this->database->release_lock($lock_name);
|
||||
|
||||
return true;
|
||||
} catch(cora\exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -20,7 +20,6 @@ beestat.ecobee_thermostat_models = {
|
||||
'vulcanSmart': 'SmartThermostat'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get a default value for an argument if it is not currently set.
|
||||
*
|
||||
@ -106,16 +105,6 @@ beestat.get_thermostat_color = function(thermostat_id) {
|
||||
return beestat.style.color.bluegray.dark;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current user.
|
||||
*
|
||||
* @return {object}
|
||||
*/
|
||||
beestat.get_user = function() {
|
||||
var user_id = Object.keys(beestat.cache.user)[0];
|
||||
return beestat.cache.user[user_id];
|
||||
};
|
||||
|
||||
// Register service worker
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', function() {
|
||||
|
@ -178,8 +178,6 @@ beestat.api.prototype.load_ = function(response_text) {
|
||||
) {
|
||||
window.location.href = '/';
|
||||
return;
|
||||
} else if (response.data && response.data.error_code === 1209) {
|
||||
// Could not get lock; safe to ignore as that means sync is running.
|
||||
} else if (response.success !== true) {
|
||||
beestat.error(
|
||||
'API call failed: ' + response.data.error_message,
|
||||
|
25
js/beestat/ecobee.js
Normal file
25
js/beestat/ecobee.js
Normal file
@ -0,0 +1,25 @@
|
||||
beestat.ecobee = {};
|
||||
|
||||
/**
|
||||
* Check to see if ecobee is down. If so, render the footer component.
|
||||
*/
|
||||
beestat.ecobee.notify_if_down = function() {
|
||||
if (
|
||||
beestat.cache !== undefined &&
|
||||
beestat.cache.thermostat !== undefined &&
|
||||
beestat.user.get() !== undefined
|
||||
) {
|
||||
var last_update = moment.utc(beestat.user.get().sync_status.thermostat);
|
||||
var down = last_update.isBefore(moment().subtract(15, 'minutes'));
|
||||
|
||||
if (beestat.ecobee.down_notification_ === undefined) {
|
||||
beestat.ecobee.down_notification_ = new beestat.component.down_notification();
|
||||
}
|
||||
|
||||
if (down === true) {
|
||||
beestat.ecobee.down_notification_.render($('body'));
|
||||
} else {
|
||||
beestat.ecobee.down_notification_.dispose();
|
||||
}
|
||||
}
|
||||
};
|
@ -95,6 +95,8 @@ beestat.poll = function() {
|
||||
beestat.cache.set('ecobee_sensor', response.ecobee_sensor);
|
||||
beestat.enable_poll();
|
||||
beestat.dispatcher.dispatchEvent('poll');
|
||||
|
||||
beestat.ecobee.notify_if_down();
|
||||
});
|
||||
|
||||
api.send();
|
||||
|
@ -6,8 +6,6 @@ beestat.component = function() {
|
||||
// Give every component a state object to use for storing data.
|
||||
this.state_ = {};
|
||||
|
||||
// this.render_count_ = 0;
|
||||
|
||||
this.layer_ = beestat.current_layer;
|
||||
|
||||
if (this.rerender_on_breakpoint_ === true) {
|
||||
@ -28,26 +26,27 @@ beestat.extend(beestat.component, rocket.EventTarget);
|
||||
* @return {beestat.component} This
|
||||
*/
|
||||
beestat.component.prototype.render = function(parent) {
|
||||
var self = this;
|
||||
if (this.rendered_ === false) {
|
||||
var self = this;
|
||||
|
||||
if (parent !== undefined) {
|
||||
this.component_container_ = $.createElement('div')
|
||||
.style('position', 'relative');
|
||||
this.decorate_(this.component_container_);
|
||||
parent.appendChild(this.component_container_);
|
||||
} else {
|
||||
this.decorate_();
|
||||
if (parent !== undefined) {
|
||||
this.component_container_ = $.createElement('div')
|
||||
.style('position', 'relative');
|
||||
this.decorate_(this.component_container_);
|
||||
parent.appendChild(this.component_container_);
|
||||
} else {
|
||||
this.decorate_();
|
||||
}
|
||||
|
||||
// The element should now exist on the DOM.
|
||||
setTimeout(function() {
|
||||
self.dispatchEvent('render');
|
||||
}, 0);
|
||||
|
||||
// The render function was called.
|
||||
this.rendered_ = true;
|
||||
}
|
||||
|
||||
// The element should now exist on the DOM.
|
||||
setTimeout(function() {
|
||||
self.dispatchEvent('render');
|
||||
}, 0);
|
||||
|
||||
// The render function was called.
|
||||
this.rendered_ = true;
|
||||
// this.render_count_++;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
@ -71,21 +70,20 @@ beestat.component.prototype.rerender = function() {
|
||||
setTimeout(function() {
|
||||
self.dispatchEvent('render');
|
||||
}, 0);
|
||||
|
||||
// this.render_count_++;
|
||||
|
||||
return this;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove this component from the page.
|
||||
*/
|
||||
beestat.component.prototype.dispose = function() {
|
||||
var child = this.component_container_.parentNode();
|
||||
var parent = child.parentNode();
|
||||
parent.removeChild(child);
|
||||
this.rendered_ = false;
|
||||
if (this.rendered_ === true) {
|
||||
var child = this.component_container_;
|
||||
var parent = child.parentNode();
|
||||
parent.removeChild(child);
|
||||
this.rendered_ = false;
|
||||
}
|
||||
};
|
||||
|
||||
beestat.component.prototype.decorate_ = function() {
|
||||
|
34
js/component/down_notification.js
Normal file
34
js/component/down_notification.js
Normal file
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Ecobee is down!
|
||||
*/
|
||||
beestat.component.down_notification = function() {
|
||||
beestat.component.apply(this, arguments);
|
||||
};
|
||||
beestat.extend(beestat.component.down_notification, beestat.component);
|
||||
|
||||
beestat.component.down_notification.prototype.rerender_on_breakpoint_ = false;
|
||||
|
||||
/**
|
||||
* Decorate a floating banner at the bottom of the page.
|
||||
*
|
||||
* @param {rocket.Elements} parent
|
||||
*/
|
||||
beestat.component.down_notification.prototype.decorate_ = function(parent) {
|
||||
var div = $.createElement('div');
|
||||
div.style({
|
||||
'position': 'fixed',
|
||||
'bottom': '0px',
|
||||
'left': '0px',
|
||||
'width': '100%',
|
||||
'text-align': 'center',
|
||||
'padding-left': beestat.style.size.gutter,
|
||||
'padding-right': beestat.style.size.gutter,
|
||||
'background': beestat.style.color.red.dark
|
||||
});
|
||||
|
||||
var last_update = moment.utc(beestat.user.get().sync_status.thermostat).local()
|
||||
.format('h:m a');
|
||||
div.appendChild($.createElement('p').innerText('Ecobee seems to be down. Your data will update as soon as possible. Last update was at ' + last_update + '.'));
|
||||
|
||||
parent.appendChild(div);
|
||||
};
|
@ -19,7 +19,6 @@ beestat.component.menu.prototype.decorate_ = function(parent) {
|
||||
.set_bubble_text(this.bubble_text_)
|
||||
.set_bubble_color(this.bubble_color_)
|
||||
.set_text_color('#fff')
|
||||
// .set_background_hover_color(beestat.style.color.bluegray.light)
|
||||
.set_background_hover_color('rgba(255, 255, 255, 0.1')
|
||||
.addEventListener('click', function() {
|
||||
// Did I just try to open the same menu as last time?
|
||||
@ -41,12 +40,20 @@ beestat.component.menu.prototype.decorate_ = function(parent) {
|
||||
* Close this menu by hiding the container and removing the event listeners.
|
||||
*/
|
||||
beestat.component.menu.prototype.dispose = function() {
|
||||
var self = this;
|
||||
beestat.component.menu.open_menu.rendered_ = false;
|
||||
this.rendered_ = false;
|
||||
|
||||
if (beestat.component.menu.open_menu !== undefined) {
|
||||
var container = beestat.component.menu.open_menu.container_;
|
||||
container.style('transform', 'scale(0)');
|
||||
|
||||
delete beestat.component.menu.open_menu;
|
||||
setTimeout(function() {
|
||||
self.menu_items_.forEach(function(menu_item) {
|
||||
menu_item.dispose();
|
||||
});
|
||||
|
||||
container.parentNode().removeChild(container);
|
||||
}, 200);
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ beestat.component.menu_item.prototype.decorate_ = function(parent) {
|
||||
.render(parent);
|
||||
|
||||
// Events
|
||||
parent.addEventListener('mouseenter', function() {
|
||||
parent.addEventListener('mouseenter', function() {
|
||||
parent.style({
|
||||
'background': beestat.style.color.blue.light,
|
||||
'color': '#fff'
|
||||
@ -48,7 +48,7 @@ beestat.component.menu_item.prototype.decorate_ = function(parent) {
|
||||
});
|
||||
parent.addEventListener('click', function() {
|
||||
self.menu_.dispose();
|
||||
if(self.callback_ !== undefined) {
|
||||
if (self.callback_ !== undefined) {
|
||||
self.callback_();
|
||||
}
|
||||
});
|
||||
|
@ -31,6 +31,7 @@ if($setting->get('environment') === 'dev' || $setting->get('environment') === 'd
|
||||
echo '<script src="/js/beestat/highcharts.js"></script>' . PHP_EOL;
|
||||
echo '<script src="/js/beestat/get_sync_progress.js"></script>' . PHP_EOL;
|
||||
echo '<script src="/js/beestat/user.js"></script>' . PHP_EOL;
|
||||
echo '<script src="/js/beestat/ecobee.js"></script>' . PHP_EOL;
|
||||
|
||||
// Layer
|
||||
echo '<script src="/js/layer.js"></script>' . PHP_EOL;
|
||||
@ -42,6 +43,7 @@ if($setting->get('environment') === 'dev' || $setting->get('environment') === 'd
|
||||
// Component
|
||||
echo '<script src="/js/component.js"></script>' . PHP_EOL;
|
||||
echo '<script src="/js/component/alert.js"></script>' . PHP_EOL;
|
||||
echo '<script src="/js/component/down_notification.js"></script>' . PHP_EOL;
|
||||
echo '<script src="/js/component/card.js"></script>' . PHP_EOL;
|
||||
echo '<script src="/js/component/card/runtime_thermostat_summary.js"></script>' . PHP_EOL;
|
||||
echo '<script src="/js/component/card/alerts.js"></script>' . PHP_EOL;
|
||||
|
@ -22,6 +22,8 @@ beestat.layer.prototype.render = function() {
|
||||
|
||||
body.innerHTML('');
|
||||
body.appendChild(container);
|
||||
|
||||
beestat.ecobee.notify_if_down();
|
||||
};
|
||||
|
||||
beestat.layer.prototype.decorate_ = function(parent) {
|
||||
|
@ -128,7 +128,6 @@ beestat.layer.load.prototype.decorate_ = function(parent) {
|
||||
);
|
||||
|
||||
api.set_callback(function(response) {
|
||||
|
||||
beestat.cache.set('user', response.user);
|
||||
|
||||
// Rollbar isn't defined on dev.
|
||||
@ -217,6 +216,8 @@ beestat.layer.load.prototype.decorate_ = function(parent) {
|
||||
|
||||
(new beestat.layer.dashboard()).render();
|
||||
|
||||
beestat.ecobee.notify_if_down();
|
||||
|
||||
/*
|
||||
* If never seen an announcement, or if there is an unread important
|
||||
* announcement, show the modal.
|
||||
|
Loading…
x
Reference in New Issue
Block a user