@radiorichter:
20:59:30.166 [info] javascript.0 Stop script script.js.common.Skript2_2
20:59:30.321 [info] javascript.0 Start javascript script.js.common.Skript2_2
20:59:30.322 [info] javascript.0 script.js.common.Skript2_2: registered 0 subscriptions and 0 schedules
20:59:30.332 [info] javascript.0 script.js.common.Skript2_2: port opened
20:59:30.354 [info] javascript.0 script.js.common.Skript2_2: Stick Version: v37605107 `
Es startet erstmal alles korrekt, dann wird aber kein Gerät gefunden. Das kann jetzt viele Gründe haben:
-
alle angelernte Geräte sind außer Funkreichweite des Sticks. Besonders wenn du den Stick in einem Schrank oder Metallregal versteckst kann das passieren. Der Stick ist leider nicht auf starke Sendeleistung ausgelegt, ggf. kann ein USB-Verlängerungskabel helfen oder diverse 2.4 GHz Antennen-Mods.
-
Key, PANID oder CHANNEL sind falsch
-
Es befinden sich keine weiteren Geräte (wie z.B. Raffstore) im selben System, in das du den Stick eingelernt hast (das nehme ich mal nicht an)
Versuche am besten die Fehlerquellen in der oben beschriebenen Reihenfolge auszuschließen und schalte den Javascript-Adapter auf debug-Ausgabe, damit das Skript mehr Infos ausspuckt.
Ich hänge unten mal das Skript an, so wie bei mir in Verwendung (mit verfälschtem Key). So in etwa müsste das Skript ausgeben (ohne Debug):
` > 0:28:11.073 [info] javascript.1 Start javascript script.js.Devices.WMS
00:28:11.073 [info] javascript.1 script.js.Devices.WMS: registered 0 subscriptions and 0 schedules
00:28:11.082 [info] javascript.1 script.js.Devices.WMS: port opened
00:28:11.110 [info] javascript.1 script.js.Devices.WMS: Stick Version: v37605100
00:28:16.259 [info] javascript.1 script.js.Devices.WMS: device type 20 found: 699906
00:28:16.295 [info] javascript.1 script.js.Devices.WMS: device type 20 found: F99906
00:28:16.296 [info] javascript.1 script.js.Devices.WMS: device type 20 found: 1E9996 `
var namespace = 'Custom.WMS';
var SerialPort = require('serialport');
//config
const PATH = "/dev/ttyUSB0";
const CHANNEL = 17;
const PANID = "F228"; //inclusion mode: FFFF
const KEY = "0E99EB48AB0AEDBB5682857D933D294D"; //inclusion mode: "00112233445566778899AABBCCDDEEFF"
var positionInterval = 60; //how often current position is requested (seconds)
var scanInterval = 600; //how often to scan for devices (seconds)
//listPorts(); //uncomment to list all available serial ports
/* do not edit below! */
//globals
var knownDevices = {}; //stores known devices
var lastData = ''; //contains last packet
var writeQueue = []; //stores data to be sent to serial port
var timers = {};
/* open serial port and setup parser */
function init() {
//scan 3 times
setTimeout(function () {
wmsScan();
}, 5000);
setTimeout(function () {
wmsScan();
}, 10000);
setTimeout(function () {
wmsScan();
}, 30000);
//scan again every scanInterval seconds
setInterval(function () {
wmsScan();
}, scanInterval * 1000);
}
//connect to serial port
const port = new SerialPort(PATH, {
baudRate: 125000,
parity: 'none',
dataBits: 8,
stopBits: 1,
autoOpen: false,
});
//create parser with '}' as delemiter
const parser = port.pipe(new SerialPort.parsers.Readline({delimiter: '}'}));
// handle serial port errors
port.on('error', function () {
log('serial port error!', 'warn');
closePort();
});
//parse incomming packets
parser.on('data', parseData);
//open serial port
portOpen().then((msg) => {
log(msg);
writeAndWaitFor('{G}', 'gWMS USB-Stick', true).then((line) => {
return writeAndWaitFor('{V}', 'v', true);
}).then((line) => {
log('Stick Version: ' + line);
return writeAndWaitFor(encodeWMS('setKey', {key: KEY}), 'a', true);
}).then((line) => {
return writeAndWaitFor(encodeWMS('switchChannel', {
channel: CHANNEL,
panId: PANID
}), 'a', true);
}).then((line) => {
init();
}).catch((err) => {
log(err, 'warn');
closePort();
});
}).catch((err) => {
log(err, 'warn');
});
/* serialport helper functions */
//opens port with promise
function portOpen() {
return new Promise((resolve, reject) => {
port.open((err) => {
err ? reject(err) : resolve('port opened');
})
});
}
//close port if open
function closePort() {
return new Promise((resolve, reject) => {
log('closing open serial ports', 'warn');
if (port && port.isOpen) {
// close connection
port.close(() => {
resolve('port closed')
});
} else {
reject('no port was opened');
}
});
}
//on script stop close port
onStop(closePort, 2000);
//handle incomming data
function parseData(data) {
//trim data
data = wmsTrim(data);
//do nothing, if packet is received twice
if (lastData === data) return
lastData = data;
log('received message: ' + data, 'debug');
//decode data into object
var obj = decodeWMS(data);
log(JSON.stringify(obj), 'debug');
//process object
processWMS(obj);
}
//list available serial ports
function listPorts() {
SerialPort.list().then((ports) => {
log('Serial Ports: ' + JSON.stringify(ports));
}).catch((err) => {
log('error listing ports: ' + JSON.stringify(err));
});
}
//write to serial port and wait for answer
function writeAndWaitFor(data, expect, rejectOnTimeout, timeout) {
return new Promise((resolve, reject) => {
if (isNaN(timeout)) timeout = 5000;
log('sending ' + data, 'debug');
listener = (line) => {
if (wmsTrim(line).substr(0, expect.length) === expect) {
log('received expected answer: ' + expect, 'debug');
parser.removeListener('data', listener);
resolve(wmsTrim(line));
} else {
log('received unexpected answer (still waiting): ' + wmsTrim(line).substr(0, expect.length) + '!=' + expect, 'debug');
}
};
parser.on('data', listener);
enqueue(data);
//remove listener after 5 seconds
setTimeout(() => {
parser.removeListener('data', listener);
rejectOnTimeout ? reject(expect) : resolve(false);
}, timeout);
});
}
function enqueue(data) {
if (typeof data === 'string') writeQueue.push(data);
if (writeQueue.length === 1) {
port.write(writeQueue.shift());
port.drain((err) => {
if (writeQueue.length) enqueue();
});
}
}
/* WMS helper functions */
//trim wms string
function wmsTrim(data) {
return data.trim().substr(1);
}
//decode wms strings into an object
function decodeWMS(packet) {
var obj = {};
switch (packet.substr(0, 1)) {
case 'g':
obj.type = 'stickType';
obj.payload = {name: packet.substr(1)};
break;
case 'v':
obj.type = 'stickVersion';
obj.payload = {version: packet.substr(1)};
break;
case 'f':
obj.type = 'error';
break;
case 'a':
obj.type = 'ack';
break;
case 'r':
obj.type = 'message';
obj.payload = decodeWMSMessage(packet.substr(1));
break;
default:
obj.type = 'unknown';
obj.payload = packet.substr(1);
}
return obj;
}
//decode wms messages into an object
function decodeWMSMessage(message) {
var obj = {};
obj.src = message.substr(0, 6);
var type = message.substr(6, 4);
var payload = message.substr(10);
switch (type) {
case '5018':
obj.type = 'joinNetworkRequest';
obj.messagePayload = {
panId: payload.substr(0, 4),
networkKey: payload.substr(4, 32).match(/../g).reverse().join(""),
unknown: payload.substr(36, 2),
channel: parseInt(payload.substr(38, 2), 16)
};
break;
case '5060':
obj.type = 'switchChannelRequest';
obj.messagePayload = {
panId: payload.substr(0, 4),
deviceType: payload.substr(4, 2),
channel: parseInt(payload.substr(6, 2), 16)
};
break;
case '50AC':
obj.type = 'ack';
obj.messagePayload = {
unknown: payload.substr(0, 4)
};
break;
case '7020':
obj.type = 'scanRequest';
obj.messagePayload = {
panId: payload.substr(0, 4),
deviceType: payload.substr(4, 2)
};
break;
case '7021':
obj.type = 'scanResponse';
obj.messagePayload = {
panId: payload.substr(0, 4),
deviceType: payload.substr(4, 2), //63: wetterstation, 06: webcontrol, 02: stick/software, 20: zwischenstecker
unknown: payload.substr(6) //optional
};
break;
case '7080':
obj.type = 'weatherBroadcast';
obj.messagePayload = {
unknown_1: payload.substr(0, 2),
wind: parseInt(payload.substr(2, 2), 16),
lumen: payload.substr(4, 2) === '00' ? parseInt(payload.substr(12, 2), 16) * 2 : parseInt(payload.substr(4, 2), 16) * parseInt(payload.substr(12, 2), 16) * 2,
unknown_2: payload.substr(6, 6),
unknown_3: payload.substr(14, 2),
rain: payload.substr(16, 2) === 'C8',
temp: parseInt(payload.substr(18, 2), 16) / 2 - 35,
unknown_4: payload.substr(20)
};
break;
case '7050':
obj.type = 'beckonRequest';
break;
case '7070':
obj.type = 'controlRequest';
obj.messagePayload = {
unknown: payload.substr(0, 2),
position: parseInt(payload.substr(2, 2), 16) / 2,
angle: parseInt(payload.substr(4, 2), 16) - 127,
valance_1: payload.substr(6, 2),
valance_2: payload.substr(8, 2)
};
break;
case '7071':
obj.type = 'controlResponse';
obj.messagePayload = payload;
break;
case '8010':
obj.type = 'parameterGetRequest';
obj.messagePayload = {
parameter: payload.substr(0) //01000005: position, 26000046: clock timer settings, 0C000006: auto modes & limits
};
break;
case '8011':
obj.type = 'parameterGetResponse';
obj.messagePayload = {
parameter: payload.substr(0, 8)
};
switch (obj.messagePayload.parameter) {
case '01000003': //position
case '01000005': //position
obj.messagePayload.type = 'position';
obj.messagePayload.position = parseInt(payload.substr(8, 2), 16) / 2;
obj.messagePayload.angle = parseInt(payload.substr(10, 2), 16) - 127;
obj.messagePayload.valance_1 = payload.substr(12, 2);
obj.messagePayload.valance_2 = payload.substr(14, 2);
break;
case '0C000006': //auto modes & limits
obj.messagePayload.type = 'autoSettings';
obj.messagePayload.wind = parseInt(payload.substr(8, 2), 16);
obj.messagePayload.rain = parseInt(payload.substr(10, 2), 16);
obj.messagePayload.sun = parseInt(payload.substr(12, 2), 16);
obj.messagePayload.dusk = parseInt(payload.substr(14, 2), 16);
obj.messagePayload.op = parseInt(payload.substr(16, 2), 16);
break;
case '26000046':
obj.messagePayload.type = 'clock';
obj.messagePayload.unknown = payload.substr(8);
break;
default:
obj.messagePayload.type = 'unknown';
obj.messagePayload.unknown = payload.substr(8);
}
break;
case '8020':
obj.type = 'parameterSetRequest';
obj.messagePayload = {
parameter: payload.substr(0, 8)
};
switch (obj.messagePayload.parameter) {
case '0B080009':
obj.messagePayload.type = 'clock';
obj.messagePayload.year = parseInt(payload.substr(8, 2), 16);
obj.messagePayload.month = parseInt(payload.substr(10, 2), 16);
obj.messagePayload.day = parseInt(payload.substr(12, 2), 16);
obj.messagePayload.hour = parseInt(payload.substr(14, 2), 16);
obj.messagePayload.minute = parseInt(payload.substr(16, 2), 16);
obj.messagePayload.second = parseInt(payload.substr(18, 2), 16);
obj.messagePayload.day_of_week = parseInt(payload.substr(20, 2), 16);
obj.messagePayload.unknown = payload.substr(22);
break;
default:
obj.messagePayload.type = 'unknown';
obj.messagePayload.unknown = payload.substr(8);
}
break;
default:
obj.type = 'unknown';
obj.messagePayload = payload;
}
return obj;
}
//create wms strings
function encodeWMS(type, parameter) {
if (!parameter) parameter = {};
switch (type) {
case 'setKey':
if (!parameter.key) return false;
return '{K401' + parameter.key + '}';
break;
case 'setScanMode':
if (isNaN(parameter.channel) || !parameter.panId) return false;
return '{M#' + parameter.channel + parameter.panId.match(/../g).reverse().join("") + '}';
break;
case 'switchChannel':
if (isNaN(parameter.channel) || !parameter.panId) return false;
return '{M%' + parameter.channel + parameter.panId + '}';
break;
case 'ack':
if (!parameter.dst) return false;
return '{R21' + parameter.dst + '50AC}';
break;
case 'switchChannelRequest': //channel 17 fixed
if (!parameter.panId) return false;
return '{R04FFFFFF5060' + parameter.panId + '021100}'; // dst or FFFFFF???
break;
case 'scanRequest':
return '{R04FFFFFF7020' + parameter.panId + '02}';
break;
case 'scanResponse':
if (!parameter.panId || !parameter.dst) return false;
return '{R01' + parameter.dst + '7021' + parameter.panId + '02}'; //fixed to deviceType 02 for now
break;
case 'beckonRequest':
if (!parameter.dst) return false;
return '{R06' + parameter.dst + '7050}';
break;
case 'controlRequest':
if (!parameter.dst || isNaN(parameter.position) || isNaN(parameter.angle)) return false;
return '{R04' + parameter.dst + '7070' + '03'
+ ('0' + (Math.min(Math.max(parameter.position, 0), 100) * 2).toString(16)).substr(-2).toUpperCase()
+ ('0' + (Math.min(Math.max(parameter.angle, 0), 90) + 127).toString(16)).substr(-2).toUpperCase()
+ 'FFFF}'; //no idea how valance works
break;
case 'parameterGetRequest':
if (!parameter.dst || !parameter.parameter) return false;
return '{R06' + parameter.dst + '8010' + parameter.parameter + '}';
break;
case 'parameterGetRequestPosition':
if (!parameter.dst) return false;
return '{R06' + parameter.dst + '8010' + '01000005}';
break;
case 'parameterGetRequestClock':
if (!parameter.dst) return false;
return '{R06' + parameter.dst + '8010' + '26000046}';
break;
case 'parameterGetRequestAutoSettings':
if (!parameter.dst) return false;
return '{R06' + parameter.dst + '8010' + '0C000006}';
break;
case 'parameterSetRequestAutoSettings':
if (!parameter.dst || !parameter.parameter
|| isNaN(parameter.wind) || isNaN(parameter.rain)
|| isNaN(parameter.sun) || isNaN(parameter.dusk))
return false;
return '{R06' + parameter.dst + '8020' + '0D000004'
+ ('0' + Math.min(Math.max(parameter.wind, 0), 9).toString(16)).substr(-2).toUpperCase()
+ ('0' + Math.min(Math.max(parameter.rain, 0), 9).toString(16)).substr(-2).toUpperCase()
+ ('0' + Math.min(Math.max(parameter.sun, 0), 9).toString(16)).substr(-2).toUpperCase()
+ ('0' + Math.min(Math.max(parameter.dusk, 0), 9).toString(16)).substr(-2).toUpperCase()
+ (parameter.op ? '01' : '00')
+ '}';
break;
case 'parameterSetRequestAutoAll':
if (!parameter.dst) return false;
return '{R06' + parameter.dst + '8020' + '0D040001' + (parameter.op ? '01' : '00') + '}';
break;
default: //unkown message type
return false;
break;
}
}
//process packets
function processWMS(obj) {
//log(JSON.stringify(obj));
if (obj.type !== 'message') return;
switch (obj.payload.type) {
case 'switchChannelRequest':
log('received switchChannelRequest, switching channel to ' + obj.payload.messagePayload.channel, 'debug');
writeAndWaitFor(encodeWMS('switchChannel', {
channel: obj.payload.messagePayload.channel,
panId: PANID
}), 'a');
break;
case 'scanRequest':
// send scanResponse
log('received scanRequest, sending scanResponse', 'debug');
writeAndWaitFor(encodeWMS('scanResponse', {dst: obj.payload.src, panId: PANID}), 'a');
break;
case 'joinNetworkRequest':
log('received joinNetworkRequest:', 'debug');
log('KEY: ' + obj.payload.messagePayload.networkKey);
log('CHANNEL: ' + obj.payload.messagePayload.channel);
log('PANID: ' + obj.payload.messagePayload.panId);
writeAndWaitFor(encodeWMS('ack', {dst: obj.payload.src}), 'a');
break;
case 'scanResponse':
log('received scanResponse', 'debug');
log('TYPE: ' + obj.payload.messagePayload.deviceType, 'debug');
log('SNR:' + obj.payload.src, 'debug');
if (obj.payload.messagePayload.deviceType === '20') {
createState(this.namespace + '.Raffstore.' + obj.payload.src + '.position', 0, false, {
type: 'number',
min: 0,
max: 100,
unit: '%'
});
createState(this.namespace + '.Raffstore.' + obj.payload.src + '.angle', 0, false, {
type: 'number',
min: 0,
max: 90,
unit: '°'
}, function () {
if (knownDevices[obj.payload.src]) return;
knownDevices[obj.payload.src] = true;
var src = obj.payload.src;
log('device type 20 found: ' + src);
var deviceId = 'javascript.' + instance + '.' + this.namespace + '.Raffstore.' + src;
on({id: deviceId + '.position', change: 'ne', ack: false}, function (obj) {
//send parameter
writeAndWaitFor(
encodeWMS('controlRequest', {
dst: src,
position: obj.state.val,
angle: getState(deviceId + '.angle').val
}),
'r' + src + '7071'
).then(() => {
var lastValue = getState(deviceId + '.position').val;
writeAndWaitFor(encodeWMS('parameterGetRequestPosition', {dst: src}), 'a');
var timer = setInterval(function () {
//get parameter periodicaly until no change is detected
log(getState(deviceId + '.position').val + ':' + lastValue, 'debug')
if (getState(deviceId + '.position').val === lastValue)
clearInterval(timer);
lastValue = getState(deviceId + '.position').val;
writeAndWaitFor(encodeWMS('parameterGetRequestPosition', {dst: src}), 'a');
}, 3500);
//setState(deviceId + '.position', getState(deviceId + '.position').val, true);
});
});
on({id: deviceId + '.angle', change: 'ne', ack: false}, function (obj) {
//send parameter
writeAndWaitFor(encodeWMS('controlRequest', {
dst: src,
position: getState(deviceId + '.position').val,
angle: obj.state.val
}),
'r' + src + '7071'
).then(() => {
var lastValue = getState(deviceId + '.angle').val;
writeAndWaitFor(encodeWMS('parameterGetRequestPosition', {dst: src}), 'a');
var timer = setInterval(function () {
//get parameter periodicaly until no change is detected
log(getState(deviceId + '.angle').val + ':' + lastValue, 'debug')
if (getState(deviceId + '.angle').val === lastValue)
clearInterval(timer);
lastValue = getState(deviceId + '.angle').val;
writeAndWaitFor(encodeWMS('parameterGetRequestPosition', {dst: src}), 'a');
}, 3500);
//setState(deviceId + '.angle', getState(deviceId + '.angle').val, true);
});
});
setTimeout(function () {
//get parameter once
writeAndWaitFor(encodeWMS('parameterGetRequestPosition', {dst: src}), 'a');
}, 5000 + Math.random() * 5000);
setInterval(function () {
//get parameter periodicaly
writeAndWaitFor(encodeWMS('parameterGetRequestPosition', {dst: src}), 'a');
}, positionInterval * 1000 + Math.random() * 5000);
});
}
break;
case 'parameterGetResponse':
log('received parameterGetResponse', 'debug');
switch (obj.payload.messagePayload.type) {
case 'position':
setStateDelayed(this.namespace + '.Raffstore.' + obj.payload.src + '.position', obj.payload.messagePayload.position, true, 100, true);
setStateDelayed(this.namespace + '.Raffstore.' + obj.payload.src + '.angle', obj.payload.messagePayload.angle, true, 100, true);
default:
break;
}
break;
case 'weatherBroadcast':
log('received weatherBroadcast', 'debug');
createState(this.namespace + '.Wetter.' + obj.payload.src + '.temp', 0, false, {
type: 'number',
unit: '°C',
write: false
}, function () {
setStateDelayed(this.namespace + '.Wetter.' + obj.payload.src + '.temp', obj.payload.messagePayload.temp, true, 100, true);
});
createState(this.namespace + '.Wetter.' + obj.payload.src + '.wind', 0, false, {
type: 'number',
min: 0,
unit: 'm/s',
write: false
}, function () {
setStateDelayed(this.namespace + '.Wetter.' + obj.payload.src + '.wind', obj.payload.messagePayload.wind, true, 100, true);
});
createState(this.namespace + '.Wetter.' + obj.payload.src + '.lux', 0, false, {
type: 'number',
min: 0,
unit: 'lux',
write: false
}, function () {
setStateDelayed(this.namespace + '.Wetter.' + obj.payload.src + '.lux', obj.payload.messagePayload.lumen, true, 100, true);
});
createState(this.namespace + '.Wetter.' + obj.payload.src + '.rain', false, false, {
type: 'boolean',
write: false
}, function () {
setStateDelayed(this.namespace + '.Wetter.' + obj.payload.src + '.rain', obj.payload.messagePayload.rain, true, 100, true);
});
break;
default:
break;
}
}
//scan for devices
function wmsScan() {
writeAndWaitFor(encodeWMS('scanRequest', {panId: PANID}), 'a');
}