homebridge-knx
Version:
homebridge shim for KNX home automation.
294 lines (264 loc) • 8.62 kB
JavaScript
/* RGB WW CW light from HSB (Homekit)
*
* Source for formulas: http://www.rapidtables.com/convert/color/rgb-to-hsv.htm
*
* EXPERIMENTAL maturity state!
*/
/* jshint esversion: 6, strict: true, node: true */
'use strict';
/**
* @type {./handlerpattern.js~HandlerPattern}
*/
var HandlerPattern = require('./handlerpattern.js');
var log = require('debug')('RGBWWCWLight');
/**
* @classdesc A custom handler for a RGB Light with 4 group addresses: "On" DPT1 (0,1), "Red", "Green" and "Blue" DPT5 (0..255)
* @extends HandlerPattern
*/
class RGBWWCWLight extends HandlerPattern {
/****
* onKNXValueChange is invoked if a Bus value for one of the bound addresses is received
*
*/
onKNXValueChange(field, oldValue, knxValue) {
// value for HomeKit
var newValue;
console.log('INFO: onKNXValueChange(' + field + ", "+ oldValue + ", "+ knxValue+ ")");
log('INFO: this.myAPI.getProperty(field, "minValue") returns ' + this.myAPI.getProperty(field, "minValue"));
log('INFO: this.myAPI.getProperty(field, "maxValue") returns ' + this.myAPI.getProperty(field, "maxValue"));
log('INFO: this.myAPI.getProperty(field, "perms") returns ' + this.myAPI.getProperty(field, "perms"));
if (field==='On'){
// On/Off has been changed
if ((newValue? 1:0) !== (oldValue? 1:0)) {
// inform homekit
log('INFO Writing ON value of ' + newValue? 1:0 + ' to HomeKit ');
this.myAPI.setValue('On', newValue? 1:0 );
}
}else{
if (field==='Red') {
this.red = knxValue;
} else if (field==='Green') {
this.green = knxValue;
} else if (field==='Blue') {
this.blue = knxValue;
} else if (field==='WarmWhite') {
this.coldWhite = knxValue;
} else if (field==='ColdWhite') {
this.warmWhite = knxValue;
}
var r2 = this.red/255;
var g2 = this.green/255;
var b2 = this.blue/255;
var cmax = Math.max(r2,g2,b2);
var cmin = Math.max(r2,g2,b2);
var delta = cmax-cmin;
//hue
var hue;
if (delta===0) {
hue=0;
} else if (cmax===r2) {
hue = 60 * (((g2-b2)/delta) % 6);
} else if (cmax===g2) {
hue = 60 * ((b2-r2)/delta + 2);
} else if (cmax===b2) {
hue = 60 * ((r2-g2)/delta + 4);
}
// saturation
var saturation;
if (cmax===0) {
saturation = 0;
} else {
saturation = delta/cmax;
}
// color temperature
var ct;
ct = Math.round(360/255*this.warmWhite)+140;
//brightness
var brightness = cmax;
log('INFO calculated values of H,S,B ' + hue + ', ' + saturation + ', ' + brightness);
//send to homekit if changed
/* if (this.myAPI.getValue('Brightness')!== brightness) {
this.myAPI.setValue('Brightness', brightness);
log('INFO Writing Brightness value of ' + brightness + ' to HomeKit ');
}
*/
if (this.myAPI.getValue('Saturation')!== saturation) {
//this.myAPI.setValue('Saturation', saturation);
log('INFO Writing Saturation value of ' + saturation + ' to HomeKit ');
}
if (this.myAPI.getValue('Hue')!== hue) {
//this.myAPI.setValue('Hue', hue);
log('INFO Writing Hue value of ' + hue + ' to HomeKit ');
}
/*if (this.myAPI.getValue('ColorTemperature')!== ct && (this.warmWhite !== 0 || this.coldWhite !== 0)) {
console.log(this.warmWhite + " " + this.coldWhite);
this.myAPI.setValue('ColorTemperature', ct);
log('INFO Writing ColorTemperature value of ' + ct + ' to HomeKit ');
}
*/
}
} // onBusValueChange
/****
* onHKValueChange is invoked if HomeKit is changing characteristic values
*
*/
onHKValueChange(field, oldValue, newValue) {
// homekit will only send a TargetPosition value, so we do not care about (non-) potential others
console.log('INFO: onHKValueChange(' + field + ", "+ oldValue + ", "+ newValue + ")");
// log('INFO: this.myAPI.getProperty(field, "minValue") returns ' + this.myAPI.getProperty(field, "minValue"));
// log('INFO: this.myAPI.getProperty(field, "maxValue") returns ' + this.myAPI.getProperty(field, "maxValue"));
// log('INFO: this.myAPI.getProperty(field, "perms") returns ' + this.myAPI.getProperty(field, "perms"));
if (field==='On') {
// On/Off has been changed
if ((newValue? 1:0) !== (oldValue? 1:0)) {
//really a change!
log('INFO Writing ON value of ' + newValue? 1:0 + ' to KNX bus ');
this.myAPI.knxWrite("On", newValue? 1:0,"DPT1");
}
log('INFO No change in ON value of ' + newValue? 1:0 + ' to KNX bus ');
}else if (field ==='ColorTemperature') {
var val = this.myAPI.getValue("Brightness")/100;
var hue = this.myAPI.getValue("Hue");
var sat = this.myAPI.getValue("Saturation")/100;
var ct = this.myAPI.getValue("ColorTemperature");
var max = 500;
var min = 140;
var ctNorm = ct-140; //0 = kallvit 360 = varmvit
var ww = Math.round(255/360*ctNorm*val);
var cw = Math.round(255/360*(360-ctNorm)*val);
this.myAPI.knxWrite("WarmWhite",ww,"DPT5");
this.myAPI.knxWrite("ColdWhite",cw,"DPT5");
this.myAPI.knxWrite("Red", 0, "DPT5");
this.myAPI.knxWrite("Green", 0, "DPT5");
this.myAPI.knxWrite("Blue", 0, "DPT5");
// persist in the object for later use
this.red = 0;
this.green = 0;
this.blue = 0;
this.warmWhite = ww;
this.coldWhite = cw;
//console.log("Brightness: " + val + " | Hue: " + hue + " | sat: "+ sat + " | CT: " + ct);
}
else {
// Color/Brightness has been changed
// calculate RGB values
var hue = this.myAPI.getValue("Hue");
var sat = this.myAPI.getValue("Saturation")/100;
var val = this.myAPI.getValue("Brightness")/100;
var ct = this.myAPI.getValue("ColorTemperature");
//console.log("Brightness: " + val + " | Hue: " + hue + " | sat: "+ sat + " | CT: " + ct);
var c = val*sat;
var x = c*(1-Math.abs((hue/60) % 2 - 1));
var m = val - c;
var r2,g2,b2;
if (hue < 60) {
r2 = c;
g2 = x;
b2 = 0;
} else if (hue<120) {
r2 = x;
g2 = c;
b2 = 0;
} else if (hue<180) {
r2 = 0;
g2 = c;
b2 = x;
} else if (hue<240) {
r2 = 0;
g2 = x;
b2 = c;
} else if (hue<300) {
r2 = x;
g2 = 0;
b2 = c;
} else {
r2 = c;
g2 = 0;
b2 = x;
}
var r = Math.round((r2 + m)*255);
var g = Math.round((g2 + m)*255);
var b = Math.round((b2 + m)*255);
log('INFO Writing Red value of ' + r + ' to KNX bus ');
log('INFO Writing Green value of ' + g + ' to KNX bus ');
log('INFO Writing Blue value of ' + b + ' to KNX bus ');
this.myAPI.knxWrite("Red", r, "DPT5");
this.myAPI.knxWrite("Green", g, "DPT5");
this.myAPI.knxWrite("Blue", b, "DPT5");
if(this.warmWhite !== 0){
this.myAPI.knxWrite("WarmWhite", 0, "DPT5");
}
if(this.coldWhite !== 0){
this.myAPI.knxWrite("ColdWhite", 0, "DPT5");
}
// persist in the object for later use
this.red = r;
this.green = g;
this.blue = b;
this.warmWhite = 0;
this.coldWhite = 0;
}
} // onHKValueChange
} // class
module.exports= RGBWWCWLight;
/* **********************************************************************************************************************
* The config for that should look like this
* Reverse keyword is not allowed for custom handlers
*
*
"Services": [{
"ServiceType": "Lightbulb",
"Handler": "RGBWWCWLight",
"ServiceName": "Color Light",
"Characteristics": [{
"Type": "On",
"Set": "1/2/1",
"Listen": "1/2/1"
},
{
"Type": "Hue"
},
{
"Type": "Saturation"
},
{
"Type": "Brightness"
},
{
"Type": "ColorTemperature"
}],
"KNXObjects": [
{
"Type": "Red",
"Set": "1/2/2",
"DPT": "DPT5"
},
{
"Type": "Green",
"Set": "1/2/3",
"DPT": "DPT5"
},
{
"Type": "Blue",
"Set": "1/2/4",
"DPT": "DPT5"
},
{
"Type": "WarmWhite",
"Set": "1/2/5",
"DPT": "DPT5"
},
{
"Type": "ColdWhite",
"Set": "1/2/6",
"DPT": "DPT5"
}
],
"KNX-ReadRequests": [
"1/2/1"
]
}]
*
*
*
*/