more work on the specification and functionality of base classes, added StateUpdateManager utility, to simplify partial state updates

This commit is contained in:
jangxx 2019-01-26 01:19:25 +01:00
parent 58891b99f4
commit 5d58715922
9 changed files with 138 additions and 5 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules/

View File

@ -10,6 +10,10 @@ class HCColorlamp extends HCControlBase {
return "colorlamp";
}
get effects() {
return [];
}
turnOn() {
return Promise.reject();
}
@ -26,7 +30,19 @@ class HCColorlamp extends HCControlBase {
return Promise.reject();
}
changeColor(hue, sat, light) {
/**
* Sets the color to the values given in the colors object
* Also accepts partial values (only update hue and sat for example)
* @param {Object} color
* @param {Number} color.hue Hue
* @param {Number} color.sat Saturation
* @param {Number} color.l Lightness
*/
changeColor(color) {
return Promise.reject();
}
setEffect(id) {
return Promise.reject();
}
}

View File

@ -9,8 +9,12 @@ class HCControlBase extends EventEmitter {
constructor(configuration, state) {
super();
if (!configuration.name) throw new Error("Device name is missing");
this._configuration = configuration;
this._state = state;
this._name = this._configuration.name;
}
get state() {
@ -21,6 +25,10 @@ class HCControlBase extends EventEmitter {
return "none";
}
get name() {
return this._name;
}
/**
* Updates the state by polling the controlled device
* @returns A promise which resolves when the state has been pulled

92
StateUpdateManager.js Normal file
View File

@ -0,0 +1,92 @@
/**
* Optional utility classes can use when states need to be partially updated
*/
class StateUpdateManager {
constructor(initialState) {
this._highestConfirmedId = 0;
this._currentId = 0;
this._nextId = 1;
this._updateHistory = [{ id: 0, state: initialState }];
}
get highestConfirmedId() {
return this._highestConfirmedId;
}
/**
* Returns the latest (unconfirmed state)
*/
get state() {
return this._updateHistory.find(su => su.id == this._currentId).state;
}
/**
* Returns the latest confirmed state
*/
get confirmedState() {
return this._updateHistory.find(su => su.id == this._highestConfirmedId).state;
}
/**
* Registers the potential update of the state
* @param {Object} state
* @returns {Number} id which is used to confirm or reject the update later
*/
registerUpdate(state) {
let updateId = this._nextId;
this._nextId += 1;
this._currentId = updateId; // until rejected, this is the most recent id
this._updateHistory.push({
id: updateId,
state
});
return updateId;
}
/**
* Add a new state and instantly confirm it
* @param {Object} state
*/
insertConfirmedState(state) {
let suid = this.registerUpdate();
this.confirmUpdate(suid);
}
/**
* Confirm the update with the specified id
* Also remove all updates with lower ids, as these are now confirmed to be outdated
* @param {Number} uid
*/
confirmUpdate(uid) {
if (uid < this._highestConfirmedId) return; // already outdated
this._highestConfirmedId = uid;
// remove history entries with lower ids
this._updateHistory = this._updateHistory.filter(su => su.id >= this._highestConfirmedId);
}
/**
* Rejects the update with the specified id
* If the id is outdated it is ignored
* Otherwise it is thrown out, and the currentId decremented if required
* @param {Number} uid
*/
rejectUpdate(uid) {
if (uid < this._highestConfirmedId) return; // already outdated
// remove element from history
this._updateHistory = this._updateHistory.filter(su => su.id != uid);
if (this._currentId == uid) {
// set current id to highest known uid
this._currentId = this._updateHistory.reduce((highest, su) => (su.id > highest) ? su.id : highest, -1);
}
}
}
module.exports = StateUpdateManager;

View File

@ -1,4 +1,5 @@
module.exports = {
HCColorLamp: require('./ColorlampBase'),
HCSwitch: require('./SwitchBase'),
StateUpdateManager: require('./StateUpdateManager'),
};

5
package-lock.json generated
View File

@ -16,6 +16,11 @@
"requires": {
"is-plain-obj": "1.1.0"
}
},
"node-object-hash": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/node-object-hash/-/node-object-hash-1.4.1.tgz",
"integrity": "sha512-JQVqSM5/mOaUoUhCYR0t1vgm8RFo7qpJtPvnoFCLeqQh1xrfmr3BCD3nGBnACzpIEF7F7EVgqGD3O4lao/BY/A=="
}
}
}

View File

@ -13,6 +13,7 @@
"author": "Jan Scheiper",
"license": "UNLICENSED",
"dependencies": {
"merge-options": "^1.0.1"
"merge-options": "^1.0.1",
"node-object-hash": "^1.4.1"
}
}

View File

@ -1,3 +1,5 @@
const objectHash = require('node-object-hash');
class BaseState {
constructor(cloneObj) {
}
@ -6,12 +8,16 @@ class BaseState {
return {};
}
get hash() {
return objectHash(this.asObj);
}
static get default() {
return {};
}
clone() {
return this(this.asObj);
return new this.constructor(this.asObj);
}
}

View File

@ -13,6 +13,7 @@ class ColorlampState extends BaseState {
sat: cloneState.color.sat,
l: cloneState.color.l
};
this.effect = cloneState.effect;
}
static get default() {
@ -23,7 +24,8 @@ class ColorlampState extends BaseState {
hue: 0,
sat: 0,
l: 0
}
},
effect: "none"
};
}
@ -35,7 +37,8 @@ class ColorlampState extends BaseState {
hue: this.color.hue,
sat: this.color.sat,
l: this.color.l
}
},
effect: this.effect
};
}
}