commit 1010d2e47f59db4c6e20be2ed59eaed745395cdd Author: jangxx Date: Sat Mar 2 14:45:30 2019 +0100 finished first version diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..607a36b --- /dev/null +++ b/.jshintrc @@ -0,0 +1,3 @@ +{ + "esversion": 6 +} \ No newline at end of file diff --git a/HCmilight.js b/HCmilight.js new file mode 100644 index 0000000..d35d2d6 --- /dev/null +++ b/HCmilight.js @@ -0,0 +1,207 @@ +const { + HCColorLamp, + StateUpdateManager +} = require('homecontrol-control-base'); + +const { + MilightController, + commands2, +} = require('node-milight-promise'); + +const mergeOptions = require('merge-options'); + +class HCmilight extends HCColorLamp { + constructor(config) { + super(config); + + if (!("address" in this._configuration)) { + throw new Error(`Required configuration field "address" is missing"`); + } + + this._zone = (this._configuration.zone !== undefined) ? this._configuration.zone : 1; + + let opts = { + ip: this._configuration.address + }; + + opts = mergeOptions(opts, { + commandRepeat: 2, + }, this._configuration.controllerOpts); + + this._state.brightness = 100; + this._state.color.l = 100; + this._state.color.sat = 0; + + this._light = new MilightController(opts); + + this._sumanager = new StateUpdateManager(this._state); + } + + // overwrite to make use of the SUManager + get state() { + return this._sumanager.state.clone(); + } + + get effects() { + return [ + {name: "Disco Mode", id: "disco"} + ]; + } + + turnOn() { + let futureState = this.state; + futureState.on = true; + let suid = this._sumanager.registerUpdate(futureState); + + this._light.sendCommands(commands2.rgbw.on(this._zone)); + + return this._light.close().then(() => { + this._sumanager.confirmUpdate(suid); + + if (this._sumanager.highestConfirmedId == suid) { + this.emit("state change", this.state); + } + }).catch(err => { + this._sumanager.rejectUpdate(suid); + throw err; + }); + } + + turnOff() { + let futureState = this.state; + futureState.on = false; + let suid = this._sumanager.registerUpdate(futureState); + + this._light.sendCommands(commands2.rgbw.off(this._zone)); + + return this._light.close().then(() => { + this._sumanager.confirmUpdate(suid); + + if (this._sumanager.highestConfirmedId == suid) { + this.emit("state change", this.state); + } + }).catch(err => { + this._sumanager.rejectUpdate(suid); + throw err; + }); + } + + setBrightness(brightness) { + let futureState = this.state; + futureState.brightness = brightness; + let suid = this._sumanager.registerUpdate(futureState); + + this._light.sendCommands( + commands2.rgbw.on(this._zone), + commands2.rgbw.brightness(brightness) + ); + + return this._light.close().then(() => { + this._sumanager.confirmUpdate(suid); + + if (this._sumanager.highestConfirmedId == suid) { + this.emit("state change", this.state); + } + }).catch(err => { + this._sumanager.rejectUpdate(suid); + throw err; + }); + } + + setColor(color) { + let futureState = this.state; + futureState.color = fillPartialHSL(color, futureState.color); + futureState.effect = 'none'; + let suid = this._sumanager.registerUpdate(futureState); + + if (futureState.color.sat < 60) { + this._light.sendCommands( + commands2.rgbw.on(this._zone), + commands2.rgbw.whiteMode(this._zone) + ); + } else { + let hue = Math.round(convertHue(futureState.color.hue) * (255/360)); + this._light.sendCommands( + commands2.rgbw.on(this._zone), + commands2.rgbw.hue(hue) + ); + } + + return this._light.close().then(() => { + this._sumanager.confirmUpdate(suid); + + if (this._sumanager.highestConfirmedId == suid) { + this.emit("state change", this.state); + } + }).catch(err => { + this._sumanager.rejectUpdate(suid); + throw err; + }); + } + + setEffect(id) { + let futureState = this.state; + + if (id == 'none') { + if (futureState.color.sat < 60) { + this._light.sendCommands( + commands2.rgbw.on(this._zone), + commands2.rgbw.whiteMode(this._zone) + ); + } else { + let hue = Math.round(convertHue(futureState.color.hue) * (255/360)); + this._light.sendCommands( + commands2.rgbw.on(this._zone), + commands2.rgbw.hue(hue) + ); + } + + futureState.effect = "none"; + } else if (id == 'disco') { + this._light.sendCommands( + commands2.rgbw.on(this._zone), + commands2.rgbw.effectModeNext() + ); + + futureState.effect = 'disco'; + } else { + return Promise.reject(new Error("Invalid effect id")); + } + + let suid = this._sumanager.registerUpdate(futureState); + return this._light.close().then(() => { + this._sumanager.confirmUpdate(suid); + + if (this._sumanager.highestConfirmedId == suid) { + this.emit("state change", this.state); + } + }).catch(err => { + this._sumanager.rejectUpdate(suid); + throw err; + }); + } +} + +module.exports = HCmilight; + +//the milight hue values are fucking stupid +function convertHue(hue) { + if(hue < 240) { + return 240 - hue; + } else { + return 600 - hue; + } +} + +function fillPartialHSL(newcolor, oldcolor) { + if(newcolor.sat != undefined && newcolor.l == undefined) { + newcolor.l = (newcolor.sat > 50) ? 50 : 100 - newcolor.sat; + } + + //console.log(newcolor, oldcolor); + let color = {}; + color.hue = (newcolor.hue != null) ? newcolor.hue : oldcolor.hue; + color.sat = (newcolor.sat != null) ? newcolor.sat : oldcolor.sat; + color.l = (newcolor.l != null) ? newcolor.l : oldcolor.l; + return color; +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..ee92329 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,60 @@ +{ + "name": "hc-milight", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "bluebird": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", + "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==" + }, + "homecontrol-control-base": { + "version": "git+https://git.literalchaos.de/jan/homecontrol-control-base.git#b7ed807affe888d61efd9dbff3d55bcc128215b7", + "requires": { + "merge-options": "1.0.1", + "node-object-hash": "1.4.2" + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + }, + "merge-options": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-1.0.1.tgz", + "integrity": "sha512-iuPV41VWKWBIOpBsjoxjDZw8/GbSfZ2mk7N1453bwMrfzdrIk7EzBd+8UVR6rkw67th7xnk9Dytl3J+lHPdxvg==", + "requires": { + "is-plain-obj": "1.1.0" + } + }, + "node-milight-promise": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/node-milight-promise/-/node-milight-promise-0.3.2.tgz", + "integrity": "sha512-AyWLXGQ8SIDkAhirSZe8kh1g1t5MJAaRBPNlWGxKuztYDCdtZID+IASamvQtGaWgb0FvGvVWs8aMXSP5Rx2a7Q==", + "requires": { + "bluebird": "3.5.3", + "promise-retryer": "0.0.1" + } + }, + "node-object-hash": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/node-object-hash/-/node-object-hash-1.4.2.tgz", + "integrity": "sha512-UdS4swXs85fCGWWf6t6DMGgpN/vnlKeSGEQ7hJcrs7PBFoxoKLmibc3QRb7fwiYsjdL7PX8iI/TMSlZ90dgHhQ==" + }, + "promise-object": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/promise-object/-/promise-object-0.1.7.tgz", + "integrity": "sha1-MsQ6Fn38xq6NIKiu47GAc14yYQc=" + }, + "promise-retryer": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/promise-retryer/-/promise-retryer-0.0.1.tgz", + "integrity": "sha1-H8QiUAN++keClmKNmnOKynobIEk=", + "requires": { + "promise-object": "0.1.7" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..00c4013 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "hc-milight", + "version": "1.0.0", + "description": "Homecontrol plugin for milight bulbs", + "main": "HCmilight.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git@git.literalchaos.de:jan/hc-milight.git" + }, + "keywords": [ + "homecontrol-plugin" + ], + "author": "Jan Scheiper", + "license": "UNLICENSED", + "dependencies": { + "homecontrol-control-base": "git+https://git.literalchaos.de/jan/homecontrol-control-base.git", + "merge-options": "^1.0.1", + "node-milight-promise": "^0.3.2" + } +}