Initial commit
This commit is contained in:
94
test/lib/mqttClient.js
Normal file
94
test/lib/mqttClient.js
Normal file
@@ -0,0 +1,94 @@
|
||||
'use strict';
|
||||
const mqtt = require('mqtt');
|
||||
|
||||
function Client(cbConnected, cbChanged, config) {
|
||||
let that = this;
|
||||
if (typeof config === 'string') config = {name: config};
|
||||
config = config || {};
|
||||
config.url = config.url || 'localhost';
|
||||
this.client = mqtt.connect('mqtt://' + (config.user ? (config.user + ':' + config.pass + '@') : '') + config.url + (config.name ? '?clientId=' + config.name : ''), config);
|
||||
|
||||
this.client.on('connect', () => {
|
||||
console.log((new Date()) + ' test client connected to localhost');
|
||||
|
||||
/*that.client.publish('mqtt/0/test', 'Roger1');
|
||||
client.publish('test/out/testMessage1', 'Roger1');
|
||||
client.publish('test/out/testMessage2', 'Roger2');
|
||||
client.publish('test/in/testMessage3', 'Roger3');
|
||||
client.publish('test/in/testMessage4', 'Roger4');*/
|
||||
|
||||
/*client.publish('arduino/kitchen/out/temperature', '10.1');
|
||||
client.publish('arduino/kitchen/out/humidity', '56');
|
||||
// Current light state
|
||||
client.publish('arduino/kitchen/in/lightActor', 'false');
|
||||
|
||||
client.subscribe('arduino/kitchen/in/#');*/
|
||||
//client.subscribe('arduino/kitchen/in/updateInterval');
|
||||
that.client.subscribe('#');
|
||||
if (cbConnected) cbConnected(true);
|
||||
});
|
||||
|
||||
this.client.on('message', (topic, message, packet) => {
|
||||
// message is Buffer
|
||||
if (cbChanged) {
|
||||
cbChanged(topic, message, packet);
|
||||
} else {
|
||||
console.log('Test MQTT Client received "' + topic + '": ' + message.toString());
|
||||
}
|
||||
});
|
||||
this.client.on('close', err => {
|
||||
if (err) console.error('Connection closed: ' + err);
|
||||
// message is Buffer
|
||||
if (cbConnected) {
|
||||
cbConnected(false);
|
||||
} else {
|
||||
console.log('Test MQTT Client closed');
|
||||
}
|
||||
});
|
||||
|
||||
this.client.on('error', error => {
|
||||
console.error('Test MQTT Client error: ' + error);
|
||||
});
|
||||
|
||||
this.publish = (topic, message, qos, retain, cb) => {
|
||||
if (typeof qos === 'function') {
|
||||
cb = qos;
|
||||
qos = undefined;
|
||||
}
|
||||
if (typeof retain === 'function') {
|
||||
cb = retain;
|
||||
retain = undefined;
|
||||
}
|
||||
const opts = {
|
||||
retain: retain || false,
|
||||
qos: qos || 0
|
||||
};
|
||||
that.client.publish(topic, message, opts, cb);
|
||||
};
|
||||
this.subscribe = (topic, opts, cb) => {
|
||||
if (typeof opts === 'function') {
|
||||
cb = opts;
|
||||
opts = null;
|
||||
}
|
||||
that.client.subscribe(topic, opts, cb);
|
||||
};
|
||||
this.unsubscribe = (topic, cb) => {
|
||||
that.client.unsubscribe(topic, cb);
|
||||
};
|
||||
this.destroy = () => {
|
||||
if (that.client) {
|
||||
that.client.end();
|
||||
that.client = null;
|
||||
}
|
||||
};
|
||||
|
||||
this.stop = this.destroy;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
if (typeof module !== 'undefined' && module.parent) {
|
||||
module.exports = Client;
|
||||
} else {
|
||||
new Client();
|
||||
}
|
||||
1411
test/lib/objects.js
Normal file
1411
test/lib/objects.js
Normal file
File diff suppressed because it is too large
Load Diff
718
test/lib/setup.js
Normal file
718
test/lib/setup.js
Normal file
@@ -0,0 +1,718 @@
|
||||
'use strict';
|
||||
/* jshint -W097 */// jshint strict:false
|
||||
/*jslint node: true */
|
||||
// check if tmp directory exists
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const child_process = require('child_process');
|
||||
const rootDir = path.normalize(__dirname + '/../../');
|
||||
const pkg = require(rootDir + 'package.json');
|
||||
const debug = typeof v8debug === 'object';
|
||||
pkg.main = pkg.main || 'main.js';
|
||||
|
||||
let adapterName = path.normalize(rootDir).replace(/\\/g, '/').split('/');
|
||||
adapterName = adapterName[adapterName.length - 2];
|
||||
let adapterStarted = false;
|
||||
|
||||
function getAppName() {
|
||||
const parts = __dirname.replace(/\\/g, '/').split('/');
|
||||
return parts[parts.length - 3].split('.')[0];
|
||||
}
|
||||
|
||||
const appName = getAppName().toLowerCase();
|
||||
|
||||
let objects;
|
||||
let states;
|
||||
|
||||
let pid = null;
|
||||
|
||||
function copyFileSync(source, target) {
|
||||
|
||||
let targetFile = target;
|
||||
|
||||
//if target is a directory a new file with the same name will be created
|
||||
if (fs.existsSync(target)) {
|
||||
if ( fs.lstatSync( target ).isDirectory() ) {
|
||||
targetFile = path.join(target, path.basename(source));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
fs.writeFileSync(targetFile, fs.readFileSync(source));
|
||||
}
|
||||
catch (err) {
|
||||
console.log(`file copy error: ${source}" -> "${targetFile}" (error ignored)`);
|
||||
}
|
||||
}
|
||||
|
||||
function copyFolderRecursiveSync(source, target, ignore) {
|
||||
let files = [];
|
||||
|
||||
let base = path.basename(source);
|
||||
if (base === adapterName) {
|
||||
base = pkg.name;
|
||||
}
|
||||
//check if folder needs to be created or integrated
|
||||
const targetFolder = path.join(target, base);
|
||||
if (!fs.existsSync(targetFolder)) {
|
||||
fs.mkdirSync(targetFolder);
|
||||
}
|
||||
|
||||
//copy
|
||||
if (fs.lstatSync(source).isDirectory()) {
|
||||
files = fs.readdirSync(source);
|
||||
files.forEach(function (file) {
|
||||
if (ignore && ignore.indexOf(file) !== -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const curSource = path.join(source, file);
|
||||
const curTarget = path.join(targetFolder, file);
|
||||
if (fs.lstatSync(curSource).isDirectory()) {
|
||||
// ignore grunt files
|
||||
if (file.indexOf('grunt') !== -1) return;
|
||||
if (file === 'chai') return;
|
||||
if (file === 'mocha') return;
|
||||
copyFolderRecursiveSync(curSource, targetFolder, ignore);
|
||||
} else {
|
||||
copyFileSync(curSource, curTarget);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!fs.existsSync(rootDir + 'tmp')) {
|
||||
fs.mkdirSync(rootDir + 'tmp');
|
||||
}
|
||||
|
||||
function storeOriginalFiles() {
|
||||
console.log('Store original files...');
|
||||
const dataDir = rootDir + 'tmp/' + appName + '-data/';
|
||||
|
||||
let f = fs.readFileSync(dataDir + 'objects.json');
|
||||
const objects = JSON.parse(f.toString());
|
||||
if (objects['system.adapter.admin.0'] && objects['system.adapter.admin.0'].common) {
|
||||
objects['system.adapter.admin.0'].common.enabled = false;
|
||||
}
|
||||
if (objects['system.adapter.admin.1'] && objects['system.adapter.admin.1'].common) {
|
||||
objects['system.adapter.admin.1'].common.enabled = false;
|
||||
}
|
||||
|
||||
fs.writeFileSync(dataDir + 'objects.json.original', JSON.stringify(objects));
|
||||
try {
|
||||
f = fs.readFileSync(dataDir + 'states.json');
|
||||
fs.writeFileSync(dataDir + 'states.json.original', f);
|
||||
}
|
||||
catch (err) {
|
||||
console.log('no states.json found - ignore');
|
||||
}
|
||||
}
|
||||
|
||||
function restoreOriginalFiles() {
|
||||
console.log('restoreOriginalFiles...');
|
||||
const dataDir = rootDir + 'tmp/' + appName + '-data/';
|
||||
|
||||
let f = fs.readFileSync(dataDir + 'objects.json.original');
|
||||
fs.writeFileSync(dataDir + 'objects.json', f);
|
||||
try {
|
||||
f = fs.readFileSync(dataDir + 'states.json.original');
|
||||
fs.writeFileSync(dataDir + 'states.json', f);
|
||||
}
|
||||
catch (err) {
|
||||
console.log('no states.json.original found - ignore');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function checkIsAdapterInstalled(cb, counter, customName) {
|
||||
customName = customName || pkg.name.split('.').pop();
|
||||
counter = counter || 0;
|
||||
const dataDir = rootDir + 'tmp/' + appName + '-data/';
|
||||
console.log('checkIsAdapterInstalled...');
|
||||
|
||||
try {
|
||||
const f = fs.readFileSync(dataDir + 'objects.json');
|
||||
const objects = JSON.parse(f.toString());
|
||||
if (objects['system.adapter.' + customName + '.0']) {
|
||||
console.log('checkIsAdapterInstalled: ready!');
|
||||
setTimeout(() => cb && cb(), 100);
|
||||
return;
|
||||
} else {
|
||||
console.warn('checkIsAdapterInstalled: still not ready');
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
}
|
||||
|
||||
if (counter > 20) {
|
||||
console.error('checkIsAdapterInstalled: Cannot install!');
|
||||
if (cb) cb('Cannot install');
|
||||
} else {
|
||||
console.log('checkIsAdapterInstalled: wait...');
|
||||
setTimeout(() => checkIsAdapterInstalled(cb, counter + 1), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
function checkIsControllerInstalled(cb, counter) {
|
||||
counter = counter || 0;
|
||||
const dataDir = rootDir + 'tmp/' + appName + '-data/';
|
||||
|
||||
console.log('checkIsControllerInstalled...');
|
||||
try {
|
||||
const f = fs.readFileSync(dataDir + 'objects.json');
|
||||
const objects = JSON.parse(f.toString());
|
||||
if (objects['system.adapter.admin.0']) {
|
||||
console.log('checkIsControllerInstalled: installed!');
|
||||
setTimeout(() => cb && cb(), 100);
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
}
|
||||
|
||||
if (counter > 20) {
|
||||
console.log('checkIsControllerInstalled: Cannot install!');
|
||||
if (cb) cb('Cannot install');
|
||||
} else {
|
||||
console.log('checkIsControllerInstalled: wait...');
|
||||
setTimeout(() => checkIsControllerInstalled(cb, counter + 1), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
function installAdapter(customName, cb) {
|
||||
if (typeof customName === 'function') {
|
||||
cb = customName;
|
||||
customName = null;
|
||||
}
|
||||
customName = customName || pkg.name.split('.').pop();
|
||||
console.log('Install adapter...');
|
||||
const startFile = 'node_modules/' + appName + '.js-controller/' + appName + '.js';
|
||||
// make first install
|
||||
if (debug) {
|
||||
child_process.execSync('node ' + startFile + ' add ' + customName + ' --enabled false', {
|
||||
cwd: rootDir + 'tmp',
|
||||
stdio: [0, 1, 2]
|
||||
});
|
||||
checkIsAdapterInstalled(error => {
|
||||
if (error) console.error(error);
|
||||
console.log('Adapter installed.');
|
||||
if (cb) cb();
|
||||
});
|
||||
} else {
|
||||
// add controller
|
||||
const _pid = child_process.fork(startFile, ['add', customName, '--enabled', 'false'], {
|
||||
cwd: rootDir + 'tmp',
|
||||
stdio: [0, 1, 2, 'ipc']
|
||||
});
|
||||
|
||||
waitForEnd(_pid, () => {
|
||||
checkIsAdapterInstalled(error => {
|
||||
if (error) console.error(error);
|
||||
console.log('Adapter installed.');
|
||||
if (cb) cb();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function waitForEnd(_pid, cb) {
|
||||
if (!_pid) {
|
||||
cb(-1, -1);
|
||||
return;
|
||||
}
|
||||
_pid.on('exit', (code, signal) => {
|
||||
if (_pid) {
|
||||
_pid = null;
|
||||
cb(code, signal);
|
||||
}
|
||||
});
|
||||
_pid.on('close', (code, signal) => {
|
||||
if (_pid) {
|
||||
_pid = null;
|
||||
cb(code, signal);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function installJsController(cb) {
|
||||
console.log('installJsController...');
|
||||
if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller') ||
|
||||
!fs.existsSync(rootDir + 'tmp/' + appName + '-data')) {
|
||||
// try to detect appName.js-controller in node_modules/appName.js-controller
|
||||
// travis CI installs js-controller into node_modules
|
||||
if (fs.existsSync(rootDir + 'node_modules/' + appName + '.js-controller')) {
|
||||
console.log('installJsController: no js-controller => copy it from "' + rootDir + 'node_modules/' + appName + '.js-controller"');
|
||||
// copy all
|
||||
// stop controller
|
||||
console.log('Stop controller if running...');
|
||||
let _pid;
|
||||
if (debug) {
|
||||
// start controller
|
||||
_pid = child_process.exec('node ' + appName + '.js stop', {
|
||||
cwd: rootDir + 'node_modules/' + appName + '.js-controller',
|
||||
stdio: [0, 1, 2]
|
||||
});
|
||||
} else {
|
||||
_pid = child_process.fork(appName + '.js', ['stop'], {
|
||||
cwd: rootDir + 'node_modules/' + appName + '.js-controller',
|
||||
stdio: [0, 1, 2, 'ipc']
|
||||
});
|
||||
}
|
||||
|
||||
waitForEnd(_pid, () => {
|
||||
// copy all files into
|
||||
if (!fs.existsSync(rootDir + 'tmp')) fs.mkdirSync(rootDir + 'tmp');
|
||||
if (!fs.existsSync(rootDir + 'tmp/node_modules')) fs.mkdirSync(rootDir + 'tmp/node_modules');
|
||||
|
||||
if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller')){
|
||||
console.log('Copy js-controller...');
|
||||
copyFolderRecursiveSync(rootDir + 'node_modules/' + appName + '.js-controller', rootDir + 'tmp/node_modules/');
|
||||
}
|
||||
|
||||
console.log('Setup js-controller...');
|
||||
let __pid;
|
||||
if (debug) {
|
||||
// start controller
|
||||
_pid = child_process.exec('node ' + appName + '.js setup first --console', {
|
||||
cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller',
|
||||
stdio: [0, 1, 2]
|
||||
});
|
||||
} else {
|
||||
__pid = child_process.fork(appName + '.js', ['setup', 'first', '--console'], {
|
||||
cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller',
|
||||
stdio: [0, 1, 2, 'ipc']
|
||||
});
|
||||
}
|
||||
waitForEnd(__pid, () => {
|
||||
checkIsControllerInstalled(() => {
|
||||
// change ports for object and state DBs
|
||||
const config = require(rootDir + 'tmp/' + appName + '-data/' + appName + '.json');
|
||||
config.objects.port = 19001;
|
||||
config.states.port = 19000;
|
||||
fs.writeFileSync(rootDir + 'tmp/' + appName + '-data/' + appName + '.json', JSON.stringify(config, null, 2));
|
||||
console.log('Setup finished.');
|
||||
|
||||
copyAdapterToController();
|
||||
|
||||
installAdapter(() => {
|
||||
storeOriginalFiles();
|
||||
if (cb) cb(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// check if port 9000 is free, else admin adapter will be added to running instance
|
||||
const client = new require('net').Socket();
|
||||
client.connect(9000, '127.0.0.1', () => {
|
||||
console.error('Cannot initiate fisrt run of test, because one instance of application is running on this PC. Stop it and repeat.');
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
client.destroy();
|
||||
if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller')) {
|
||||
console.log('installJsController: no js-controller => install from git');
|
||||
|
||||
child_process.execSync('npm install https://github.com/' + appName + '/' + appName + '.js-controller/tarball/master --prefix ./ --production', {
|
||||
cwd: rootDir + 'tmp/',
|
||||
stdio: [0, 1, 2]
|
||||
});
|
||||
} else {
|
||||
console.log('Setup js-controller...');
|
||||
if (debug) {
|
||||
// start controller
|
||||
child_process.exec('node ' + appName + '.js setup first', {
|
||||
cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller',
|
||||
stdio: [0, 1, 2]
|
||||
});
|
||||
} else {
|
||||
child_process.fork(appName + '.js', ['setup', 'first'], {
|
||||
cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller',
|
||||
stdio: [0, 1, 2, 'ipc']
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// let npm install admin and run setup
|
||||
checkIsControllerInstalled(() => {
|
||||
let _pid;
|
||||
|
||||
if (fs.existsSync(rootDir + 'node_modules/' + appName + '.js-controller/' + appName + '.js')) {
|
||||
_pid = child_process.fork(appName + '.js', ['stop'], {
|
||||
cwd: rootDir + 'node_modules/' + appName + '.js-controller',
|
||||
stdio: [0, 1, 2, 'ipc']
|
||||
});
|
||||
}
|
||||
|
||||
waitForEnd(_pid, () => {
|
||||
// change ports for object and state DBs
|
||||
const config = require(rootDir + 'tmp/' + appName + '-data/' + appName + '.json');
|
||||
config.objects.port = 19001;
|
||||
config.states.port = 19000;
|
||||
fs.writeFileSync(rootDir + 'tmp/' + appName + '-data/' + appName + '.json', JSON.stringify(config, null, 2));
|
||||
|
||||
copyAdapterToController();
|
||||
|
||||
installAdapter(() => {
|
||||
storeOriginalFiles();
|
||||
if (cb) cb(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
} else {
|
||||
setImmediate(() => {
|
||||
console.log('installJsController: js-controller installed');
|
||||
if (cb) cb(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function copyAdapterToController() {
|
||||
console.log('Copy adapter...');
|
||||
// Copy adapter to tmp/node_modules/appName.adapter
|
||||
copyFolderRecursiveSync(rootDir, rootDir + 'tmp/node_modules/', ['.idea', 'test', 'tmp', '.git', appName + '.js-controller']);
|
||||
console.log('Adapter copied.');
|
||||
}
|
||||
|
||||
function clearControllerLog() {
|
||||
const dirPath = rootDir + 'tmp/log';
|
||||
let files;
|
||||
try {
|
||||
if (fs.existsSync(dirPath)) {
|
||||
console.log('Clear controller log...');
|
||||
files = fs.readdirSync(dirPath);
|
||||
} else {
|
||||
console.log('Create controller log directory...');
|
||||
files = [];
|
||||
fs.mkdirSync(dirPath);
|
||||
}
|
||||
} catch(e) {
|
||||
console.error('Cannot read "' + dirPath + '"');
|
||||
return;
|
||||
}
|
||||
if (files.length > 0) {
|
||||
try {
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const filePath = dirPath + '/' + files[i];
|
||||
fs.unlinkSync(filePath);
|
||||
}
|
||||
console.log('Controller log cleared');
|
||||
} catch (err) {
|
||||
console.error('cannot clear log: ' + err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clearDB() {
|
||||
const dirPath = rootDir + 'tmp/iobroker-data/sqlite';
|
||||
let files;
|
||||
try {
|
||||
if (fs.existsSync(dirPath)) {
|
||||
console.log('Clear sqlite DB...');
|
||||
files = fs.readdirSync(dirPath);
|
||||
} else {
|
||||
console.log('Create controller log directory...');
|
||||
files = [];
|
||||
fs.mkdirSync(dirPath);
|
||||
}
|
||||
} catch(e) {
|
||||
console.error('Cannot read "' + dirPath + '"');
|
||||
return;
|
||||
}
|
||||
if (files.length > 0) {
|
||||
try {
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const filePath = dirPath + '/' + files[i];
|
||||
fs.unlinkSync(filePath);
|
||||
}
|
||||
console.log('Clear sqlite DB');
|
||||
} catch (err) {
|
||||
console.error('cannot clear DB: ' + err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setupController(cb) {
|
||||
installJsController(isInited => {
|
||||
clearControllerLog();
|
||||
clearDB();
|
||||
|
||||
if (!isInited) {
|
||||
restoreOriginalFiles();
|
||||
copyAdapterToController();
|
||||
}
|
||||
// read system.config object
|
||||
const dataDir = rootDir + 'tmp/' + appName + '-data/';
|
||||
|
||||
let objs;
|
||||
try {
|
||||
objs = fs.readFileSync(dataDir + 'objects.json');
|
||||
objs = JSON.parse(objs);
|
||||
}
|
||||
catch (e) {
|
||||
console.log('ERROR reading/parsing system configuration. Ignore');
|
||||
objs = {'system.config': {}};
|
||||
}
|
||||
if (!objs || !objs['system.config']) {
|
||||
objs = {'system.config': {}};
|
||||
}
|
||||
|
||||
if (cb) cb(objs['system.config']);
|
||||
});
|
||||
}
|
||||
|
||||
function startAdapter(objects, states, callback) {
|
||||
if (adapterStarted) {
|
||||
console.log('Adapter already started ...');
|
||||
if (callback) callback(objects, states);
|
||||
return;
|
||||
}
|
||||
adapterStarted = true;
|
||||
console.log('startAdapter...');
|
||||
if (fs.existsSync(rootDir + 'tmp/node_modules/' + pkg.name + '/' + pkg.main)) {
|
||||
try {
|
||||
if (debug) {
|
||||
// start controller
|
||||
pid = child_process.exec('node node_modules/' + pkg.name + '/' + pkg.main + ' --console silly', {
|
||||
cwd: rootDir + 'tmp',
|
||||
stdio: [0, 1, 2]
|
||||
});
|
||||
} else {
|
||||
// start controller
|
||||
pid = child_process.fork('node_modules/' + pkg.name + '/' + pkg.main, ['--console', 'silly'], {
|
||||
cwd: rootDir + 'tmp',
|
||||
stdio: [0, 1, 2, 'ipc']
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(JSON.stringify(error));
|
||||
}
|
||||
} else {
|
||||
console.error('Cannot find: ' + rootDir + 'tmp/node_modules/' + pkg.name + '/' + pkg.main);
|
||||
}
|
||||
if (callback) callback(objects, states);
|
||||
}
|
||||
|
||||
function startController(isStartAdapter, onObjectChange, onStateChange, callback) {
|
||||
if (typeof isStartAdapter === 'function') {
|
||||
callback = onStateChange;
|
||||
onStateChange = onObjectChange;
|
||||
onObjectChange = isStartAdapter;
|
||||
isStartAdapter = true;
|
||||
}
|
||||
|
||||
if (onStateChange === undefined) {
|
||||
callback = onObjectChange;
|
||||
onObjectChange = undefined;
|
||||
}
|
||||
|
||||
if (pid) {
|
||||
console.error('Controller is already started!');
|
||||
} else {
|
||||
console.log('startController...');
|
||||
adapterStarted = false;
|
||||
let isObjectConnected;
|
||||
let isStatesConnected;
|
||||
|
||||
const Objects = require(rootDir + 'tmp/node_modules/' + appName + '.js-controller/lib/objects/objectsInMemServer');
|
||||
objects = new Objects({
|
||||
connection: {
|
||||
type : 'file',
|
||||
host : '127.0.0.1',
|
||||
port : 19001,
|
||||
user : '',
|
||||
pass : '',
|
||||
noFileCache: false,
|
||||
connectTimeout: 2000
|
||||
},
|
||||
logger: {
|
||||
silly: function (msg) {
|
||||
console.log(msg);
|
||||
},
|
||||
debug: function (msg) {
|
||||
console.log(msg);
|
||||
},
|
||||
info: function (msg) {
|
||||
console.log(msg);
|
||||
},
|
||||
warn: function (msg) {
|
||||
console.warn(msg);
|
||||
},
|
||||
error: function (msg) {
|
||||
console.error(msg);
|
||||
}
|
||||
},
|
||||
connected: () => {
|
||||
isObjectConnected = true;
|
||||
if (isStatesConnected) {
|
||||
console.log('startController: started!');
|
||||
if (isStartAdapter) {
|
||||
startAdapter(objects, states, callback);
|
||||
} else {
|
||||
if (callback) {
|
||||
callback(objects, states);
|
||||
callback = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
change: onObjectChange
|
||||
});
|
||||
|
||||
// Just open in memory DB itself
|
||||
const States = require(rootDir + 'tmp/node_modules/' + appName + '.js-controller/lib/states/statesInMemServer');
|
||||
states = new States({
|
||||
connection: {
|
||||
type: 'file',
|
||||
host: '127.0.0.1',
|
||||
port: 19000,
|
||||
options: {
|
||||
auth_pass: null,
|
||||
retry_max_delay: 15000
|
||||
}
|
||||
},
|
||||
logger: {
|
||||
silly: function (msg) {
|
||||
console.log(msg);
|
||||
},
|
||||
debug: function (msg) {
|
||||
console.log(msg);
|
||||
},
|
||||
info: function (msg) {
|
||||
console.log(msg);
|
||||
},
|
||||
warn: function (msg) {
|
||||
console.log(msg);
|
||||
},
|
||||
error: function (msg) {
|
||||
console.log(msg);
|
||||
}
|
||||
},
|
||||
connected: () => {
|
||||
isStatesConnected = true;
|
||||
if (isObjectConnected) {
|
||||
console.log('startController: started!!');
|
||||
if (isStartAdapter) {
|
||||
startAdapter(objects, states, callback);
|
||||
} else {
|
||||
if (callback) {
|
||||
callback(objects, states);
|
||||
callback = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
change: onStateChange
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function stopAdapter(cb) {
|
||||
if (!pid) {
|
||||
console.error('Controller is not running!');
|
||||
if (cb) {
|
||||
setImmediate(() => cb(false));
|
||||
}
|
||||
} else {
|
||||
adapterStarted = false;
|
||||
pid.on('exit', (code, signal) => {
|
||||
if (pid) {
|
||||
console.log('child process terminated due to receipt of signal ' + signal);
|
||||
if (cb) cb();
|
||||
pid = null;
|
||||
}
|
||||
});
|
||||
|
||||
pid.on('close', (code, signal) => {
|
||||
if (pid) {
|
||||
if (cb) cb();
|
||||
pid = null;
|
||||
}
|
||||
});
|
||||
|
||||
pid.kill('SIGTERM');
|
||||
}
|
||||
}
|
||||
|
||||
function _stopController() {
|
||||
if (objects) {
|
||||
objects.destroy();
|
||||
objects = null;
|
||||
}
|
||||
if (states) {
|
||||
states.destroy();
|
||||
states = null;
|
||||
}
|
||||
}
|
||||
|
||||
function stopController(cb) {
|
||||
let timeout;
|
||||
if (objects) {
|
||||
console.log('Set system.adapter.' + pkg.name + '.0');
|
||||
objects.setObject('system.adapter.' + pkg.name + '.0', {
|
||||
common:{
|
||||
enabled: false
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
stopAdapter(() => {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
}
|
||||
|
||||
_stopController();
|
||||
|
||||
if (cb) {
|
||||
cb(true);
|
||||
cb = null;
|
||||
}
|
||||
});
|
||||
|
||||
timeout = setTimeout(() => {
|
||||
timeout = null;
|
||||
console.log('child process NOT terminated');
|
||||
|
||||
_stopController();
|
||||
|
||||
if (cb) {
|
||||
cb(false);
|
||||
cb = null;
|
||||
}
|
||||
pid = null;
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// Setup the adapter
|
||||
function setAdapterConfig(common, native, instance) {
|
||||
const objects = JSON.parse(fs.readFileSync(rootDir + 'tmp/' + appName + '-data/objects.json').toString());
|
||||
const id = 'system.adapter.' + adapterName.split('.').pop() + '.' + (instance || 0);
|
||||
if (common) objects[id].common = common;
|
||||
if (native) objects[id].native = native;
|
||||
fs.writeFileSync(rootDir + 'tmp/' + appName + '-data/objects.json', JSON.stringify(objects));
|
||||
}
|
||||
|
||||
// Read config of the adapter
|
||||
function getAdapterConfig(instance) {
|
||||
const objects = JSON.parse(fs.readFileSync(rootDir + 'tmp/' + appName + '-data/objects.json').toString());
|
||||
const id = 'system.adapter.' + adapterName.split('.').pop() + '.' + (instance || 0);
|
||||
return objects[id];
|
||||
}
|
||||
|
||||
if (typeof module !== undefined && module.parent) {
|
||||
module.exports.getAdapterConfig = getAdapterConfig;
|
||||
module.exports.setAdapterConfig = setAdapterConfig;
|
||||
module.exports.startController = startController;
|
||||
module.exports.stopController = stopController;
|
||||
module.exports.setupController = setupController;
|
||||
module.exports.stopAdapter = stopAdapter;
|
||||
module.exports.startAdapter = startAdapter;
|
||||
module.exports.installAdapter = installAdapter;
|
||||
module.exports.appName = appName;
|
||||
module.exports.adapterName = adapterName;
|
||||
module.exports.adapterStarted = adapterStarted;
|
||||
}
|
||||
708
test/lib/states.js
Normal file
708
test/lib/states.js
Normal file
@@ -0,0 +1,708 @@
|
||||
'use strict';
|
||||
const path = require('path');
|
||||
const rootDir = path.normalize(__dirname + '/../../');
|
||||
let adapterName = path.normalize(rootDir).replace(/\\/g, '/').split('/');
|
||||
adapterName = adapterName[adapterName.length - 2];
|
||||
|
||||
const logger = {
|
||||
info: function (msg) {
|
||||
console.log(msg);
|
||||
},
|
||||
debug: function (msg) {
|
||||
console.log(msg);
|
||||
},
|
||||
warn: function (msg) {
|
||||
console.warn(msg);
|
||||
},
|
||||
error: function (msg) {
|
||||
console.error(msg);
|
||||
}
|
||||
};
|
||||
|
||||
function States(cb, stateChange) {
|
||||
const that = this;
|
||||
const _States = require(rootDir + 'tmp/node_modules/iobroker.js-controller/lib/states');
|
||||
let callbackId = 0;
|
||||
|
||||
const options = {
|
||||
stateChange: (id, state) => stateChange && stateChange(id, state)
|
||||
};
|
||||
|
||||
that.namespace = 'test';
|
||||
|
||||
that.states = new _States({
|
||||
connection: {
|
||||
type : 'file',
|
||||
host : '127.0.0.1',
|
||||
port : 19000,
|
||||
options : {
|
||||
auth_pass : null,
|
||||
retry_max_delay : 15000
|
||||
}
|
||||
},
|
||||
logger: logger,
|
||||
change: (id, state) => {
|
||||
if (!id || typeof id !== 'string') {
|
||||
console.log('Something is wrong! ' + JSON.stringify(id));
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear cache if accidentally got the message about change (Will work for admin and javascript)
|
||||
if (id.match(/^system\.user\./) || id.match(/^system\.group\./)) {
|
||||
that.users = [];
|
||||
}
|
||||
|
||||
// If someone want to have log messages
|
||||
if (that.logList && id.match(/\.logging$/)) {
|
||||
that.logRedirect(state ? state.val : false, id.substring(0, id.length - '.logging'.length));
|
||||
} else
|
||||
if (id === 'log.system.adapter.' + that.namespace) {
|
||||
that.processLog(state);
|
||||
} else
|
||||
// If this is messagebox
|
||||
if (id === 'messagebox.system.adapter.' + that.namespace && state) {
|
||||
// Read it from fifo list
|
||||
that.states.delMessage('system.adapter.' + that.namespace, state._id);
|
||||
const obj = state;
|
||||
if (obj) {
|
||||
// If callback stored for this request
|
||||
if (obj.callback &&
|
||||
obj.callback.ack &&
|
||||
obj.callback.id &&
|
||||
that.callbacks &&
|
||||
that.callbacks['_' + obj.callback.id]) {
|
||||
// Call callback function
|
||||
if (that.callbacks['_' + obj.callback.id].cb) {
|
||||
that.callbacks['_' + obj.callback.id].cb(obj.message);
|
||||
delete that.callbacks['_' + obj.callback.id];
|
||||
}
|
||||
// delete too old callbacks IDs, like garbage collector
|
||||
const now = Date.now();
|
||||
for (const _id in that.callbacks) {
|
||||
if (that.callbacks.hasOwnProperty(_id) && now - that.callbacks[_id].time > 3600000) delete that.callbacks[_id];
|
||||
}
|
||||
|
||||
} else {
|
||||
if (options.message) {
|
||||
// Else inform about new message the adapter
|
||||
options.message(obj);
|
||||
}
|
||||
that.emit('message', obj);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (id.slice(that.namespace.length) === that.namespace) {
|
||||
if (typeof options.stateChange === 'function') options.stateChange(id.slice(that.namespace.length + 1), state);
|
||||
// emit 'stateChange' event instantly
|
||||
setImmediate(() => that.emit('stateChange', id.slice(that.namespace.length + 1), state));
|
||||
|
||||
} else {
|
||||
if (typeof options.stateChange === 'function') options.stateChange(id, state);
|
||||
if (id.substring(0, 4) === 'log.') {
|
||||
console.log("LOG");
|
||||
}
|
||||
if (that.emit) {
|
||||
// emit 'stateChange' event instantly
|
||||
setImmediate(() => that.emit('stateChange', id, state));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
connectTimeout: (error) => {
|
||||
if (logger) logger.error(that.namespace + ' no connection to states DB');
|
||||
if (cb) cb('Timeout');
|
||||
}
|
||||
});
|
||||
|
||||
// Send message to other adapter instance or all instances of adapter
|
||||
that.sendTo = function sendTo(objName, command, message, callback) {
|
||||
if (typeof message === 'undefined') {
|
||||
message = command;
|
||||
command = 'send';
|
||||
}
|
||||
const obj = {command: command, message: message, from: 'system.adapter.' + that.namespace};
|
||||
|
||||
if (!objName.match(/^system\.adapter\./)) objName = 'system.adapter.' + objName;
|
||||
|
||||
that.log.info('sendTo "' + command + '" to ' + objName + ' from system.adapter.' + that.namespace + ': ' + JSON.stringify(message));
|
||||
|
||||
// If not specific instance
|
||||
if (!objName.match(/\.[0-9]+$/)) {
|
||||
// Send to all instances of adapter
|
||||
that.objects.getObjectView('system', 'instance', {startkey: objName + '.', endkey: objName + '.\u9999'}, (err, _obj) => {
|
||||
if (_obj) {
|
||||
for (let i = 0; i < _obj.rows.length; i++) {
|
||||
that.states.pushMessage(_obj.rows[i].id, obj);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (callback) {
|
||||
if (typeof callback === 'function') {
|
||||
// force subscribe even no messagebox enabled
|
||||
if (!that.common.messagebox && !that.mboxSubscribed) {
|
||||
that.mboxSubscribed = true;
|
||||
that.states.subscribeMessage('system.adapter.' + that.namespace);
|
||||
}
|
||||
|
||||
obj.callback = {
|
||||
message: message,
|
||||
id: callbackId++,
|
||||
ack: false,
|
||||
time: Date.now()
|
||||
};
|
||||
if (callbackId >= 0xFFFFFFFF) {
|
||||
callbackId = 1;
|
||||
}
|
||||
if (!that.callbacks) that.callbacks = {};
|
||||
that.callbacks['_' + obj.callback.id] = {cb: callback};
|
||||
|
||||
// delete too old callbacks IDs
|
||||
const now = Date.now();
|
||||
for (const _id in that.callbacks) {
|
||||
if (that.callbacks.hasOwnProperty(_id) && now - that.callbacks[_id].time > 3600000) {
|
||||
delete that.callbacks[_id];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
obj.callback = callback;
|
||||
obj.callback.ack = true;
|
||||
}
|
||||
}
|
||||
|
||||
that.states.pushMessage(objName, obj);
|
||||
}
|
||||
};
|
||||
|
||||
// Send message to specific host or to all hosts
|
||||
that.sendToHost = function sendToHost(objName, command, message, callback) {
|
||||
if (typeof message === 'undefined') {
|
||||
message = command;
|
||||
command = 'send';
|
||||
}
|
||||
const obj = {command: command, message: message, from: 'system.adapter.' + that.namespace};
|
||||
|
||||
if (objName && objName.substring(0, 'system.host.'.length) !== 'system.host.') objName = 'system.host.' + objName;
|
||||
|
||||
if (!objName) {
|
||||
// Send to all hosts
|
||||
that.objects.getObjectList({startkey: 'system.host.', endkey: 'system.host.' + '\u9999'}, null, (err, res) => {
|
||||
if (!err && res.rows.length) {
|
||||
for (let i = 0; i < res.rows.length; i++) {
|
||||
const parts = res.rows[i].id.split('.');
|
||||
// ignore system.host.name.alive and so on
|
||||
if (parts.length === 3) {
|
||||
that.states.pushMessage(res.rows[i].id, obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (callback) {
|
||||
if (typeof callback === 'function') {
|
||||
// force subscribe even no messagebox enabled
|
||||
if (!that.common.messagebox && !that.mboxSubscribed) {
|
||||
that.mboxSubscribed = true;
|
||||
that.states.subscribeMessage('system.adapter.' + that.namespace);
|
||||
}
|
||||
|
||||
obj.callback = {
|
||||
message: message,
|
||||
id: callbackId++,
|
||||
ack: false,
|
||||
time: Date.now()
|
||||
};
|
||||
if (callbackId >= 0xFFFFFFFF) callbackId = 1;
|
||||
if (!that.callbacks) that.callbacks = {};
|
||||
that.callbacks['_' + obj.callback.id] = {cb: callback};
|
||||
} else {
|
||||
obj.callback = callback;
|
||||
obj.callback.ack = true;
|
||||
}
|
||||
}
|
||||
|
||||
that.states.pushMessage(objName, obj);
|
||||
}
|
||||
};
|
||||
|
||||
that.setState = function setState(id, state, ack, options, callback) {
|
||||
if (typeof state === 'object' && typeof ack !== 'boolean') {
|
||||
callback = options;
|
||||
options = ack;
|
||||
ack = undefined;
|
||||
}
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
id = that._fixId(id, 'state');
|
||||
|
||||
if (typeof ack === 'function') {
|
||||
callback = ack;
|
||||
ack = undefined;
|
||||
}
|
||||
|
||||
if (typeof state !== 'object' || state === null || state === undefined) state = {val: state};
|
||||
|
||||
if (ack !== undefined) {
|
||||
state.ack = ack;
|
||||
}
|
||||
|
||||
state.from = 'system.adapter.' + that.namespace;
|
||||
if (options && options.user && options.user !== 'system.user.admin') {
|
||||
checkStates(id, options, 'setState', err => {
|
||||
if (err) {
|
||||
if (typeof callback === 'function') callback(err);
|
||||
} else {
|
||||
that.states.setState(id, state, callback);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
that.states.setState(id, state, callback);
|
||||
}
|
||||
};
|
||||
|
||||
that.setForeignState = function setForeignState(id, state, ack, options, callback) {
|
||||
if (typeof state === 'object' && typeof ack !== 'boolean') {
|
||||
callback = options;
|
||||
options = ack;
|
||||
ack = undefined;
|
||||
}
|
||||
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
if (typeof ack === 'function') {
|
||||
callback = ack;
|
||||
ack = undefined;
|
||||
}
|
||||
|
||||
if (typeof state !== 'object' || state === null || state === undefined) state = {val: state};
|
||||
|
||||
if (ack !== undefined) {
|
||||
state.ack = ack;
|
||||
}
|
||||
|
||||
state.from = 'system.adapter.' + that.namespace;
|
||||
|
||||
if (options && options.user && options.user !== 'system.user.admin') {
|
||||
checkStates(id, options, 'setState', err => {
|
||||
if (err) {
|
||||
if (typeof callback === 'function') callback(err);
|
||||
} else {
|
||||
that.states.setState(id, state, callback);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
that.states.setState(id, state, callback);
|
||||
}
|
||||
};
|
||||
|
||||
that.getState = function getState(id, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
id = that._fixId(id, 'state');
|
||||
if (options && options.user && options.user !== 'system.user.admin') {
|
||||
checkStates(id, options, 'getState', err => {
|
||||
if (err) {
|
||||
if (typeof callback === 'function') callback(err);
|
||||
} else {
|
||||
that.states.getState(id, callback);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
that.states.getState(id, callback);
|
||||
}
|
||||
};
|
||||
|
||||
that.getStateHistory = function getStateHistory(id, start, end, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
id = that._fixId(id, 'state');
|
||||
that.getForeignStateHistory(id, start, end, options, callback);
|
||||
};
|
||||
|
||||
that.getForeignStateHistory = function getForeignStateHistory(id, start, end, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
if (typeof start === 'function') {
|
||||
callback = start;
|
||||
start = undefined;
|
||||
end = undefined;
|
||||
} else if (typeof end === 'function') {
|
||||
callback = end;
|
||||
end = undefined;
|
||||
}
|
||||
|
||||
start = start || Math.round((new Date()).getTime() / 1000) - 31536000; // - 1 year
|
||||
end = end || Math.round((new Date()).getTime() / 1000) + 5000;
|
||||
|
||||
const history = [];
|
||||
const docs = [];
|
||||
|
||||
// get data from states
|
||||
that.log.debug('get states history ' + id + ' ' + start + ' ' + end);
|
||||
that.getFifo(id, (err, res) => {
|
||||
if (!err && res) {
|
||||
let iProblemCount = 0;
|
||||
for (let i = 0; i < res.length; i++) {
|
||||
if (!res[i]) {
|
||||
iProblemCount++;
|
||||
continue;
|
||||
}
|
||||
if (res[i].ts < start) {
|
||||
continue;
|
||||
} else if (res[i].ts > end) {
|
||||
break;
|
||||
}
|
||||
history.push(res[i]);
|
||||
}
|
||||
if (iProblemCount) that.log.warn('got null states ' + iProblemCount + ' times for ' + id);
|
||||
|
||||
that.log.debug('got ' + res.length + ' datapoints for ' + id);
|
||||
} else {
|
||||
if (err !== 'Not exists') {
|
||||
that.log.error(err);
|
||||
} else {
|
||||
that.log.debug('datapoints for ' + id + ' do not yet exist');
|
||||
}
|
||||
}
|
||||
|
||||
// fetch a history document from objectDB
|
||||
function getObjectsLog(cid, callback) {
|
||||
that.log.info('getObjectLog ' + cid);
|
||||
that.getForeignObject(cid, options, (err, res) => {
|
||||
if (!err && res.common.data) {
|
||||
for (let i = 0; i < res.common.data.length; i++) {
|
||||
if (res.common.data[i].ts < start) {
|
||||
continue;
|
||||
} else if (res.common.data[i].ts > end) {
|
||||
break;
|
||||
}
|
||||
history.push(res.common.data[i]);
|
||||
}
|
||||
} else {
|
||||
that.log.warn(cid + ' not found');
|
||||
}
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
|
||||
// queue objects history documents fetching
|
||||
function queue(ts) {
|
||||
if (ts < start) {
|
||||
callback(null, history);
|
||||
return;
|
||||
}
|
||||
const cid = 'history.' + id + '.' + ts2day(ts);
|
||||
if (docs.indexOf(cid) !== -1) {
|
||||
getObjectsLog(cid, err => queue(ts - 86400)); // - 1 day
|
||||
} else {
|
||||
queue(ts - 86400); // - 1 day
|
||||
}
|
||||
}
|
||||
|
||||
// get list of available history documents
|
||||
that.objects.getObjectList({startkey: 'history.' + id, endkey: 'history.' + id + '\u9999'}, options, (err, res) => {
|
||||
if (!err && res.rows.length) {
|
||||
for (let i = 0; i < res.rows.length; i++) {
|
||||
docs.push(res.rows[i].id);
|
||||
}
|
||||
queue(end);
|
||||
} else {
|
||||
callback(null, history);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// normally only foreign history has interest, so there is no getHistory and getForeignHistory
|
||||
that.getHistory = function getHistory(id, options, callback) {
|
||||
options = options || {};
|
||||
options.end = options.end || Math.round((new Date()).getTime() / 1000) + 5000;
|
||||
if (!options.count && !options.start) {
|
||||
options.start = options.start || Math.round((new Date()).getTime() / 1000) - 604800; // - 1 week
|
||||
}
|
||||
|
||||
if (!options.instance) {
|
||||
if (!that.defaultHistory) {
|
||||
// read default history instance from system.config
|
||||
return getDefaultHistory(() => that.getHistory(id, options, callback));
|
||||
} else {
|
||||
options.instance = that.defaultHistory;
|
||||
}
|
||||
}
|
||||
|
||||
that.sendTo(options.instance || 'history.0', 'getHistory', {id: id, options: options}, res => {
|
||||
setImmediate(() => callback(res.error, res.result, res.step));
|
||||
});
|
||||
};
|
||||
|
||||
// Convert ID adapter.instance.device.channel.state
|
||||
// Convert ID to {device: D, channel: C, state: S}
|
||||
that.idToDCS = function idToDCS(id) {
|
||||
if (!id) return null;
|
||||
const parts = id.split('.');
|
||||
if (parts[0] + '.' + parts[1] !== that.namespace) {
|
||||
that.log.warn("Try to decode id not from this adapter");
|
||||
return null;
|
||||
}
|
||||
return {device: parts[2], channel: parts[3], state: parts[4]};
|
||||
};
|
||||
|
||||
that.getForeignState = function getForeignState(id, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
if (options && options.user && options.user !== 'system.user.admin') {
|
||||
checkStates(id, options, 'getState', err => {
|
||||
if (err) {
|
||||
if (typeof callback === 'function') callback(err);
|
||||
} else {
|
||||
that.states.getState(id, callback);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
that.states.getState(id, callback);
|
||||
}
|
||||
};
|
||||
|
||||
that.delForeignState = function delForeignState(id, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
if (options && options.user && options.user !== 'system.user.admin') {
|
||||
checkStates(id, options, 'delState', err => {
|
||||
if (err) {
|
||||
if (typeof callback === 'function') callback(err);
|
||||
} else {
|
||||
that.states.delState(id, callback);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
that.states.delState(id, callback);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
that.delState = function delState(id, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
id = that._fixId(id);
|
||||
if (options && options.user && options.user !== 'system.user.admin') {
|
||||
checkStates(id, options, 'delState', err => {
|
||||
if (err) {
|
||||
if (typeof callback === 'function') callback(err);
|
||||
} else {
|
||||
that.states.delState(id, callback);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
that.states.delState(id, callback);
|
||||
}
|
||||
};
|
||||
|
||||
that.getStates = function getStates(pattern, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
pattern = that._fixId(pattern, 'state');
|
||||
that.getForeignStates(pattern, options, callback);
|
||||
};
|
||||
|
||||
that.getForeignStates = function getForeignStates(pattern, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
const list = {};
|
||||
if (typeof pattern === 'function') {
|
||||
callback = pattern;
|
||||
pattern = '*';
|
||||
}
|
||||
|
||||
if (typeof callback !== 'function') {
|
||||
logger.error('getForeignStates invalid callback for ' + pattern);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof pattern === 'object') {
|
||||
that.states.getStates(pattern, (err, arr) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < pattern.length; i++) {
|
||||
if (typeof arr[i] === 'string') arr[i] = JSON.parse(arr[i]);
|
||||
list[pattern[i]] = arr[i] || {};
|
||||
}
|
||||
callback(null, list);
|
||||
});
|
||||
return;
|
||||
}
|
||||
const keys = [];
|
||||
let params = {};
|
||||
if (pattern && pattern !== '*') {
|
||||
params = {
|
||||
startkey: pattern.replace('*', ''),
|
||||
endkey: pattern.replace('*', '\u9999')
|
||||
};
|
||||
}
|
||||
that.objects.getObjectView('system', 'state', params, options, (err, res) => {
|
||||
if (err) {
|
||||
if (typeof callback === 'function') callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < res.rows.length; i++) {
|
||||
keys.push(res.rows[i].id);
|
||||
}
|
||||
|
||||
if (options && options.user && options.user !== 'system.user.admin') {
|
||||
checkStates(keys, options, 'getState', (err, keys) => {
|
||||
if (err) {
|
||||
if (typeof callback === 'function') callback(err);
|
||||
return;
|
||||
}
|
||||
that.states.getStates(keys, function (err, arr) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < res.rows.length; i++) {
|
||||
if (typeof arr[i] === 'string') arr[i] = JSON.parse(arr[i]);
|
||||
list[keys[i]] = arr[i] || {};
|
||||
}
|
||||
if (typeof callback === 'function') callback(null, list);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
that.states.getStates(keys, function (err, arr) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < res.rows.length; i++) {
|
||||
if (typeof arr[i] === 'string') arr[i] = JSON.parse(arr[i]);
|
||||
list[keys[i]] = arr[i] || {};
|
||||
}
|
||||
if (typeof callback === 'function') callback(null, list);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
that.subscribeForeignStates = function subscribeForeignStates(pattern, options) {
|
||||
if (!pattern) pattern = '*';
|
||||
that.states.subscribe(pattern, options);
|
||||
};
|
||||
|
||||
that.unsubscribeForeignStates = function unsubscribeForeignStates(pattern, options) {
|
||||
if (!pattern) pattern = '*';
|
||||
that.states.unsubscribe(pattern, options);
|
||||
};
|
||||
|
||||
that.subscribeStates = function subscribeStates(pattern, options) {
|
||||
// Exception. Threat the '*' case automatically
|
||||
if (!pattern || pattern === '*') {
|
||||
that.states.subscribe(that.namespace + '.*', options);
|
||||
} else {
|
||||
pattern = that._fixId(pattern, 'state');
|
||||
that.states.subscribe(pattern, options);
|
||||
}
|
||||
};
|
||||
|
||||
that.unsubscribeStates = function unsubscribeStates(pattern, options) {
|
||||
if (!pattern || pattern === '*') {
|
||||
that.states.unsubscribe(that.namespace + '.*', options);
|
||||
} else {
|
||||
pattern = that._fixId(pattern, 'state');
|
||||
that.states.unsubscribe(pattern, options);
|
||||
}
|
||||
};
|
||||
|
||||
that.pushFifo = function pushFifo(id, state, callback) {
|
||||
that.states.pushFifo(id, state, callback);
|
||||
};
|
||||
|
||||
that.trimFifo = function trimFifo(id, start, end, callback) {
|
||||
that.states.trimFifo(id, start, end, callback);
|
||||
};
|
||||
|
||||
that.getFifoRange = function getFifoRange(id, start, end, callback) {
|
||||
that.states.getFifoRange(id, start, end, callback);
|
||||
};
|
||||
|
||||
that.getFifo = function getFifo(id, callback) {
|
||||
that.states.getFifo(id, callback);
|
||||
};
|
||||
|
||||
that.lenFifo = function lenFifo(id, callback) {
|
||||
that.states.lenFifo(id, callback);
|
||||
};
|
||||
|
||||
that.subscribeFifo = function subscribeFifo(pattern) {
|
||||
that.states.subscribeFifo(pattern);
|
||||
};
|
||||
|
||||
that.getSession = function getSession(id, callback) {
|
||||
that.states.getSession(id, callback);
|
||||
};
|
||||
that.setSession = function setSession(id, ttl, data, callback) {
|
||||
that.states.setSession(id, ttl, data, callback);
|
||||
};
|
||||
that.destroySession = function destroySession(id, callback) {
|
||||
that.states.destroySession(id, callback);
|
||||
};
|
||||
|
||||
that.getMessage = function getMessage(callback) {
|
||||
that.states.getMessage('system.adapter.' + that.namespace, callback);
|
||||
};
|
||||
|
||||
that.lenMessage = function lenMessage(callback) {
|
||||
that.states.lenMessage('system.adapter.' + that.namespace, callback);
|
||||
};
|
||||
|
||||
// Write binary block into redis, e.g image
|
||||
that.setBinaryState = function setBinaryState(id, binary, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
that.states.setBinaryState(id, binary, callback);
|
||||
};
|
||||
|
||||
// Read binary block fromredis, e.g. image
|
||||
that.getBinaryState = function getBinaryState(id, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
that.states.getBinaryState(id, callback);
|
||||
};
|
||||
|
||||
logger.debug(that.namespace + ' statesDB connected');
|
||||
|
||||
if (typeof cb === 'function') {
|
||||
setImmediate(() => cb(), 0);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
module.exports = States;
|
||||
92
test/testPackageFiles.js
Normal file
92
test/testPackageFiles.js
Normal file
@@ -0,0 +1,92 @@
|
||||
/* jshint -W097 */
|
||||
/* jshint strict:false */
|
||||
/* jslint node: true */
|
||||
/* jshint expr: true */
|
||||
'use strict';
|
||||
const expect = require('chai').expect;
|
||||
const fs = require('fs');
|
||||
|
||||
describe('Test package.json and io-package.json', () => {
|
||||
it('Test package files', function (done) {
|
||||
console.log();
|
||||
|
||||
const fileContentIOPackage = fs.readFileSync(__dirname + '/../io-package.json', 'utf8');
|
||||
const ioPackage = JSON.parse(fileContentIOPackage);
|
||||
|
||||
const fileContentNPMPackage = fs.readFileSync(__dirname + '/../package.json', 'utf8');
|
||||
const npmPackage = JSON.parse(fileContentNPMPackage);
|
||||
|
||||
expect(ioPackage).to.be.an('object');
|
||||
expect(npmPackage).to.be.an('object');
|
||||
|
||||
expect(ioPackage.common.version, 'ERROR: Version number in io-package.json needs to exist').to.exist;
|
||||
expect(npmPackage.version, 'ERROR: Version number in package.json needs to exist').to.exist;
|
||||
|
||||
expect(ioPackage.common.version, 'ERROR: Version numbers in package.json and io-package.json needs to match').to.be.equal(npmPackage.version);
|
||||
|
||||
if (!ioPackage.common.news || !ioPackage.common.news[ioPackage.common.version]) {
|
||||
console.log('WARNING: No news entry for current version exists in io-package.json, no rollback in Admin possible!');
|
||||
console.log();
|
||||
}
|
||||
|
||||
expect(npmPackage.author, 'ERROR: Author in package.json needs to exist').to.exist;
|
||||
expect(ioPackage.common.authors, 'ERROR: Authors in io-package.json needs to exist').to.exist;
|
||||
|
||||
if (ioPackage.common.name.indexOf('template') !== 0) {
|
||||
if (Array.isArray(ioPackage.common.authors)) {
|
||||
expect(ioPackage.common.authors.length, 'ERROR: Author in io-package.json needs to be set').to.not.be.equal(0);
|
||||
if (ioPackage.common.authors.length === 1) {
|
||||
expect(ioPackage.common.authors[0], 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name <my@email.com>');
|
||||
}
|
||||
}
|
||||
else {
|
||||
expect(ioPackage.common.authors, 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name <my@email.com>');
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.log('WARNING: Testing for set authors field in io-package skipped because template adapter');
|
||||
console.log();
|
||||
}
|
||||
expect(fs.existsSync(__dirname + '/../README.md'), 'ERROR: README.md needs to exist! Please create one with description, detail information and changelog. English is mandatory.').to.be.true;
|
||||
if (!ioPackage.common.titleLang || typeof ioPackage.common.titleLang !== 'object') {
|
||||
console.log('WARNING: titleLang is not existing in io-package.json. Please add');
|
||||
console.log();
|
||||
}
|
||||
if (
|
||||
ioPackage.common.title.indexOf('iobroker') !== -1 ||
|
||||
ioPackage.common.title.indexOf('ioBroker') !== -1 ||
|
||||
ioPackage.common.title.indexOf('adapter') !== -1 ||
|
||||
ioPackage.common.title.indexOf('Adapter') !== -1
|
||||
) {
|
||||
console.log('WARNING: title contains Adapter or ioBroker. It is clear anyway, that it is adapter for ioBroker.');
|
||||
console.log();
|
||||
}
|
||||
|
||||
if (ioPackage.common.name.indexOf('vis-') !== 0) {
|
||||
if (!ioPackage.common.materialize || !fs.existsSync(__dirname + '/../admin/index_m.html') || !fs.existsSync(__dirname + '/../gulpfile.js')) {
|
||||
console.log('WARNING: Admin3 support is missing! Please add it');
|
||||
console.log();
|
||||
}
|
||||
if (ioPackage.common.materialize) {
|
||||
expect(fs.existsSync(__dirname + '/../admin/index_m.html'), 'Admin3 support is enabled in io-package.json, but index_m.html is missing!').to.be.true;
|
||||
}
|
||||
}
|
||||
|
||||
const licenseFileExists = fs.existsSync(__dirname + '/../LICENSE');
|
||||
const fileContentReadme = fs.readFileSync(__dirname + '/../README.md', 'utf8');
|
||||
if (fileContentReadme.indexOf('## Changelog') === -1) {
|
||||
console.log('Warning: The README.md should have a section ## Changelog');
|
||||
console.log();
|
||||
}
|
||||
expect((licenseFileExists || fileContentReadme.indexOf('## License') !== -1), 'A LICENSE must exist as LICENSE file or as part of the README.md').to.be.true;
|
||||
if (!licenseFileExists) {
|
||||
console.log('Warning: The License should also exist as LICENSE file');
|
||||
console.log();
|
||||
}
|
||||
if (fileContentReadme.indexOf('## License') === -1) {
|
||||
console.log('Warning: The README.md should also have a section ## License to be shown in Admin3');
|
||||
console.log();
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
264
test/testServer.js
Normal file
264
test/testServer.js
Normal file
@@ -0,0 +1,264 @@
|
||||
/* jshint -W097 */
|
||||
// jshint strict:true
|
||||
/*jslint node: true */
|
||||
/*jslint esversion: 6 */
|
||||
'use strict';
|
||||
let expect = require('chai').expect;
|
||||
let setup = require(__dirname + '/lib/setup');
|
||||
|
||||
let objects = null;
|
||||
let states = null;
|
||||
let mqttClientEmitter = null;
|
||||
let mqttClientDetector = null;
|
||||
let connected = false;
|
||||
let lastReceivedTopic1;
|
||||
let lastReceivedMessage1;
|
||||
let lastReceivedTopic2;
|
||||
let lastReceivedMessage2;
|
||||
|
||||
let clientConnected1 = false;
|
||||
let clientConnected2 = false;
|
||||
let brokerStarted = false;
|
||||
|
||||
let rules = {
|
||||
'tele/sonoff_4ch/STATE': {send: '{"Time":"2017-10-02T19:26:06", "Uptime":0, "Vcc":3.226, "POWER1":"OFF", "POWER2":"OFF", "POWER3":"OFF", "POWER4":"OFF", "Wifi":{"AP":1, "SSId":"AAA", "RSSI": 15}}', expect: {Vcc: 3.226, Wifi_RSSI: 15}},
|
||||
'tele/sonoff/SENSOR': {send: '{"Time":"2017-10-05T17:43:19", "DS18x20":{"DS1":{"Type":"DS18B20", "Address":"28FF9A9876815022A", "Temperature":12.2}}, "TempUnit":"C"}', expect: {DS18x20_DS1_Temperature: 12.2}},
|
||||
'tele/sonoff5/SENSOR': {send: '{"Time":"2017-10-03T14:02:25", "AM2301-14":{"Temperature":21.6, "Humidity":54.7}, "TempUnit":"C"}', expect: {'AM2301-14_Temperature': 21.6, 'AM2301-14_Humidity': 54.7}},
|
||||
'tele/SonoffPOW/INFO1': {send: '{"Module":"Sonoff Pow", "Version":"5.8.0", "FallbackTopic":"SonoffPOW", "GroupTopic":"sonoffs"}', expect: {'INFO.Module': 'Sonoff Pow', 'INFO.Version': '5.8.0'}},
|
||||
'tele/SonoffPOW/INFO2': {send: '{"WebServerMode":"Admin", "Hostname":"Sonoffpow", "IPAddress":"192.168.2.182"}', expect: {'INFO.Hostname': 'Sonoffpow', 'INFO.IPAddress': '192.168.2.182'}},
|
||||
'tele/SonoffPOW/INFO3': {send: '{"RestartReason":"Software/System restart"}', expect: {'INFO.RestartReason': 'Software/System restart'}},
|
||||
'tele/sonoff_4ch/ENERGY': {send: '{"Time":"2017-10-02T19:24:32", "Total":1.753, "Yesterday":0.308, "Today":0.205, "Period":0, "Power":3, "Factor":0.12, "Voltage":221, "Current":0.097}', expect: {'ENERGY.Total': 1.753, 'ENERGY.Current': 0.097}},
|
||||
'tele/sonoff_4ch/ENERGY1': {send: '"Time":"2017-10-02T19:24:32", "Total":1.753, "Yesterday":0.308, "Today":0.205, "Period":0, "Power":3, "Factor":0.12, "Voltage":221, "Current":0.097}', expect: {}},
|
||||
'tele/sonoff_1ch/STATE': {send: '{"Time":"2017-10-02T19:24:32", "Color": "112233"}', expect: {}},
|
||||
'tele/sonoff/STATE': {send: '{"Time":"2018-06-19T06:39:33","Uptime":"0T23:47:32","Vcc":3.482,"POWER":"OFF","Dimmer":100,"Color":"000000FF","HSBColor":"0,0,0","Channel":[0,0,0,100],"Scheme":0,"Fade":"OFF","Speed":4,"LedTable":"OFF","Wifi":{"AP":1,"SSId":"WLAN-7490","RSSI":50,"APMac":"34:31:C4:C6:EB:0F"}}',
|
||||
expect:{}},
|
||||
'tele/sonoff1/SENSOR': {send: '{"Time":"2018-06-15T10:03:24","DS18B20":{"Temperature":0.0},"TempUnit":"C"}', expect: {'DS18B20_Temperature': 0}},
|
||||
'/ESP_BOX/BM280/Pressure': {send: '1010.09', expect: {'Pressure': 1010.09}},
|
||||
'/ESP_BOX/BM280/Humidity': {send: '42.39', expect: {'Humidity': 42.39}},
|
||||
'/ESP_BOX/BM280/Temperature': {send: '25.86', expect: {'Temperature': 25.86}},
|
||||
'/ESP_BOX/BM280/Approx. Altitude': {send: '24', expect: {'Approx_Altitude': 24}},
|
||||
'stat/sonoff/POWER': {send: 'ON', expect: {'POWER': true}},
|
||||
'cmnd/sonoff/POWER': {send: '', expect: {}},
|
||||
'stat/sonoff/RESULT': {send: '{"POWER": "ON"}', expect: {'RESULT': null}},
|
||||
'stat/sonoff/LWT': {send: 'someTopic', expect: {'LWT': null}},
|
||||
'stat/sonoff/ABC': {send: 'text', expect: {'ABC': null}}
|
||||
};
|
||||
function decrypt(key, value) {
|
||||
let result = '';
|
||||
for (let i = 0; i < value.length; ++i) {
|
||||
result += String.fromCharCode(key[i % key.length].charCodeAt(0) ^ value.charCodeAt(i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function startClients(_done) {
|
||||
// start mqtt client
|
||||
const MqttClient = require(__dirname + '/lib/mqttClient.js');
|
||||
|
||||
// Start client to emit topics
|
||||
mqttClientEmitter = new MqttClient(connected => {
|
||||
// on connected
|
||||
if (connected) {
|
||||
console.log('Test MQTT Emitter is connected to MQTT broker');
|
||||
clientConnected1 = true;
|
||||
if (_done && brokerStarted && clientConnected1 && clientConnected2) {
|
||||
_done();
|
||||
_done = null;
|
||||
}
|
||||
}
|
||||
}, (topic, message) => {
|
||||
console.log(Date.now() + ' emitter received ' + topic + ': ' + message.toString());
|
||||
// on receive
|
||||
lastReceivedTopic1 = topic;
|
||||
lastReceivedMessage1 = message ? message.toString() : null;
|
||||
}, {name: 'Emitter*1', user: 'user', pass: 'pass1'});
|
||||
|
||||
// Start client to receive topics
|
||||
mqttClientDetector = new MqttClient(connected => {
|
||||
// on connected
|
||||
if (connected) {
|
||||
console.log('Test MQTT Detector is connected to MQTT broker');
|
||||
clientConnected2 = true;
|
||||
if (_done && brokerStarted && clientConnected1 && clientConnected2) {
|
||||
_done();
|
||||
_done = null;
|
||||
}
|
||||
}
|
||||
}, (topic, message) => {
|
||||
console.log(Date.now() + ' detector received ' + topic + ': ' + message.toString());
|
||||
// on receive
|
||||
lastReceivedTopic2 = topic;
|
||||
lastReceivedMessage2 = message ? message.toString() : null;
|
||||
console.log(JSON.stringify(lastReceivedMessage2));
|
||||
}, {name: 'Detector-1', user: 'user', pass: 'pass1'});
|
||||
}
|
||||
|
||||
function checkMqtt2Adapter(id, task, _it, _done) {
|
||||
_it.timeout(1000);
|
||||
|
||||
lastReceivedMessage1 = null;
|
||||
lastReceivedTopic1 = null;
|
||||
lastReceivedTopic2 = null;
|
||||
lastReceivedMessage2 = null;
|
||||
|
||||
console.log(`[${new Date().toISOString()}] Publish ${id}: ${task.send}`);
|
||||
mqttClientEmitter.publish(id, task.send, err => {
|
||||
expect(err).to.be.undefined;
|
||||
|
||||
setTimeout(() => {
|
||||
let count = 0;
|
||||
for (let e in task.expect) {
|
||||
if (! task.expect.hasOwnProperty(e)) continue;
|
||||
count++;
|
||||
(function (_id, _val) {
|
||||
objects.getObject('sonoff.0.Emitter_1.' + _id, (err, obj) => {
|
||||
if (_val !== null) {
|
||||
if (!obj) console.error('Object sonoff.0.Emitter_1.' + _id + ' not found');
|
||||
expect(obj).to.be.not.null.and.not.undefined;
|
||||
expect(obj._id).to.be.equal('sonoff.0.Emitter_1.' + _id);
|
||||
expect(obj.type).to.be.equal('state');
|
||||
|
||||
states.getState(obj._id, function (err, state) {
|
||||
expect(state).to.be.not.null.and.not.undefined;
|
||||
expect(state.val).to.be.equal(_val);
|
||||
expect(state.ack).to.be.true;
|
||||
if (!--count) _done();
|
||||
});
|
||||
} else {
|
||||
expect(obj).to.be.undefined;
|
||||
|
||||
states.getState('sonoff.0.Emitter_1.' + _id, (err, state) => {
|
||||
expect(state).to.be.undefined;
|
||||
if (!--count) _done();
|
||||
});
|
||||
}
|
||||
});
|
||||
})(e, task.expect[e]);
|
||||
}
|
||||
if (!count) _done();
|
||||
}, 200);
|
||||
});
|
||||
}
|
||||
|
||||
function checkAdapter2Mqtt(id, mqttid, value, _done) {
|
||||
console.log(new Date().toISOString() + ' Send ' + id + ' with value '+ value);
|
||||
|
||||
lastReceivedTopic1 = null;
|
||||
lastReceivedMessage1 = null;
|
||||
lastReceivedTopic2 = null;
|
||||
lastReceivedMessage2 = null;
|
||||
|
||||
states.setState(id, {
|
||||
val: value,
|
||||
ack: false
|
||||
}, (err, id) => {
|
||||
setTimeout(() => {
|
||||
if (!lastReceivedTopic1) {
|
||||
setTimeout(() => {
|
||||
expect(lastReceivedTopic1).to.be.equal(mqttid);
|
||||
expect(lastReceivedMessage1).to.be.equal(value ? 'ON' : 'OFF');
|
||||
_done();
|
||||
}, 200);
|
||||
} else {
|
||||
expect(lastReceivedTopic1).to.be.equal(mqttid);
|
||||
expect(lastReceivedMessage1).to.be.equal(value ? 'ON' : 'OFF');
|
||||
_done();
|
||||
}
|
||||
}, 400);
|
||||
});
|
||||
}
|
||||
|
||||
function checkConnection(value, done, counter) {
|
||||
counter = counter || 0;
|
||||
if (counter > 20) {
|
||||
done && done('Cannot check ' + value);
|
||||
return;
|
||||
}
|
||||
|
||||
states.getState('sonoff.0.info.connection', (err, state) => {
|
||||
if (err) console.error(err);
|
||||
if (state && typeof state.val === 'string' && ((value && state.val.indexOf(',') !== -1) || (!value && state.val.indexOf(',') === -1))) {
|
||||
connected = value;
|
||||
done();
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
checkConnection(value, done, counter + 1);
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
describe('Sonoff server: Test mqtt server', () => {
|
||||
before('Sonoff server: Start js-controller', function (_done) { //
|
||||
this.timeout(600000); // because of first install from npm
|
||||
setup.adapterStarted = false;
|
||||
|
||||
setup.setupController(systemConfig => {
|
||||
let config = setup.getAdapterConfig();
|
||||
// enable adapter
|
||||
config.common.enabled = true;
|
||||
config.common.loglevel = 'debug';
|
||||
config.native.user = 'user';
|
||||
config.native.pass = decrypt(systemConfig.native.secret, 'pass1');
|
||||
|
||||
setup.setAdapterConfig(config.common, config.native);
|
||||
|
||||
setup.startController((_objects, _states) => {
|
||||
objects = _objects;
|
||||
states = _states;
|
||||
brokerStarted = true;
|
||||
if (_done && brokerStarted && clientConnected1 && clientConnected2) {
|
||||
_done();
|
||||
_done = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
startClients(_done);
|
||||
});
|
||||
|
||||
it('Sonoff Server: Check if connected to MQTT broker', done => {
|
||||
if (!connected) {
|
||||
checkConnection(true, done);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
}).timeout(2000);
|
||||
|
||||
for (let r in rules) {
|
||||
(function(id, task) {
|
||||
it('Sonoff Server: Check receive ' + id, function (done) { // let FUNCTION here
|
||||
checkMqtt2Adapter(id, task, this, done);
|
||||
});
|
||||
})(r, rules[r]);
|
||||
}
|
||||
|
||||
// give time to client to receive all messages
|
||||
it('wait', done => {
|
||||
setTimeout(() => done(), 1000);
|
||||
}).timeout(3000);
|
||||
|
||||
it('Sonoff server: detector must receive cmnd/sonoff/POWER', done => {
|
||||
checkAdapter2Mqtt('sonoff.0.Emitter_1.POWER', 'cmnd/sonoff/POWER', false, done);
|
||||
}).timeout(2000);
|
||||
|
||||
it('Sonoff Server: check reconnection', done => {
|
||||
mqttClientEmitter.stop();
|
||||
mqttClientDetector.stop();
|
||||
checkConnection(false, error => {
|
||||
expect(error).to.be.not.ok;
|
||||
startClients();
|
||||
checkConnection(true, error => {
|
||||
expect(error).to.be.not.ok;
|
||||
done();
|
||||
});
|
||||
});
|
||||
}).timeout(10000);
|
||||
|
||||
after('Sonoff Server: Stop js-controller', function (_done) { // let FUNCTION and not => here
|
||||
this.timeout(5000);
|
||||
mqttClientEmitter.stop();
|
||||
mqttClientDetector.stop();
|
||||
setup.stopController(() => _done());
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user