2018-08-05 09:38:33 -07:00
|
|
|
const logger = require('./util/logger');
|
|
|
|
const data = require('./util/data');
|
2020-07-10 13:09:16 -07:00
|
|
|
const settings = require('./util/settings');
|
2018-08-05 09:38:33 -07:00
|
|
|
const fs = require('fs');
|
2019-03-15 15:18:19 -07:00
|
|
|
const objectAssignDeep = require('object-assign-deep');
|
2018-08-05 09:38:33 -07:00
|
|
|
|
|
|
|
const saveInterval = 1000 * 60 * 5; // 5 minutes
|
|
|
|
|
2019-03-15 15:18:19 -07:00
|
|
|
const dontCacheProperties = [
|
2020-04-05 06:41:48 -07:00
|
|
|
'action', 'action_.*', 'button', 'button_left', 'button_right', 'click', 'forgotten', 'keyerror',
|
|
|
|
'step_size', 'transition_time', 'group_list', 'group_capacity', 'no_occupancy_since',
|
2019-09-29 04:42:58 -07:00
|
|
|
'step_mode', 'transition_time', 'duration', 'elapsed', 'from_side', 'to_side',
|
2019-03-15 15:18:19 -07:00
|
|
|
];
|
|
|
|
|
2020-04-05 06:48:23 -07:00
|
|
|
class State {
|
|
|
|
constructor(eventBus) {
|
2018-08-05 09:38:33 -07:00
|
|
|
this.state = {};
|
|
|
|
this.file = data.joinPath('state.json');
|
2018-11-28 11:34:37 -07:00
|
|
|
this.timer = null;
|
2020-04-05 06:48:23 -07:00
|
|
|
this.eventBus = eventBus;
|
2018-11-28 11:34:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
2018-08-05 09:38:33 -07:00
|
|
|
this._load();
|
|
|
|
|
|
|
|
// Save the state on every interval
|
2018-11-28 11:34:37 -07:00
|
|
|
this.clearTimer();
|
2018-08-05 09:38:33 -07:00
|
|
|
this.timer = setInterval(() => this.save(), saveInterval);
|
|
|
|
}
|
2018-11-28 11:34:37 -07:00
|
|
|
|
|
|
|
clearTimer() {
|
|
|
|
if (this.timer) {
|
|
|
|
clearTimeout(this.timer);
|
|
|
|
this.timer = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
stop() {
|
|
|
|
this.clearTimer();
|
|
|
|
this.save();
|
|
|
|
}
|
2018-08-05 09:38:33 -07:00
|
|
|
|
|
|
|
_load() {
|
|
|
|
if (fs.existsSync(this.file)) {
|
|
|
|
try {
|
|
|
|
this.state = JSON.parse(fs.readFileSync(this.file, 'utf8'));
|
|
|
|
logger.debug(`Loaded state from file ${this.file}`);
|
|
|
|
} catch (e) {
|
|
|
|
logger.debug(`Failed to load state from file ${this.file} (corrupt file?)`);
|
|
|
|
}
|
|
|
|
} else {
|
2019-02-19 11:16:18 -07:00
|
|
|
logger.debug(`Can't load state from file ${this.file} (doesn't exist)`);
|
2018-08-05 09:38:33 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
save() {
|
2020-07-10 13:09:16 -07:00
|
|
|
if (settings.get().advanced.cache_state_persistent) {
|
|
|
|
logger.debug(`Saving state to file ${this.file}`);
|
2020-11-11 10:36:15 -07:00
|
|
|
const json = JSON.stringify(this.state, null, 4);
|
2020-09-19 01:59:12 -07:00
|
|
|
try {
|
|
|
|
fs.writeFileSync(this.file, json, 'utf8');
|
|
|
|
} catch (e) {
|
|
|
|
logger.error(`Failed to write state to '${this.file}' (${e.message})`);
|
|
|
|
}
|
2020-07-10 13:09:16 -07:00
|
|
|
} else {
|
|
|
|
logger.debug(`Not saving state`);
|
|
|
|
}
|
2018-08-05 09:38:33 -07:00
|
|
|
}
|
|
|
|
|
2019-09-17 09:33:27 -07:00
|
|
|
exists(ID) {
|
|
|
|
return this.state.hasOwnProperty(ID);
|
2018-08-05 09:38:33 -07:00
|
|
|
}
|
|
|
|
|
2019-09-17 09:33:27 -07:00
|
|
|
get(ID) {
|
|
|
|
return this.state[ID];
|
2018-08-05 09:38:33 -07:00
|
|
|
}
|
|
|
|
|
2019-09-29 05:35:05 -07:00
|
|
|
set(ID, state, reason=null) {
|
2019-04-29 11:38:40 -07:00
|
|
|
const toState = objectAssignDeep.noMutate(state);
|
2020-04-05 06:41:48 -07:00
|
|
|
|
|
|
|
for (const property of Object.keys(toState)) {
|
|
|
|
if (dontCacheProperties.find((p) => property.match(p))) {
|
2019-04-29 11:38:40 -07:00
|
|
|
delete toState[property];
|
2019-03-15 15:18:19 -07:00
|
|
|
}
|
2020-04-05 06:41:48 -07:00
|
|
|
}
|
2019-03-15 15:18:19 -07:00
|
|
|
|
2019-09-17 09:33:27 -07:00
|
|
|
const fromState = this.state[ID];
|
|
|
|
|
|
|
|
this.state[ID] = toState;
|
|
|
|
|
2020-04-05 06:48:23 -07:00
|
|
|
this.eventBus.emit('stateChange', {ID, from: fromState, to: toState, reason});
|
2018-08-05 09:38:33 -07:00
|
|
|
}
|
|
|
|
|
2020-08-02 14:09:43 -07:00
|
|
|
removeKey(ID, path) {
|
|
|
|
if (this.exists(ID)) {
|
|
|
|
let state = this.state[ID];
|
|
|
|
for (let i = 0; i < path.length; i++) {
|
|
|
|
const key = path[i];
|
|
|
|
if (i === path.length - 1) {
|
|
|
|
delete state[key];
|
|
|
|
} else {
|
|
|
|
if (state[key]) {
|
|
|
|
state = state[key];
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-17 09:33:27 -07:00
|
|
|
remove(ID) {
|
|
|
|
if (this.exists(ID)) {
|
|
|
|
delete this.state[ID];
|
2018-08-05 09:38:33 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = State;
|