const { HCColorLamp, StateUpdateManager, utils } = require('homecontrol-control-base'); const axios = require('axios'); const { URL } = require('url'); class HCTasmota extends HCColorLamp { constructor(config) { super(config); if (!("address" in this._configuration)) { throw new Error(`Required configuration field "address" is missing"`); } this._sumanager = new StateUpdateManager(this._state); } // overwrite to make use of the SUManager get state() { return this._sumanager.state.clone(); } turnOn() { let futureState = this.state; futureState.on = true; let suid = this._sumanager.registerUpdate(futureState); let url = new URL("/cm", this._configuration.address); url.searchParams.append("cmnd", "Power on"); return axios.post(url.toString()).then(resolveHelper(this, suid), rejectHelper(this, suid)); } turnOff() { let futureState = this.state; futureState.on = false; let suid = this._sumanager.registerUpdate(futureState); let url = new URL("/cm", this._configuration.address); url.searchParams.append("cmnd", "Power off"); return axios.post(url.toString()).then(resolveHelper(this, suid), rejectHelper(this, suid)); } setBrightness(brightness) { let futureState = this.state; futureState.brightness = brightness; let suid = this._sumanager.registerUpdate(futureState); let url = new URL("/cm", this._configuration.address); url.searchParams.append("cmnd", `HsbColor3 ${Math.round(brightness)}`); return axios.post(url.toString()).then(resolveHelper(this, suid), rejectHelper(this, suid)); } setColor(color) { let futureState = this.state; futureState.color = utils.fillPartialHSL(color, futureState.color); let suid = this._sumanager.registerUpdate(futureState); let url = new URL("/cm", this._configuration.address); url.searchParams.append("cmnd", `HsbColor ${Math.round(color.hue)},${Math.round(color.sat)},${futureState.brightness}`); return axios.post(url.toString()).then(resolveHelper(this, suid), rejectHelper(this, suid)); } setEffect(id) { } pullState() { let url = new URL("/cm", this._configuration.address); url.searchParams.append("cmnd", `State`); return axios.get(url.toString()).then(res => { if (res.status) { let currentState = this.state; let futureState = currentState.clone(); let { hsl, brightness } = parseHsbString(res.data.HSBColor); futureState.on = res.data.POWER == "ON"; futureState.color = hsl; futureState.brightness = brightness; if (currentState.hash != futureState.hash) { // the state of the controller has changed externally this._sumanager.insertConfirmedState(futureState); this.emit("state change", this.state); } } else { throw new Error(`Got HTTP ${res.status}: ${res.data}`); } }); } } module.exports = HCTasmota; function rejectHelper(self, suid) { return (err) => { self._sumanager.rejectUpdate(suid); throw err; // rethrow so we can catch it at a higher level }; } function resolveHelper(self, suid) { return () => { self._sumanager.confirmUpdate(suid); if (self._sumanager.highestConfirmedId == suid) { self.emit("state change", self.state); } }; } function parseHsbString(hsb) { let parts = hsb.split(","); let hue = Number(parts[0]); let sat = Number(parts[1]); let brightness = Number(parts[2]); let hsl = { hue, sat, l: 100 - sat/100 * 50, }; return { hsl, brightness }; }