So, hab jetzt die Gruppensteuerung drin und bei mir läuft das mit 2 Lampen auch schon eine ganze Weile stabil.
Damit die Gruppensteuerung bei vorhandener Nutzung funktioniert muss man den Datenpunkt "group" entweder manuell anlegen, oder den Yeelight-Baum komplett löschen, damit der automatisch neu angelegt wird.
Die Funktion der Gruppensteuerung ist recht simpel. Lampen, die die gleiche Gruppennummer haben erhalten die gleichen Kommandos. Ich hab dafür im VIS einen Schieberegler der nur die Werte 1,2 und 3 annehmen kann. Das kann aber jeder machen wie er lustig ist.
Hier das Script zum Anlegen der Lampen (lesen des Broadcast):
var dgram = require('dgram');
var s = dgram.createSocket('udp4');
var debug = true;
const MULTICAST_IP = '239.255.255.250';
const s_dataset = 'javascript.0.Yeelight.Bulb';
const s_id = '.id';
const s_address = '.Location';
const s_bright = '.bright';
const s_hue = '.hue';
const s_colorMode = '.color_mode';
const s_rgb = '.rgb';
const s_sat = '.sat';
const s_power = '.power';
const s_ct = '.ct';
const s_name = '.name';
const s_group = '.group';
s.bind(1982, function() {
s.addMembership(MULTICAST_IP);
if (debug) console.log("listening on all addresses");
});
s.on("message", function (msg, rinfo) {
if (debug) console.log('Broadcast empfangen: ' + msg);
if (msg.indexOf('yeelight') < 0) {
if (debug) console.log("Kein Yeelight Broadcast");
return;
}
var bulbID = extractValue('id: ', msg);
var i = 1;
var unknownBulb = true;
//wenn Lampe vorhanden, dann Werte aktualisieren
while (getObject(s_dataset + i + s_id)) {
if (getState(s_dataset + i + s_id).val == bulbID) {
setState(s_dataset + i + s_address, extractValue('Location: ', msg), false);
setState(s_dataset + i + s_bright, parseInt(extractValue('bright: ', msg)), true);
setState(s_dataset + i + s_hue, parseInt(extractValue('hue: ', msg)), true);
setState(s_dataset + i + s_colorMode, parseInt(extractValue('color_mode: ', msg)), true);
setState(s_dataset + i + s_rgb, '#' + parseInt(extractValue('rgb: ', msg)).toString(16), true); //Wert umgewandelt von Dec zu Hex damit Colorpicker Widget einfacher zu nutzen ist
setState(s_dataset + i + s_sat, parseInt(extractValue('sat: ', msg)), true);
setState(s_dataset + i + s_ct, parseInt(extractValue('ct: ', msg)), true);
setState(s_dataset + i + s_power, extractValue('power: ', msg), true);
unknownBulb = false;
if (debug) console.log('Werte für Lampe ' + i + ' aktualisiert');
break;
}
i++;
//Notaus
if (i >= 100) {
if (debug) console.log('zu viele Schleifendurchläufe');
break;
}
}
//wenn Lampe nicht gefunden, dann neue Lampe erstellen
if (unknownBulb) {
if (debug) console.log('neue Lampe wird erstellt');
createNewBulb(i, msg);
}
});
function createNewBulb(i, msg) {
createState(s_dataset + i + s_id, extractValue('id: ', msg), {read: true, write: true, desc: "unique ID der Yeelight Lampe", type: "string", def: ""});
createState(s_dataset + i + s_address, extractValue('Location: ', msg), {read: true, write: true, desc: "URL der Yeelight Lampe", type: "string", def: ""});
createState(s_dataset + i + s_bright, parseInt(extractValue('bright: ', msg)), {read: true, write: true, desc: "Helligkeit", type: "number", min: 1, max: 100, def: 1, unit: "%"});
createState(s_dataset + i + s_hue, parseInt(extractValue('hue: ', msg)), {read: true, write: true, desc: "HSV Farbwert", type: "number", min: 0, max: 359, def: 1, unit: ""});
createState(s_dataset + i + s_colorMode, parseInt(extractValue('color_mode: ', msg)), {read: true, write: true, desc: "Farbwert", type: "number", min: 1, max: 3, def: 1, unit: ""});
createState(s_dataset + i + s_rgb, '#' + parseInt(extractValue('rgb: ', msg)).toString(16), {read: true, write: true, desc: "RGB Farbwert in HEX", type: "string", def: ""});
createState(s_dataset + i + s_sat, parseInt(extractValue('sat: ', msg)), {read: true, write: true, desc: "HSV Sättigung", type: "number", min: 1, max: 100, def: 1, unit: ""});
createState(s_dataset + i + s_ct, parseInt(extractValue('ct: ', msg)), {read: true, write: true, desc: "Farbtemperatur weiß", type: "number", min: 1700, max: 6500, def: 5000, unit: ""});
createState(s_dataset + i + s_power, extractValue('power: ', msg), {read: true, write: true, desc: "Zustand on/off", type: "string", def: ""});
createState(s_dataset + i + s_name, '', {read: true, write: true, desc: "selbst vergebener Name", type: "string", def: "Yeelight"});
createState(s_dataset + i + s_group, i, {read: true, write: true, desc: "Gruppe", type: "number", min: 1, max: 100, def: 1, unit: ""});
if (debug) console.log('neue Lampe erstellt mit ID: ' + i);
}
function extractValue(key, message) {
var text = message.toString('utf-8');
var startPos = (text.indexOf(key) + key.length);
var endPos = text.substring(startPos).indexOf('\r\n');
return text.substr(startPos, endPos);
}
// close connection if script stopped
onStop(function (callback) {
if (s) {
// close connection
s.close();
if (debug) console.log('UDP Client gestoppt');
}
callback();
}, 2000 /*ms*/);
Hier das Script zum Steuern der Lampen:
var net = require('net');
var clients = [];
var debug = true;
const switchEffect = 'smooth'; //smooth or sudden
const switchEffectTime = 1000; // min value 30
const s_dataset = 'javascript.0.Yeelight.Bulb';
const s_bright = '.bright';
const s_hue = '.hue';
const s_colorMode = '.color_mode';
const s_rgb = '.rgb';
const s_sat = '.sat';
const s_power = '.power';
const s_ct = '.ct';
const s_name = '.name';
const s_group = '.group';
const s_location = '.Location';
function createClient(bulbID, location) {
var port = (new RegExp(':([0-9]{1,5})')).exec(location)[1];
var ipAddress = (new RegExp('(?:[0-9]{1,3}\.){3}[0-9]{1,3}')).exec(location)[0];
clients[bulbID - 1] = net.createConnection(port, ipAddress);
if (debug) console.log('Client erstellt für Lampe ' + bulbID);
clients[bulbID - 1].on('data', function(data) {
if (debug) console.log('Empfangen: ' + data);
notificationReceived(bulbID, data)
});
clients[bulbID - 1].on('close', function() {
if (debug) console.log('Verbindung geschlossen');
});
clients[bulbID - 1].on('error', function() {
if (debug) console.log('Verbindung fehlerhaft');
});
}
function killClient(bulbID) {
if (clients[bulbID - 1]) {
clients[bulbID - 1].destroy();
if (debug) console.log('Client beendet für Lampe ' + bulbID);
}
}
function sendBulbCommand(bulbID, changedValue, param1, effect, effectTime) {
var command;
switch (changedValue) {
case 'ct':
command = '{"id":' + bulbID + ',"method":"set_ct_abx","params":[' + param1 + ', "' + effect + '", ' + effectTime + ']}\r\n';
break;
case 'rgb':
command = '{"id":' + bulbID + ',"method":"set_rgb","params":[' + parseInt(param1.substring(1), 16) + ', "' + effect + '", ' + effectTime + ']}\r\n';
break;
case 'hue':
var sat = getState(s_dataset+ bulbID + '.sat').val;
command = '{"id":' + bulbID + ',"method":"set_hsv","params":[' + param1 + ', ' + sat + ', "' + effect + '", ' + effectTime + ']}\r\n';
break;
case 'sat':
var hue = getState(s_dataset + bulbID + '.hue').val;
command = '{"id":' + bulbID + ',"method":"set_hsv","params":[' + hue + ', ' + param1 + ', "' + effect + '", ' + effectTime + ']}\r\n';
break;
case 'bright':
command = '{"id":' + bulbID + ',"method":"set_bright","params":[' + param1 + ', "' + effect + '", ' + effectTime + ']}\r\n';
break;
case 'power':
command = '{"id":' + bulbID + ',"method":"set_power","params":["' + param1 + '", "' + effect + '", ' + effectTime + ']}\r\n';
break;
case 'toggle':
command = '{"id":' + bulbID + ',"method":"toggle","params":[]}\r\n';
break;
case 'name':
command = '{"id":' + bulbID + ',"method":"set_name","params":["' + param1 + '"]}\r\n';
break;
default:
if (debug) console.log('Unbekanntes Kommando');
return;
}
if (debug) console.log(command);
var commandClient = new net.Socket();
//if (debug) console.log(clients[bulbID - 1].remoteAddress);
//if (debug) console.log(clients[bulbID - 1].remotePort);
var location = getState(s_dataset + bulbID + s_location).val;
var port = (new RegExp(':([0-9]{1,5})')).exec(location)[1];
var ipAddress = (new RegExp('(?:[0-9]{1,3}\.){3}[0-9]{1,3}')).exec(location)[0];
try {
commandClient.connect(port, ipAddress, function() {
commandClient.write(command);
if (debug) console.log("CommandClient Command gesendet");
});
commandClient.on('data', function(data) {
if (debug) console.log('CommandClient empfangen: ' + data);
commandClient.destroy(); // kill client after server's response
if (debug) console.log('CommandClient beendet');
});
commandClient.on('error', function(ex) {
if (debug) console.log('CommandClient Fehler beim Verbinden');
});
} catch (err) {
if (debug) console.log('CommandClient Fehler allgemein');
}
}
function notificationReceived(bulbID, data) {
var notification = JSON.parse(data);
if (notification.params.power) {
setState(s_dataset + bulbID + s_power, notification.params.power, true);
}
if (notification.params.bright) {
setState(s_dataset + bulbID + s_bright, parseInt(notification.params.bright), true);
}
if (notification.params.ct) {
setState(s_dataset + bulbID + s_ct, parseInt(notification.params.ct), true);
}
if (notification.params.rgb) {
setState(s_dataset + bulbID + s_rgb, '#' + parseInt(notification.params.rgb).toString(16), true);
}
if (notification.params.hue) {
setState(s_dataset + bulbID + s_hue, parseInt(notification.params.hue), true);
}
if (notification.params.sat) {
setState(s_dataset + bulbID + s_sat, parseInt(notification.params.sat), true);
}
if (notification.params.color_mode) {
setState(s_dataset + bulbID + s_colorMode, parseInt(notification.params.color_mode), true);
}
}
//Subscribe für Änderung der Datenpunkte
subscribe({id: /^javascript\.0\.Yeelight.Bulb.*/, change: 'any', ack: false}, function (obj) {
var bulbID = (new RegExp('^javascript\.0\.Yeelight.Bulb([0-9]?[0-9])\.')).exec(obj.id)[1];
var changedValue = (new RegExp('^javascript\.0\.Yeelight.Bulb[0-9]?[0-9]\.(.*)')).exec(obj.id)[1];
if (debug) console.log('Kommando für Lampe ' + bulbID + ' mit ' + changedValue);
if (changedValue == 'Location') {
killClient(bulbID);
createClient(bulbID, obj.state.val);
} else {
sendBulbCommand(bulbID, changedValue, obj.state.val, switchEffect, switchEffectTime);
//Änderung an Gruppe weitergeben
var group = parseInt(getState(s_dataset + bulbID + s_group).val);
if (group > 0 && changedValue != 'group' && changedValue != 'name') {
if (debug) console.log('Kommando wird an Gruppe weitergegeben');
var i = 1;
while (getObject(s_dataset + i + s_group)) {
if (debug) console.log('Schleifendurchlauf ' + i);
if (i == bulbID) { //sich selbst muss man nicht updaten
if (debug) console.log('Gruppenmitglied bin ich selbst');
} else if (getState(s_dataset + i + s_group).val == group) { //jemand anderes ist in der gleichen Gruppe
sendBulbCommand(i, changedValue, obj.state.val, switchEffect, switchEffectTime);
if (debug) console.log('Lampe ' + i + ' in Gruppe ' + group + ' wurde angepasst');
} else {
if (debug) console.log('Lampe ' + i + ' nicht Mitglied in Gruppe ' + group);
}
i++;
}
}
}
});