first version

This commit is contained in:
Jan Scheiper 2020-12-24 01:08:19 +01:00
parent 1992bd7ce3
commit 49aaf848d3
4 changed files with 1691 additions and 35 deletions

View File

@ -0,0 +1,307 @@
const {
HCColorLamp,
StateUpdateManager,
utils
} = require('homecontrol-control-base');
const mqtt = require("async-mqtt");
class HCschalkematrix extends HCColorLamp {
constructor(config) {
super(config);
if (!("server" in this._configuration)) {
throw new Error(`Required configuration field "server" is missing"`);
}
if (!("control_topic" in this._configuration)) {
throw new Error(`Required configuration field "control_topic" is missing"`);
}
if (!("state_topic" in this._configuration)) {
throw new Error(`Required configuration field "state_topic" is missing"`);
}
if (!("max_brightness" in this._configuration)) {
this._configuration.max_brightness = 150;
}
this._client = null;
this._sumanager = new StateUpdateManager(this._state);
}
static get version() {
return require("./package.json").version;
}
// overwrite to make use of the SUManager
get state() {
return this._sumanager.state.clone();
}
get effects() {
return [
{name: "RGB Strobe", id: "1-80" },
{name: "Strobe", id: "2-99" },
{name: "Police", id: "6-90" },
{name: "Arrow Right SLOW", id: "3-40" },
{name: "Arrow Right FAST", id: "3-90" },
{name: "Arrow Left SLOW", id: "4-40" },
{name: "Arrow Left FAST", id: "4-90" },
{name: "Arrow Split SLOW", id: "5-50" },
{name: "Arrow Split FAST", id: "5-93" },
{name: "Wave White SLOW", id: "7-50" },
{name: "Wave White FAST", id: "7-90" },
{name: "Wave Red/Green SLOW", id: "10-50" },
{name: "Wave Red/Green FAST", id: "10-90" },
{name: "Spinner White", id: "11-80" },
{name: "Spinner Double", id: "14-80" },
{name: "Full Rainbow SLOW", id: "15-20" },
{name: "Full Rainbow MEDIUM", id: "15-50" },
{name: "Full Rainbow FAST", id: "15-90" },
{name: "Rainbow Slide SLOW", id: "17-50" },
{name: "Rainbow Slide FAST", id: "18-80" },
];
}
async init() {
this._client = await mqtt.connectAsync(this._configuration.server);
const initialState = this._sumanager.state;
initialState.color = utils.RGB_to_HSL({red: 255, green: 255, blue: 255 });
initialState.brightness = Math.round((128 / this._configuration.max_brightness) * 100);
this._sumanager.insertConfirmedState(initialState);
this._client.on("message", (topic, msgRaw) => {
let msg;
try {
msg = JSON.parse(msgRaw.toString());
} catch(e) {
return;
}
let newState = this.state;
newState.brightness = (msg.brightness / this._configuration.max_brightness) * 100;
newState.on = state_isOn(msg);
// only overwrite color if it isn't black
if (msg.color.r != 0 || msg.color.g != 0 || msg.color.b != 0) {
newState.color = utils.RGB_to_HSL({
red: msg.color.r,
green: msg.color.g,
blue: msg.color.b
});
}
let speeds = {};
for (let eff of this.effects) {
const matches = eff.id.match(/(\d+)-(\d+)/);
if (speeds[matches[1]] == undefined) {
speeds[matches[1]] = [];
}
speeds[matches[1]].push({speed: Number(matches[2]), id: eff.id });
}
// try to match the effect as best as possible
if (speeds[msg.effect] != undefined) {
if (speeds[msg.effect].length == 1) {
newState.effect = speeds[msg.effect][0][1];
} else {
let lowestDist = Infinity;
let lowestDistId = null;
for (let speed of speeds[msg.effect]) {
if (Math.abs(speed.speed - msg.speed) < lowestDist) {
lowestDist = Math.abs(speed.speed - msg.speed);
lowestDistId = speed.id;
}
}
newState.effect = lowestDistId;
}
}
this._sumanager.insertConfirmedState(newState);
this.emit("state change", this.state);
});
await this._client.subscribe(this._configuration.state_topic);
}
async deinit() {
await this._client.end();
}
turnOn() {
let futureState = this.state;
futureState.on = true;
futureState.brightness = (128 / this._configuration.max_brightness) * 100;
let suid = this._sumanager.registerUpdate(futureState);
const prevColor = utils.HSL_to_RGB(this.state.color);
return Promise.all([
this._client.publish(this._configuration.control_topic, JSON.stringify({
type: "color",
r: prevColor.red,
g: prevColor.green,
b: prevColor.blue,
exclusive: true,
})),
this._client.publish(this._configuration.control_topic, JSON.stringify({
type: "brightness",
brightness: 128,
})),
]).then(() => {
this._sumanager.confirmUpdate(suid);
if (this._sumanager.highestConfirmedId == suid) {
this.emit("state change", this.state);
}
return true;
}).catch(err => {
this._sumanager.rejectUpdate(suid);
throw err;
});
}
turnOff() {
let futureState = this.state;
futureState.on = false;
futureState.brightness = (30 / this._configuration.max_brightness) * 100;
let suid = this._sumanager.registerUpdate(futureState);
return Promise.all([
this._client.publish(this._configuration.control_topic, JSON.stringify({
type: "color",
r: 0,
g: 0,
b: 0,
exclusive: false,
})),
this._client.publish(this._configuration.control_topic, JSON.stringify({
type: "clock",
})),
this._client.publish(this._configuration.control_topic, JSON.stringify({
type: "brightness",
brightness: 30,
})),
]).then(() => {
this._sumanager.confirmUpdate(suid);
if (this._sumanager.highestConfirmedId == suid) {
this.emit("state change", this.state);
}
return true;
}).catch(err => {
this._sumanager.rejectUpdate(suid);
throw err;
});
}
setBrightness(brightness) {
let futureState = this.state;
futureState.brightness = brightness;
let suid = this._sumanager.registerUpdate(futureState);
return this._client.publish(this._configuration.control_topic, JSON.stringify({
type: "brightness",
brightness: Math.round(brightness / 100 * this._configuration.max_brightness),
})).then(() => {
this._sumanager.confirmUpdate(suid);
if (this._sumanager.highestConfirmedId == suid) {
this.emit("state change", this.state);
}
return true;
}).catch(err => {
this._sumanager.rejectUpdate(suid);
throw err;
});
}
setColor(color) {
let futureState = this.state;
futureState.color = utils.fillPartialHSL(color, futureState.color);
futureState.effect = "none";
let suid = this._sumanager.registerUpdate(futureState);
let { red, green, blue } = utils.HSL_to_RGB(futureState.color);
return this._client.publish(this._configuration.control_topic, JSON.stringify({
type: "color",
r: red,
g: green,
b: blue,
exclusive: true,
})).then(() => {
this._sumanager.confirmUpdate(suid);
if (this._sumanager.highestConfirmedId == suid) {
this.emit("state change", this.state);
}
return true;
}).catch(err => {
this._sumanager.rejectUpdate(suid);
throw err;
});
}
setEffect(id) {
const matches = id.match(/(\d+)-(\d+)/);
let effect = null;
let speed = null;
if (matches != null) {
effect = matches[1];
speed = matches[2];
}
const futureState = this.state;
let promise;
if (id == "none" || effect == null || speed == null) {
futureState.effect = "none";
let { red, green, blue } = utils.HSL_to_RGB(futureState.color);
promise = this._client.publish(this._configuration.control_topic, JSON.stringify({
type: "color",
r: red,
g: green,
b: blue,
exclusive: true,
}));
} else {
futureState.effect = id; // id is "valid" / can be parsed
promise = this._client.publish(this._configuration.control_topic, JSON.stringify({
type: "effect",
effect: effect,
speed: speed,
exclusive: true,
}));
}
const suid = this._sumanager.registerUpdate(futureState);
return promise.then(() => {
this._sumanager.confirmUpdate(suid);
if (this._sumanager.highestConfirmedId == suid) {
this.emit("state change", this.state);
}
return true;
}).catch(err => {
this._sumanager.rejectUpdate(suid);
throw err;
});
}
}
module.exports = HCschalkematrix;
function state_isOn(state) {
return (state.color.r != 0 || state.color.g != 0 || state.color.b != 0 || state.color.overlay == 0 || state.effect != 0);
}

10
README.md Normal file
View File

@ -0,0 +1,10 @@
## Configuration
- `server`
MQTT server address including protocol (e.g. `tcp://rpi-serv.jans`)
- `control_topic`
MQTT topic to control the matrix
- `state_topic`
MQTT topic the matrix publishes its state on
- `max_brightness`
Optional max brightness value (default: 150)

1406
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@
"author": "Jan Scheiper", "author": "Jan Scheiper",
"license": "UNLICENSED", "license": "UNLICENSED",
"dependencies": { "dependencies": {
"homecontrol-control-base": "git+https://git.literalchaos.de/jan/homecontrol-control-base.git" "async-mqtt": "^2.6.1",
"homecontrol-control-base": "file:../homecontrol-control-base"
} }
} }