You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

419 lines
15 KiB

/**
*
* template adapter
*
*
* file io-package.json comments:
*
* {
* "common": {
* "name": "template", // name has to be set and has to be equal to adapters folder name and main file name excluding extension
* "version": "0.0.0", // use "Semantic Versioning"! see http://semver.org/
* "title": "Node.js template Adapter", // Adapter title shown in User Interfaces
* "authors": [ // Array of authord
* "name <mail@template.com>"
* ]
* "desc": "template adapter", // Adapter description shown in User Interfaces. Can be a language object {de:"...",ru:"..."} or a string
* "platform": "Javascript/Node.js", // possible values "javascript", "javascript/Node.js" - more coming
* "mode": "daemon", // possible values "daemon", "schedule", "subscribe"
* "schedule": "0 0 * * *" // cron-style schedule. Only needed if mode=schedule
* "loglevel": "info" // Adapters Log Level
* },
* "native": { // the native object is available via adapter.config in your adapters code - use it for configuration
* "test1": true,
* "test2": 42,
*
* "port": 9090, // port where the REST service will be started. It should has name "port", because it can be changed from console.
* "auth": false, // if basic authentication is required. If enabled, the secure connection should be used.
* "secure": false, // If SSL communication must be used. Should has the name "secure", because it can be changed from console.
* "bind": "0.0.0.0", // Specific address, where the server will be started. "0.0.0.0" means that it will be available for all addresses. Should has the name "bind", because it can be changed from console.
* "defaultUser": "admin", // If authentication is disabled, here can be specified as which user the requests will be done to yunkong2 DB.
* "certPublic": "defaultPublic",// Required for SSL communication: name of some public certificate from yunkong2 DB
* "certPrivate": "defaultPrivate"// Required for SSL communication: name of some private key from yunkong2 DB
*
* "pollURL": "", // Request all 30 seconds the JSON from this URL, parse it and store in yunkong2
* "interval": 30000 // polling interval
* }
* }
*
* You can read here, how REST API could be implementer: https://scotch.io/tutorials/build-a-restful-api-using-node-and-express-4
*/
/* jshint -W097 */// jshint strict:false
/*jslint node: true */
'use strict';
// you have to require the utils module and call adapter function
var utils = require(__dirname + '/lib/utils'); // Get common adapter utils
// load additional libraries
var express = require('express'); // call express
var request = null; // will be initialized later if polling enabled
// you have to call the adapter function and pass a options object
// name has to be set and has to be equal to adapters folder name and main file name excluding extension
// adapter will be restarted automatically every time as the configuration changed, e.g system.adapter.template.0
var adapter = utils.Adapter('rest');
var LE = require(utils.controllerDir + '/lib/letsencrypt.js');
// REST server
var webServer = null;
var app = null;
var router = null;
var timer = null;
// is called when adapter shuts down - callback has to be called under any circumstances!
adapter.on('unload', function (callback) {
try {
adapter.log.info('cleaned everything up...');
if (webServer) {
webServer.close();
webServer = null;
}
if (timer) clearInterval(timer);
callback();
} catch (e) {
callback();
}
});
// is called if a subscribed object changes
adapter.on('objectChange', function (id, obj) {
// Warning, obj can be null if it was deleted
adapter.log.info('objectChange ' + id + ' ' + JSON.stringify(obj));
});
// is called if a subscribed state changes
adapter.on('stateChange', function (id, state) {
// Warning, state can be null if it was deleted
adapter.log.info('stateChange ' + id + ' ' + JSON.stringify(state));
// you can use the ack flag to detect if it is status (true) or command (false)
if (state && !state.ack) {
adapter.log.info('ack is not set!');
}
});
// Some message was sent to adapter instance over message box. Used by email, pushover, text2speech, ...
adapter.on('message', function (obj) {
if (typeof obj == 'object' && obj.message) {
if (obj.command == 'send') {
// e.g. send email or pushover or whatever
console.log('send command');
// Send response in callback if required
if (obj.callback) adapter.sendTo(obj.from, obj.command, 'Message received', obj.callback);
}
}
});
// is called when databases are connected and adapter received configuration.
// start here!
adapter.on('ready', function () {
main();
});
function addRoutes(_router) {
// test route to make sure everything is working (accessed at GET http://localhost:9090/api)
_router.get('/', function (req, res) {
res.json({message: 'Welcome to our JSON REST api!'});
});
_router.get('/plain/', function(req, res) {
res.send('Welcome to our text REST api!');
});
// on routes that end in /plain/:id
// ----------------------------------------------------
_router.route('/plain/:id')
// get the bear with that id (accessed at GET http://localhost:8080/api/plain/:id)
.get(function (req, res) {
adapter.getForeignState(req.params.id, {user: req.user}, function (err, state) {
if (err) {
res.status(500).send(err);
} else if (!state) {
res.status(500).send('not found');
} else {
res.send('Value: ' + state.val);
}
});
})// create a handler for post (accessed at POST http://localhost:8080/api/bears)
.post(function (req, res) {
adapter.setForeignState(req.params.id, req.body, {user: req.user}, function (err, state) {
if (err) {
res.status(500).send(err);
} else if (!state) {
res.status(500).send('not found');
} else {
res.send('Value used');
}
});
});
// handler for get over JSON
_router.route('/:id')
// get the bear with that id (accessed at GET http://localhost:8080/api/plain/:id)
.get(function (req, res) {
adapter.getForeignState(req.params.id, {user: req.user}, function (err, state) {
if (err) {
res.status(500).send({error: err});
} else {
res.json(state);
}
});
});
}
function initWebServer(settings) {
app = express();
router = express.Router();
// install authentication
app.get('/', function (req, res) {
if (settings.auth) {
var b64auth = (req.headers.authorization || '').split(' ')[1] || '';
var loginPass = new Buffer(b64auth, 'base64').toString().split(':');
var login = loginPass[0];
var password = loginPass[1];
// Check in yunkong2 user and password
adapter.checkPassword(login, password, function (result) {
if (!result) {
adapter.log.error('Wrong user or password: ' + login);
res.set('WWW-Authenticate', 'Basic realm="nope"');
res.status(401).send('You shall not pass.');
} else {
req.user = login;
}
});
} else {
req.user = settings.defaultUser;
}
});
// add route cases
addRoutes(router);
// REGISTER OUR ROUTES -------------------------------
// all of our routes will be prefixed with /api
app.use('/api', router);
if (settings.port) {
if (settings.secure) {
if (!adapter.config.certificates) {
adapter.log.error('certificates missing');
return null;
}
}
webServer = LE.createServer(app, adapter.config, adapter.config.certificates, adapter.config.leConfig, adapter.log);
adapter.getPort(settings.port, function (port) {
if (port != settings.port && !adapter.config.findNextPort) {
adapter.log.error('port ' + settings.port + ' already in use');
process.exit(1);
}
webServer.listen(port, settings.bind, function() {
adapter.log.info('Server listening on http' + (settings.secure ? 's' : '') + '://' + settings.bind + ':' + port);
});
});
} else {
adapter.log.error('port missing');
process.exit(1);
}
}
function pollData() {
// you can read about module "request" here: https://www.npmjs.com/package/request
request = request || require('request'); // load library
request(adapter.config.pollURL, function (error, response, body) {
if (error || response.statusCode !== 200) {
adapter.log.error(error || response.statusCode);
} else {
// try to parse answer
try {
var data = JSON.parse(body);
// do something with data
adapter.log.info(JSON.parse(data));
} catch (e) {
adapter.log.error('Cannot parse answer');
}
}
});
}
//zhongjin add
function createExtendObject(id, objData, callback) {
adapter.getObject(id, function (err, oldObj) {
if (!err && oldObj) {
adapter.extendObject(id, objData, callback);
} else {
adapter.setObjectNotExists(id, objData, callback);
}
});
}
function processTasks(tasks, callback) {
if (!tasks || !tasks.length) {
if (typeof callback === 'function') callback();
return;
}
let task = tasks.shift();
if (task.name === 'add') {
createExtendObject(task.id, task.obj, function () {
setTimeout(processTasks, 0, tasks, callback);
});
} else if (task.name === 'del') {
adapter.delObject(task.id, function () {
setTimeout(processTasks, 0, tasks, callback);
});
} else if (task.name === 'syncEnums') {
syncEnums('rooms', task.id, task.obj, function () {
setTimeout(processTasks, 0, tasks, callback);
});
} else {
throw 'Unknown task';
}
}
function checkObjects(options, regType, regName, regFullName, tasks, newObjects) {
let params = config.params;
/*
let options = {
config: {
type: params.type || 'tcp',
slave: params.slave,
round: parseInt(params.round, 10) || 0,
timeout: parseInt(params.timeout, 10) || 5000,
defaultDeviceId: (params.deviceId === undefined || params.deviceId === null) ? 1 : (parseInt(params.deviceId, 10) || 0),
},
devices: {}
};
options.config.round = Math.pow(10, options.config.round);
*/
}
function prepareConfig(config) {
}
function parseConfig(callback) {
let options = prepareConfig(adapter.config);
const params = adapter.config.params;
let tasks = [];
tasks.push({
id: 'info.pollTime',
name: 'add',
obj: {
type: 'state',
common: {
name: 'Poll time',
type: 'number',
role: '',
write: false,
read: true,
def: 0,
unit: 'ms'
},
native: {}
}
});
processTasks(tasks, function () {
// oldObjects = [];
// newObjects = [];
adapter.subscribeStates('*');
callback(options);
});
}
function main() {
//zhongjin add
parseConfig(options => {
});
//end zhongjin
// The adapters config (in the instance object everything under the attribute "native") is accessible via
// adapter.config:
adapter.log.info('config test1: ' + adapter.config.test1);
adapter.log.info('config test1: ' + adapter.config.test2);
/**
*
* For every state in the system there has to be also an object of type state
*
* Here a simple template for a boolean variable named "testVariable"
*
* Because every adapter instance uses its own unique namespace variable names can't collide with other adapters variables
*
*/
adapter.setObject('testVariable', {
type: 'state',
common: {
name: 'testVariable',
type: 'boolean',
role: 'indicator'
},
native: {}
});
// in this template all states changes inside the adapters namespace are subscribed
adapter.subscribeStates('*');
/**
* setState examples
*
* you will notice that each setState will cause the stateChange event to fire (because of above subscribeStates cmd)
*
*/
// the variable testVariable is set to true as command (ack=false)
adapter.setState('testVariable', true);
// same thing, but the value is flagged "ack"
// ack should be always set to true if the value is received from or acknowledged from the target system
adapter.setState('testVariable', {val: true, ack: true});
// same thing, but the state is deleted after 30s (getState will return null afterwards)
adapter.setState('testVariable', {val: true, ack: true, expire: 30});
// try to load certificates
if (adapter.config.secure) {
// Load certificates
// Load certificates
adapter.getCertificates(function (err, certificates, leConfig) {
adapter.config.certificates = certificates;
adapter.config.leConfig = leConfig;
initWebServer(adapter.config);
});
} else {
initWebServer(adapter.config);
}
// Convert port to number
adapter.config.interval = parseInt(adapter.config.interval, 10);
// If interval and URl are set => poll it every X milliseconds
if (adapter.config.interval && adapter.config.pollURL) {
// initial poll
pollData();
// poll every x milliseconds
timer = setInterval(pollData, adapter.config.interval);
}
}