commit
ad1e4b76d6
@ -0,0 +1,3 @@
|
||||
/node_modules
|
||||
/.idea
|
||||
/tmp
|
@ -0,0 +1,10 @@
|
||||
Gruntfile.js
|
||||
tasks
|
||||
node_modules
|
||||
.idea
|
||||
.git
|
||||
/node_modules
|
||||
test
|
||||
tmp
|
||||
.travis.yml
|
||||
appveyor.yml
|
@ -0,0 +1,23 @@
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
language: node_js
|
||||
node_js:
|
||||
- '4'
|
||||
- '6'
|
||||
- '8'
|
||||
- '10'
|
||||
before_script:
|
||||
- export NPMVERSION=$(echo "$($(which npm) -v)"|cut -c1)
|
||||
- 'if [[ $NPMVERSION == 5 ]]; then npm install -g npm@5; fi'
|
||||
- npm -v
|
||||
- npm install winston@2.3.1
|
||||
- 'npm install https://git.spacen.net/yunkong2/yunkong2.js-controller/tarball/master --production'
|
||||
env:
|
||||
- CXX=g++-4.8
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
@ -0,0 +1,222 @@
|
||||
// To use this file in WebStorm, right click on the file name in the Project Panel (normally left) and select "Open Grunt Console"
|
||||
|
||||
/** @namespace __dirname */
|
||||
/* jshint -W097 */// jshint strict:false
|
||||
/*jslint node: true */
|
||||
"use strict";
|
||||
|
||||
module.exports = function (grunt) {
|
||||
|
||||
var srcDir = __dirname + '/';
|
||||
var pkg = grunt.file.readJSON('package.json');
|
||||
var adaptName = pkg.name.substring('yunkong2.'.length);
|
||||
var iopackage = grunt.file.readJSON('io-package.json');
|
||||
var version = (pkg && pkg.version) ? pkg.version : iopackage.common.version;
|
||||
var newname = grunt.option('name');
|
||||
var author = grunt.option('author') || 'bluefox';
|
||||
var email = grunt.option('email') || 'dogafox@gmail.com';
|
||||
var fs = require('fs');
|
||||
|
||||
// check arguments
|
||||
if (process.argv[2] == 'rename') {
|
||||
console.log('Try to rename to "' + newname + '"');
|
||||
if (!newname) {
|
||||
console.log('Please write the new template name, like: "grunt rename --name=mywidgetset" --author="Author Name"');
|
||||
process.exit();
|
||||
}
|
||||
if (newname.indexOf(' ') != -1) {
|
||||
console.log('Name may not have space in it.');
|
||||
process.exit();
|
||||
}
|
||||
if (newname.toLowerCase() != newname) {
|
||||
console.log('Name must be lower case.');
|
||||
process.exit();
|
||||
}
|
||||
if (fs.existsSync(__dirname + '/admin/owntracks.png')) {
|
||||
fs.renameSync(__dirname + '/admin/owntracks.png', __dirname + '/admin/' + newname + '.png');
|
||||
}
|
||||
if (fs.existsSync(__dirname + '/widgets/owntracks.html')) {
|
||||
fs.renameSync(__dirname + '/widgets/owntracks.html', __dirname + '/widgets/' + newname + '.html');
|
||||
}
|
||||
if (fs.existsSync(__dirname + '/widgets/template/js/owntracks.js')) {
|
||||
fs.renameSync(__dirname + '/widgets/template/js/owntracks.js', __dirname + '/widgets/template/js/' + newname + '.js');
|
||||
}
|
||||
if (fs.existsSync(__dirname + '/widgets/owntracks')) {
|
||||
fs.renameSync(__dirname + '/widgets/owntracks', __dirname + '/widgets/' + newname);
|
||||
}
|
||||
}
|
||||
|
||||
// Project configuration.
|
||||
grunt.initConfig({
|
||||
pkg: pkg,
|
||||
|
||||
replace: {
|
||||
version: {
|
||||
options: {
|
||||
patterns: [
|
||||
{
|
||||
match: /version: *"[\.0-9]*"/,
|
||||
replacement: 'version: "' + version + '"'
|
||||
},
|
||||
{
|
||||
match: /"version": *"[\.0-9]*",/g,
|
||||
replacement: '"version": "' + version + '",'
|
||||
},
|
||||
{
|
||||
match: /version: *"[\.0-9]*",/g,
|
||||
replacement: 'version: "' + version + '",'
|
||||
}
|
||||
]
|
||||
},
|
||||
files: [
|
||||
{
|
||||
expand: true,
|
||||
flatten: true,
|
||||
src: [
|
||||
srcDir + 'package.json',
|
||||
srcDir + 'io-package.json'
|
||||
],
|
||||
dest: srcDir
|
||||
},
|
||||
{
|
||||
expand: true,
|
||||
flatten: true,
|
||||
src: [
|
||||
srcDir + 'widgets/' + adaptName + '.html'
|
||||
],
|
||||
dest: srcDir + 'widgets'
|
||||
},
|
||||
{
|
||||
expand: true,
|
||||
flatten: true,
|
||||
src: [
|
||||
srcDir + 'widgets/' + adaptName + '/js/' + adaptName + '.js'
|
||||
],
|
||||
dest: srcDir + 'widgets/' + adaptName + '/js/'
|
||||
}
|
||||
]
|
||||
},
|
||||
name: {
|
||||
options: {
|
||||
patterns: [
|
||||
{
|
||||
match: /template\-rest/g,
|
||||
replacement: newname
|
||||
},
|
||||
{
|
||||
match: /Template\-rest/g,
|
||||
replacement: newname ? (newname[0].toUpperCase() + newname.substring(1)) : 'Owntracks'
|
||||
},
|
||||
{
|
||||
match: /bluefox/g,
|
||||
replacement: author
|
||||
},
|
||||
{
|
||||
match: /dogafox@gmail.com/g,
|
||||
replacement: email
|
||||
}
|
||||
]
|
||||
},
|
||||
files: [
|
||||
{
|
||||
expand: true,
|
||||
flatten: true,
|
||||
src: [
|
||||
srcDir + 'io-package.json',
|
||||
srcDir + 'LICENSE',
|
||||
srcDir + 'package.json',
|
||||
srcDir + 'README.md',
|
||||
srcDir + 'io-package.json',
|
||||
srcDir + 'main.js',
|
||||
srcDir + 'Gruntfile.js'
|
||||
],
|
||||
dest: srcDir
|
||||
},
|
||||
{
|
||||
expand: true,
|
||||
flatten: true,
|
||||
src: [
|
||||
srcDir + 'widgets/' + newname +'.html'
|
||||
],
|
||||
dest: srcDir + 'widgets'
|
||||
},
|
||||
{
|
||||
expand: true,
|
||||
flatten: true,
|
||||
src: [
|
||||
srcDir + 'admin/index.html'
|
||||
],
|
||||
dest: srcDir + 'admin'
|
||||
},
|
||||
{
|
||||
expand: true,
|
||||
flatten: true,
|
||||
src: [
|
||||
srcDir + 'widgets/' + newname + '/js/' + newname +'.js'
|
||||
],
|
||||
dest: srcDir + 'widgets/' + newname + '/js'
|
||||
},
|
||||
{
|
||||
expand: true,
|
||||
flatten: true,
|
||||
src: [
|
||||
srcDir + 'widgets/' + newname + '/css/*.css'
|
||||
],
|
||||
dest: srcDir + 'widgets/' + newname + '/css'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
// Javascript code styler
|
||||
jscs: require(__dirname + '/tasks/jscs.js'),
|
||||
// Lint
|
||||
jshint: require(__dirname + '/tasks/jshint.js'),
|
||||
|
||||
http: {
|
||||
get_hjscs: {
|
||||
options: {
|
||||
url: 'https://raw.githubusercontent.com/yunkong2/yunkong2.js-controller/master/tasks/jscs.js'
|
||||
},
|
||||
dest: 'tasks/jscs.js'
|
||||
},
|
||||
get_jshint: {
|
||||
options: {
|
||||
url: 'https://raw.githubusercontent.com/yunkong2/yunkong2.js-controller/master/tasks/jshint.js'
|
||||
},
|
||||
dest: 'tasks/jshint.js'
|
||||
},
|
||||
get_jscsRules: {
|
||||
options: {
|
||||
url: 'https://raw.githubusercontent.com/yunkong2/yunkong2.js-controller/master/tasks/jscsRules.js'
|
||||
},
|
||||
dest: 'tasks/jscsRules.js'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
grunt.loadNpmTasks('grunt-replace');
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
grunt.loadNpmTasks('grunt-jscs');
|
||||
grunt.loadNpmTasks('grunt-http');
|
||||
|
||||
grunt.registerTask('default', [
|
||||
'http',
|
||||
'replace:version',
|
||||
'jshint',
|
||||
'jscs'
|
||||
]);
|
||||
|
||||
grunt.registerTask('prepublish', [
|
||||
'http',
|
||||
'replace:version'
|
||||
]);
|
||||
|
||||
grunt.registerTask('p', [
|
||||
'http',
|
||||
'replace:version'
|
||||
]);
|
||||
|
||||
grunt.registerTask('rename', [
|
||||
'replace:name'
|
||||
]);
|
||||
};
|
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 'bluefox' <'dogafox@gmail.com'>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@ -0,0 +1,82 @@
|
||||
![Logo](admin/owntracks.png)
|
||||
# yunkong2.owntracks
|
||||
=================
|
||||
[![NPM version](http://img.shields.io/npm/v/yunkong2.owntracks.svg)](https://www.npmjs.com/package/yunkong2.owntracks)
|
||||
[![Downloads](https://img.shields.io/npm/dm/yunkong2.owntracks.svg)](https://www.npmjs.com/package/yunkong2.owntracks)
|
||||
|
||||
[![NPM](https://nodei.co/npm/yunkong2.owntracks.png?downloads=true)](https://nodei.co/npm/yunkong2.owntracks/)
|
||||
|
||||
|
||||
[OwnTracks](http://owntracks.org/) is an app for android and iOS.
|
||||
|
||||
Link for:
|
||||
- Andorid - [https://play.google.com/store/apps/details?id=org.owntracks.android](https://play.google.com/store/apps/details?id=org.owntracks.android)
|
||||
- iOS - [https://itunes.apple.com/de/app/owntracks/id692424691?mt=8](https://itunes.apple.com/de/app/owntracks/id692424691?mt=8)
|
||||
|
||||
App sends continuously your position (position of device) to some specific server. In our case it will be yunkong2 server.
|
||||
|
||||
The MQTT protocol will be used for communication.
|
||||
|
||||
##Usage
|
||||
OwnTracks Adapter starts on port 1883 (configurable) a MQTT server to receive the messages from devices with coordinates.
|
||||
The problem is that this server must be reachable from internet.
|
||||
Normally there is a router or firewall, that must be configured to forward traffic.
|
||||
|
||||
Settings in App:
|
||||
- Connection/Mode - MQTT private
|
||||
- Connection/Host/Host - IP address of your system or DynDNS domain. E.g. http://www.noip.com/ lets use domain name instead of IP address.
|
||||
- Connection/Host/Port - 1883 or your port on your router
|
||||
- Connection/Host/WebSockets - false
|
||||
- Connection/Identification/Username - yunkong2
|
||||
- Connection/Identification/Password - from adapter settings
|
||||
- Connection/Identification/DeviceID - Name of device or person. For this device the states will be created. E.g. if deviceID is "Mark", following states will be created after first contact:
|
||||
|
||||
- owntracks.0.users.Mark.longitude
|
||||
- owntracks.0.users.Mark.latitude
|
||||
|
||||
- Connection/Identification/TrackerID - Short name of user (up to 2 letters) to write it on map.
|
||||
- Connection/Security/TLS - off
|
||||
|
||||
### Icons
|
||||
You can define for every user an icon. Just upload per drag&drop or with mouse click you image. It will be automatically scaled to 64x64.
|
||||
|
||||
The name must be equal to DeviceID in OwnTracks app.
|
||||
|
||||
![Settings](img/settings1.png)
|
||||
|
||||
## Changelog
|
||||
|
||||
#### 0.3.0 (2018-06-05)
|
||||
* (matspi) Fix handling of publish messages
|
||||
|
||||
#### 0.2.0 (2017-01-03)
|
||||
* (jp112sdl) added two properties timestamp and datetime
|
||||
|
||||
#### 0.1.1 (2016-09-05)
|
||||
* (bluefox) add pictures
|
||||
|
||||
#### 0.1.0 (2016-09-04)
|
||||
* (bluefox) initial release
|
||||
|
||||
## License
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2017 bluefox<dogafox@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
After Width: | Height: | Size: 6.1 KiB |
@ -0,0 +1,25 @@
|
||||
version: 'test-{build}'
|
||||
environment:
|
||||
matrix:
|
||||
- nodejs_version: '4'
|
||||
- nodejs_version: '6'
|
||||
- nodejs_version: '8'
|
||||
- nodejs_version: '10'
|
||||
platform:
|
||||
- x86
|
||||
- x64
|
||||
clone_folder: 'c:\projects\%APPVEYOR_PROJECT_NAME%'
|
||||
install:
|
||||
- ps: 'Install-Product node $env:nodejs_version $env:platform'
|
||||
- ps: '$NpmVersion = (npm -v).Substring(0,1)'
|
||||
- ps: 'if($NpmVersion -eq 5) { npm install -g npm@5 }'
|
||||
- ps: npm --version
|
||||
- npm install
|
||||
- npm install winston@2.3.1
|
||||
- 'npm install https://git.spacen.net/yunkong2/yunkong2.js-controller/tarball/master --production'
|
||||
test_script:
|
||||
- echo %cd%
|
||||
- node --version
|
||||
- npm --version
|
||||
- npm test
|
||||
build: 'off'
|
After Width: | Height: | Size: 56 KiB |
@ -0,0 +1,78 @@
|
||||
{
|
||||
"common": {
|
||||
"name": "owntracks",
|
||||
"version": "0.3.0",
|
||||
"title": "OwnTracks adapter",
|
||||
"desc": {
|
||||
"en": "yunkong2 OwnTracks Adapter",
|
||||
"de": "yunkong2 OwnTracks Adapter",
|
||||
"ru": "yunkong2 OwnTracks драйвер"
|
||||
},
|
||||
"news": {
|
||||
"0.3.0": {
|
||||
"en": "Fix handling of publish messages",
|
||||
"de": "Problem in der Abarbeitung von publish-Nachrichten behoben"
|
||||
},
|
||||
"0.2.0": {
|
||||
"en": "added two properties timestamp and datetime",
|
||||
"de": "Timestamp und datetime sind hinzugefügt",
|
||||
"ru": "Добавлены время последнего сообщения в разных форматах"
|
||||
},
|
||||
"0.1.1": {
|
||||
"en": "add pictures",
|
||||
"de": "Unterstützung von Bildern",
|
||||
"ru": "Возможность добавить иконки"
|
||||
},
|
||||
"0.1.0": {
|
||||
"en": "initial checkin",
|
||||
"de": "Erste version",
|
||||
"ru": "первая версия"
|
||||
}
|
||||
},
|
||||
"platform": "Javascript/Node.js",
|
||||
"mode": "daemon",
|
||||
"icon": "owntracks.png",
|
||||
"extIcon": "https://git.spacen.net/yunkong2/yunkong2.owntracks/raw/master/admin/owntracks.png",
|
||||
"keywords": [
|
||||
"owntracks",
|
||||
"position",
|
||||
"gps",
|
||||
"geo"
|
||||
],
|
||||
"readme": "https://git.spacen.net/yunkong2/yunkong2.owntracks/blob/master/README.md",
|
||||
"loglevel": "info",
|
||||
"type": "geoposition",
|
||||
"authors": [
|
||||
{
|
||||
"name": "bluefox",
|
||||
"email": "dogafox@gmail.com"
|
||||
}
|
||||
]
|
||||
},
|
||||
"native": {
|
||||
"port": 1883,
|
||||
"user": "yunkong2",
|
||||
"pass": "",
|
||||
"secure": false,
|
||||
"pictures": [],
|
||||
"bind": "0.0.0.0",
|
||||
"certPublic": "defaultPublic",
|
||||
"certPrivate": "defaultPrivate",
|
||||
"certChained": "",
|
||||
"leEnabled": false,
|
||||
"leUpdate": false,
|
||||
"leCheckPort": 80
|
||||
},
|
||||
"objects": [],
|
||||
"instanceObjects": [
|
||||
{
|
||||
"_id": "users",
|
||||
"type": "channel",
|
||||
"common": {
|
||||
"role": "users",
|
||||
"name": "List of users"
|
||||
},
|
||||
"native": {}
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
let controllerDir;
|
||||
let appName;
|
||||
|
||||
/**
|
||||
* returns application name
|
||||
*
|
||||
* The name of the application can be different and this function finds it out.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
function getAppName() {
|
||||
const parts = __dirname.replace(/\\/g, '/').split('/');
|
||||
return parts[parts.length - 2].split('.')[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* looks for js-controller home folder
|
||||
*
|
||||
* @param {boolean} isInstall
|
||||
* @returns {string}
|
||||
*/
|
||||
function getControllerDir(isInstall) {
|
||||
// Find the js-controller location
|
||||
const possibilities = [
|
||||
'yunkong2.js-controller',
|
||||
'yunkong2.js-controller',
|
||||
];
|
||||
/** @type {string} */
|
||||
let controllerPath;
|
||||
for (const pkg of possibilities) {
|
||||
try {
|
||||
const possiblePath = require.resolve(pkg);
|
||||
if (fs.existsSync(possiblePath)) {
|
||||
controllerPath = possiblePath;
|
||||
break;
|
||||
}
|
||||
} catch (e) { /* not found */ }
|
||||
}
|
||||
if (controllerPath == null) {
|
||||
if (!isInstall) {
|
||||
console.log('Cannot find js-controller');
|
||||
process.exit(10);
|
||||
} else {
|
||||
process.exit();
|
||||
}
|
||||
}
|
||||
// we found the controller
|
||||
return path.dirname(controllerPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* reads controller base settings
|
||||
*
|
||||
* @alias getConfig
|
||||
* @returns {object}
|
||||
*/
|
||||
function getConfig() {
|
||||
let configPath;
|
||||
if (fs.existsSync(
|
||||
configPath = path.join(controllerDir, 'conf', appName + '.json')
|
||||
)) {
|
||||
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||
} else if (fs.existsSync(
|
||||
configPath = path.join(controllerDir, 'conf', + appName.toLowerCase() + '.json')
|
||||
)) {
|
||||
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||
} else {
|
||||
throw new Error('Cannot find ' + controllerDir + '/conf/' + appName + '.json');
|
||||
}
|
||||
}
|
||||
appName = getAppName();
|
||||
controllerDir = getControllerDir(typeof process !== 'undefined' && process.argv && process.argv.indexOf('--install') !== -1);
|
||||
const adapter = require(path.join(controllerDir, 'lib/adapter.js'));
|
||||
|
||||
exports.controllerDir = controllerDir;
|
||||
exports.getConfig = getConfig;
|
||||
exports.Adapter = adapter;
|
||||
exports.appName = appName;
|
@ -0,0 +1,374 @@
|
||||
/* jshint -W097 */// jshint strict:false
|
||||
/*jslint node: true */
|
||||
'use strict';
|
||||
var utils = require(__dirname + '/lib/utils'); // Get common adapter utils
|
||||
var adapter = utils.Adapter('owntracks');
|
||||
//var LE = require(utils.controllerDir + '/lib/letsencrypt.js');
|
||||
var createStreamServer = require('create-stream-server');
|
||||
var mqtt = require('mqtt-connection');
|
||||
|
||||
var server;
|
||||
var clients = {};
|
||||
var objects = {};
|
||||
|
||||
function decrypt(key, value) {
|
||||
var result = '';
|
||||
for (var i = 0; i < value.length; ++i) {
|
||||
result += String.fromCharCode(key[i % key.length].charCodeAt(0) ^ value.charCodeAt(i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 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 (server) {
|
||||
server.destroy();
|
||||
server = null;
|
||||
}
|
||||
callback();
|
||||
} catch (e) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
|
||||
adapter.on('ready', main);
|
||||
|
||||
function createUser(user) {
|
||||
var id = adapter.namespace + '.users.' + user.replace(/\s|\./g, '_');
|
||||
adapter.getForeignObject(id + '.battery', function (err, obj) {
|
||||
if (!obj) {
|
||||
adapter.setForeignObject(id + '.battery', {
|
||||
common: {
|
||||
name: 'Device battery level for ' + user,
|
||||
min: 0,
|
||||
max: 100,
|
||||
unit: '%',
|
||||
role: 'battery',
|
||||
type: 'number'
|
||||
},
|
||||
type: 'state',
|
||||
native: {}
|
||||
});
|
||||
}
|
||||
});
|
||||
adapter.getForeignObject(id + '.latitude', function (err, obj) {
|
||||
if (!obj) {
|
||||
adapter.setForeignObject(id + '.latitude', {
|
||||
common: {
|
||||
name: 'Latitude for ' + user,
|
||||
role: 'gps.latitude',
|
||||
type: 'number'
|
||||
},
|
||||
type: 'state',
|
||||
native: {}
|
||||
});
|
||||
}
|
||||
});
|
||||
adapter.getForeignObject(id + '.longitude', function (err, obj) {
|
||||
if (!obj) {
|
||||
adapter.setForeignObject(id + '.longitude', {
|
||||
common: {
|
||||
name: 'Longitude for ' + user,
|
||||
role: 'gps.longitude',
|
||||
type: 'number'
|
||||
},
|
||||
type: 'state',
|
||||
native: {}
|
||||
});
|
||||
}
|
||||
});
|
||||
adapter.getForeignObject(id + '.accuracy', function (err, obj) {
|
||||
if (!obj) {
|
||||
adapter.setForeignObject(id + '.accuracy', {
|
||||
common: {
|
||||
name: 'Accuracy for ' + user,
|
||||
role: 'state',
|
||||
unit: 'm',
|
||||
type: 'number'
|
||||
},
|
||||
type: 'state',
|
||||
native: {}
|
||||
});
|
||||
}
|
||||
});
|
||||
adapter.getForeignObject(id + '.timestamp', function (err, obj) {
|
||||
if (!obj) {
|
||||
adapter.setForeignObject(id + '.timestamp', {
|
||||
common: {
|
||||
name: 'Timestamp for ' + user,
|
||||
role: 'state',
|
||||
type: 'number'
|
||||
},
|
||||
type: 'state',
|
||||
native: {}
|
||||
});
|
||||
}
|
||||
});
|
||||
adapter.getForeignObject(id + '.datetime', function (err, obj) {
|
||||
if (!obj) {
|
||||
adapter.setForeignObject(id + '.datetime', {
|
||||
common: {
|
||||
name: 'Datetime for ' + user,
|
||||
role: 'state',
|
||||
type: 'string'
|
||||
},
|
||||
type: 'state',
|
||||
native: {}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function sendState2Client(client, topic, payload) {
|
||||
// client has subscription for this ID
|
||||
if (client._subsID && client._subsID[topic]) {
|
||||
client.publish({topic: topic, payload: payload});
|
||||
} else
|
||||
// Check patterns
|
||||
if (client._subs) {
|
||||
for (var s in client._subs) {
|
||||
if (!client._subs.hasOwnProperty(s)) continue;
|
||||
if (client._subs[s].regex.exec(topic)) {
|
||||
client.publish({topic: topic, payload: payload});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function processTopic(topic, payload, ignoreClient) {
|
||||
for (var k in clients) {
|
||||
// if get and set have different topic names, send state to issuing client too.
|
||||
if (clients[k] === ignoreClient) continue;
|
||||
sendState2Client(clients[k], topic, payload);
|
||||
}
|
||||
}
|
||||
|
||||
var cltFunction = function (client) {
|
||||
client.on('connect', function (packet) {
|
||||
client.id = packet.clientId;
|
||||
if (adapter.config.user) {
|
||||
if (adapter.config.user != packet.username ||
|
||||
adapter.config.pass != packet.password) {
|
||||
adapter.log.warn('Client [' + packet.clientId + '] has invalid password(' + packet.password + ') or username(' + packet.username + ')');
|
||||
client.connack({returnCode: 4});
|
||||
if (clients[client.id]) delete clients[client.id];
|
||||
client.stream.end();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
adapter.log.info('Client [' + packet.clientId + '] connected');
|
||||
client.connack({returnCode: 0});
|
||||
clients[client.id] = client;
|
||||
});
|
||||
|
||||
client.on('publish', function (packet) {
|
||||
var isAck = true;
|
||||
var topic = packet.topic;
|
||||
var message = packet.payload;
|
||||
adapter.log.debug('publish "' + topic + '": ' + message);
|
||||
|
||||
if (packet.qos == 1) {
|
||||
client.puback({ messageId: packet.messageId});
|
||||
}
|
||||
else if (packet.qos == 2) {
|
||||
client.pubrec({ messageId: packet.messageId});
|
||||
}
|
||||
// "owntracks/yunkong2/klte":
|
||||
// {
|
||||
// "_type":"location", // location, lwt, transition, configuration, beacon, cmd, steps, card, waypoint
|
||||
// "acc":50, // accuracy of location in meters
|
||||
// "batt":46, // is the device's battery level in percent (integer)
|
||||
// "lat":49.0026446, // latitude
|
||||
// "lon":8.3832128, // longitude
|
||||
// "tid":"te", // is a configurable tracker-ID - ignored
|
||||
// "tst":1472987109 // UNIX timestamp in seconds
|
||||
// }
|
||||
var parts = topic.split('/');
|
||||
if (parts[1] !== adapter.config.user) {
|
||||
adapter.log.warn('publish "' + topic + '": invalid user name - "' + parts[1] + '"');
|
||||
return;
|
||||
}
|
||||
if (!objects[parts[2]]) {
|
||||
// create object
|
||||
createUser(parts[2]);
|
||||
objects[parts[2]] = true;
|
||||
}
|
||||
processTopic(topic, message);
|
||||
try {
|
||||
var obj = JSON.parse(message);
|
||||
if (obj._type === 'location') {
|
||||
if (obj.acc !== undefined) {
|
||||
adapter.setState('users.' + parts[2] + '.accuracy', {val: obj.acc, ts: obj.tst * 1000, ack: true});
|
||||
}
|
||||
if (obj.batt !== undefined) {
|
||||
adapter.setState('users.' + parts[2] + '.battery', {val: obj.batt, ts: obj.tst * 1000, ack: true});
|
||||
}
|
||||
if (obj.lon !== undefined) {
|
||||
adapter.setState('users.' + parts[2] + '.longitude', {val: obj.lon, ts: obj.tst * 1000, ack: true});
|
||||
}
|
||||
if (obj.lat !== undefined) {
|
||||
adapter.setState('users.' + parts[2] + '.latitude', {val: obj.lat, ts: obj.tst * 1000, ack: true});
|
||||
}
|
||||
if (obj.tst !== undefined) {
|
||||
adapter.setState('users.' + parts[2] + '.timestamp', {val: obj.tst, ts: obj.tst * 1000, ack: true});
|
||||
var date = new Date(obj.tst * 1000);
|
||||
var day = '0' + date.getDate();
|
||||
var month = '0' + (date.getMonth() + 1);
|
||||
var year = date.getFullYear();
|
||||
var hours = '0' + date.getHours();
|
||||
var minutes = '0' + date.getMinutes();
|
||||
var seconds = '0' + date.getSeconds();
|
||||
var formattedTime = day.substr(-2) + '.' + month.substr(-2) + '.' + year + ' ' + hours.substr(-2) + ':' + minutes.substr(-2) + ':' + seconds.substr(-2);
|
||||
|
||||
adapter.setState('users.' + parts[2] + '.datetime', {val: formattedTime, ts: obj.tst * 1000, ack: true});
|
||||
}
|
||||
|
||||
}
|
||||
} catch (e) {
|
||||
adapter.log.error('Cannot parse payload: ' + message);
|
||||
}
|
||||
});
|
||||
|
||||
client.on('subscribe', function (packet) {
|
||||
var granted = [];
|
||||
if (!client._subsID) client._subsID = {};
|
||||
if (!client._subs) client._subs = {};
|
||||
|
||||
for (var i = 0; i < packet.subscriptions.length; i++) {
|
||||
granted.push(packet.subscriptions[i].qos);
|
||||
|
||||
var topic = packet.subscriptions[i].topic;
|
||||
|
||||
adapter.log.debug('Subscribe on ' + topic);
|
||||
// if pattern without wildchars
|
||||
if (topic.indexOf('*') === -1 && topic.indexOf('#') === -1 && topic.indexOf('+') === -1) {
|
||||
client._subsID[topic] = {
|
||||
qos: packet.subscriptions[i].qos
|
||||
};
|
||||
} else {
|
||||
// "owntracks/+/+/info" => owntracks\/.+\/.+\/info
|
||||
var pattern = topic.replace(/\//g, '\\/').replace(/\+/g, '[^\\/]+').replace(/\*/g, '.*');
|
||||
pattern = '^' + pattern + '$';
|
||||
|
||||
// add simple pattern
|
||||
client._subs[topic] = {
|
||||
regex: new RegExp(pattern),
|
||||
qos: packet.subscriptions[i].qos,
|
||||
pattern: topic
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
client.suback({granted: granted, messageId: packet.messageId});
|
||||
//Subscribe on owntracks/+/+
|
||||
//Subscribe on owntracks/+/+/info
|
||||
//Subscribe on owntracks/yunkong2/denis/cmd
|
||||
//Subscribe on owntracks/+/+/event
|
||||
//Subscribe on owntracks/+/+/waypoint
|
||||
|
||||
// send to client all images
|
||||
if (adapter.config.pictures && adapter.config.pictures.length) {
|
||||
setTimeout(function () {
|
||||
for (var p = 0; p < adapter.config.pictures.length; p++) {
|
||||
var text = adapter.config.pictures[p].base64.split(',')[1]; // string has form data:;base64,TEXT==
|
||||
sendState2Client(client, 'owntracks/' + adapter.config.user + '/' + adapter.config.pictures[p].name + '/info',
|
||||
JSON.stringify({
|
||||
_type: 'card',
|
||||
name: adapter.config.pictures[p].name,
|
||||
face: text
|
||||
})
|
||||
);
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
});
|
||||
|
||||
client.on('pingreq', function (packet) {
|
||||
adapter.log.debug('Client [' + client.id + '] pingreq');
|
||||
client.pingresp();
|
||||
});
|
||||
|
||||
client.on('disconnect', function (packet) {
|
||||
adapter.log.info('Client [' + client.id + '] disconnected');
|
||||
client.stream.end();
|
||||
});
|
||||
|
||||
client.on('close', function (err) {
|
||||
adapter.log.info('Client [' + client.id + '] closed');
|
||||
delete clients[client.id];
|
||||
});
|
||||
|
||||
client.on('error', function (err) {
|
||||
adapter.log.warn('[' + client.id + '] ' + err);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
function initMqttServer(config) {
|
||||
var serverConfig = {};
|
||||
var options = {
|
||||
ssl: config.certificates,
|
||||
emitEvents: true // default
|
||||
};
|
||||
|
||||
config.port = parseInt(config.port, 10) || 1883;
|
||||
|
||||
if (config.ssl) {
|
||||
serverConfig.mqtts = 'ssl://0.0.0.0:' + config.port;
|
||||
if (config.webSocket) {
|
||||
serverConfig.mqtwss = 'wss://0.0.0.0:' + (config.port + 1);
|
||||
}
|
||||
} else {
|
||||
serverConfig.mqtts = 'tcp://0.0.0.0:' + config.port;
|
||||
if (config.webSocket) {
|
||||
serverConfig.mqtwss = 'ws://0.0.0.0:' + (config.port + 1);
|
||||
}
|
||||
}
|
||||
|
||||
server = createStreamServer(serverConfig, options, function (clientStream) {
|
||||
cltFunction(mqtt(clientStream, {
|
||||
notData: !options.emitEvents
|
||||
}));
|
||||
});
|
||||
|
||||
// to start
|
||||
server.listen(function () {
|
||||
if (config.ssl) {
|
||||
adapter.log.info('Starting MQTT (Secure) ' + (config.user ? 'authenticated ' : '') + 'server on port ' + config.port);
|
||||
if (config.webSocket) {
|
||||
adapter.log.info('Starting MQTT-WebSocket (Secure) ' + (config.user ? 'authenticated ' : '') + 'server on port ' + (config.port + 1));
|
||||
}
|
||||
} else {
|
||||
adapter.log.info('Starting MQTT ' + (config.user ? 'authenticated ' : '') + 'server on port ' + config.port);
|
||||
if (config.webSocket) {
|
||||
adapter.log.info('Starting MQTT-WebSocket ' + (config.user ? 'authenticated ' : '') + 'server on port ' + (config.port + 1));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function main() {
|
||||
//noinspection JSUnresolvedVariable
|
||||
adapter.config.pass = decrypt('Zgfr56gFe87jJOM', adapter.config.pass);
|
||||
|
||||
if (!adapter.config.user) {
|
||||
adapter.log.error('Empty user name not allowed!');
|
||||
process.stop(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (adapter.config.secure) {
|
||||
// Load certificates
|
||||
adapter.getCertificates(function (err, certificates, leConfig) {
|
||||
adapter.config.certificates = certificates;
|
||||
adapter.config.leConfig = leConfig;
|
||||
initMqttServer(adapter.config);
|
||||
});
|
||||
} else {
|
||||
initMqttServer(adapter.config);
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
{
|
||||
"name": "yunkong2.owntracks",
|
||||
"version": "0.3.0",
|
||||
"description": "yunkong2 OwnTracks Adapter",
|
||||
"author": {
|
||||
"name": "bluefox",
|
||||
"email": "dogafox@gmail.com"
|
||||
},
|
||||
"contributors": [
|
||||
{
|
||||
"name": "bluefox",
|
||||
"email": "dogafox@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "matspi",
|
||||
"email": "matthias.spiller@fari.software"
|
||||
}
|
||||
],
|
||||
"homepage": "https://git.spacen.net/yunkong2/yunkong2.owntracks",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"yunkong2",
|
||||
"owntracks"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git.spacen.net/yunkong2/yunkong2.owntracks"
|
||||
},
|
||||
"dependencies": {
|
||||
"create-stream-server": "^0.1.1",
|
||||
"mqtt-connection": "^2.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt": "^1.0.1",
|
||||
"grunt-replace": "^1.0.1",
|
||||
"grunt-contrib-jshint": "^1.1.0",
|
||||
"grunt-jscs": "^3.0.1",
|
||||
"grunt-http": "^2.2.0",
|
||||
"mocha": "^4.1.0",
|
||||
"chai": "^4.1.2",
|
||||
"request": "^2.75.0"
|
||||
},
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"test": "node node_modules/mocha/bin/mocha --exit"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://git.spacen.net/yunkong2/yunkong2.owntracks/issues"
|
||||
},
|
||||
"readmeFilename": "README.md"
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
var srcDir = __dirname + "/../";
|
||||
|
||||
module.exports = {
|
||||
all: {
|
||||
src: [
|
||||
srcDir + "*.js",
|
||||
srcDir + "lib/*.js",
|
||||
srcDir + "adapter/example/*.js",
|
||||
srcDir + "tasks/**/*.js",
|
||||
srcDir + "www/**/*.js",
|
||||
'!' + srcDir + "www/lib/**/*.js",
|
||||
'!' + srcDir + 'node_modules/**/*.js',
|
||||
'!' + srcDir + 'adapter/*/node_modules/**/*.js'
|
||||
],
|
||||
options: require('./jscsRules.js')
|
||||
}
|
||||
};
|
@ -0,0 +1,36 @@
|
||||
module.exports = {
|
||||
force: true,
|
||||
"requireCurlyBraces": ["else", "for", "while", "do", "try", "catch"], /*"if",*/
|
||||
"requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"],
|
||||
"requireSpaceBeforeBlockStatements": true,
|
||||
"requireParenthesesAroundIIFE": true,
|
||||
"disallowSpacesInFunctionDeclaration": {"beforeOpeningRoundBrace": true},
|
||||
"disallowSpacesInNamedFunctionExpression": {"beforeOpeningRoundBrace": true},
|
||||
"requireSpacesInFunctionExpression": {"beforeOpeningCurlyBrace": true},
|
||||
"requireSpacesInAnonymousFunctionExpression": {"beforeOpeningRoundBrace": true, "beforeOpeningCurlyBrace": true},
|
||||
"requireSpacesInNamedFunctionExpression": {"beforeOpeningCurlyBrace": true},
|
||||
"requireSpacesInFunctionDeclaration": {"beforeOpeningCurlyBrace": true},
|
||||
"disallowMultipleVarDecl": true,
|
||||
"requireBlocksOnNewline": true,
|
||||
"disallowEmptyBlocks": true,
|
||||
"disallowSpacesInsideObjectBrackets": true,
|
||||
"disallowSpacesInsideArrayBrackets": true,
|
||||
"disallowSpaceAfterObjectKeys": true,
|
||||
"disallowSpacesInsideParentheses": true,
|
||||
"requireCommaBeforeLineBreak": true,
|
||||
//"requireAlignedObjectValues": "all",
|
||||
"requireOperatorBeforeLineBreak": ["?", "+", "-", "/", "*", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
|
||||
// "disallowLeftStickedOperators": ["?", "+", "/", "*", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
|
||||
// "requireRightStickedOperators": ["!"],
|
||||
// "requireSpaceAfterBinaryOperators": ["?", "+", "/", "*", ":", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
|
||||
//"disallowSpaceAfterBinaryOperators": [","],
|
||||
"disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"],
|
||||
"disallowSpaceBeforePostfixUnaryOperators": ["++", "--"],
|
||||
"requireSpaceBeforeBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!=="],
|
||||
"requireSpaceAfterBinaryOperators": ["?", ">", ",", ">=", "<=", "<", "+", "-", "/", "*", "=", "==", "===", "!=", "!=="],
|
||||
//"validateIndentation": 4,
|
||||
//"validateQuoteMarks": { "mark": "\"", "escape": true },
|
||||
"disallowMixedSpacesAndTabs": true,
|
||||
"disallowKeywordsOnNewLine": ["else", "catch"]
|
||||
|
||||
};
|
@ -0,0 +1,17 @@
|
||||
var srcDir = __dirname + "/../";
|
||||
|
||||
module.exports = {
|
||||
options: {
|
||||
force: true
|
||||
},
|
||||
all: [
|
||||
srcDir + "*.js",
|
||||
srcDir + "lib/*.js",
|
||||
srcDir + "adapter/example/*.js",
|
||||
srcDir + "tasks/**/*.js",
|
||||
srcDir + "www/**/*.js",
|
||||
'!' + srcDir + "www/lib/**/*.js",
|
||||
'!' + srcDir + 'node_modules/**/*.js',
|
||||
'!' + srcDir + 'adapter/*/node_modules/**/*.js'
|
||||
]
|
||||
};
|
@ -0,0 +1,728 @@
|
||||
/* jshint -W097 */// jshint strict:false
|
||||
/*jslint node: true */
|
||||
// check if tmp directory exists
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var child_process = require('child_process');
|
||||
var rootDir = path.normalize(__dirname + '/../../');
|
||||
var pkg = require(rootDir + 'package.json');
|
||||
var debug = typeof v8debug === 'object';
|
||||
pkg.main = pkg.main || 'main.js';
|
||||
|
||||
var adapterName = path.normalize(rootDir).replace(/\\/g, '/').split('/');
|
||||
adapterName = adapterName[adapterName.length - 2];
|
||||
var adapterStarted = false;
|
||||
|
||||
function getAppName() {
|
||||
var parts = __dirname.replace(/\\/g, '/').split('/');
|
||||
return parts[parts.length - 3].split('.')[0];
|
||||
}
|
||||
|
||||
var appName = getAppName().toLowerCase();
|
||||
|
||||
var objects;
|
||||
var states;
|
||||
|
||||
var pid = null;
|
||||
|
||||
function copyFileSync(source, target) {
|
||||
|
||||
var 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) {
|
||||
var files = [];
|
||||
|
||||
var base = path.basename(source);
|
||||
if (base === adapterName) {
|
||||
base = pkg.name;
|
||||
}
|
||||
//check if folder needs to be created or integrated
|
||||
var 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;
|
||||
}
|
||||
|
||||
var curSource = path.join(source, file);
|
||||
var 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...');
|
||||
var dataDir = rootDir + 'tmp/' + appName + '-data/';
|
||||
|
||||
var f = fs.readFileSync(dataDir + 'objects.json');
|
||||
var 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...');
|
||||
var dataDir = rootDir + 'tmp/' + appName + '-data/';
|
||||
|
||||
var 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;
|
||||
var dataDir = rootDir + 'tmp/' + appName + '-data/';
|
||||
console.log('checkIsAdapterInstalled...');
|
||||
|
||||
try {
|
||||
var f = fs.readFileSync(dataDir + 'objects.json');
|
||||
var objects = JSON.parse(f.toString());
|
||||
if (objects['system.adapter.' + customName + '.0']) {
|
||||
console.log('checkIsAdapterInstalled: ready!');
|
||||
setTimeout(function () {
|
||||
if (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(function() {
|
||||
checkIsAdapterInstalled(cb, counter + 1);
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
function checkIsControllerInstalled(cb, counter) {
|
||||
counter = counter || 0;
|
||||
var dataDir = rootDir + 'tmp/' + appName + '-data/';
|
||||
|
||||
console.log('checkIsControllerInstalled...');
|
||||
try {
|
||||
var f = fs.readFileSync(dataDir + 'objects.json');
|
||||
var objects = JSON.parse(f.toString());
|
||||
if (objects['system.adapter.admin.0']) {
|
||||
console.log('checkIsControllerInstalled: installed!');
|
||||
setTimeout(function () {
|
||||
if (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(function() {
|
||||
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...');
|
||||
var 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(function (error) {
|
||||
if (error) console.error(error);
|
||||
console.log('Adapter installed.');
|
||||
if (cb) cb();
|
||||
});
|
||||
} else {
|
||||
// add controller
|
||||
var _pid = child_process.fork(startFile, ['add', customName, '--enabled', 'false'], {
|
||||
cwd: rootDir + 'tmp',
|
||||
stdio: [0, 1, 2, 'ipc']
|
||||
});
|
||||
|
||||
waitForEnd(_pid, function () {
|
||||
checkIsAdapterInstalled(function (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', function (code, signal) {
|
||||
if (_pid) {
|
||||
_pid = null;
|
||||
cb(code, signal);
|
||||
}
|
||||
});
|
||||
_pid.on('close', function (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...');
|
||||
var _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, function () {
|
||||
// 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...');
|
||||
var __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, function () {
|
||||
checkIsControllerInstalled(function () {
|
||||
// change ports for object and state DBs
|
||||
var 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(function () {
|
||||
storeOriginalFiles();
|
||||
if (cb) cb(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// check if port 9000 is free, else admin adapter will be added to running instance
|
||||
var client = new require('net').Socket();
|
||||
client.connect(9000, '127.0.0.1', function() {
|
||||
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(function () {
|
||||
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://git.spacen.net/' + appName + '/' + appName + '.js-controller/tarball/master --prefix ./ --production', {
|
||||
cwd: rootDir + 'tmp/',
|
||||
stdio: [0, 1, 2]
|
||||
});
|
||||
} else {
|
||||
console.log('Setup js-controller...');
|
||||
var __pid;
|
||||
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(function () {
|
||||
var _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, function () {
|
||||
// change ports for object and state DBs
|
||||
var 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(function () {
|
||||
storeOriginalFiles();
|
||||
if (cb) cb(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
} else {
|
||||
setTimeout(function () {
|
||||
console.log('installJsController: js-controller installed');
|
||||
if (cb) cb(false);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
var dirPath = rootDir + 'tmp/log';
|
||||
var 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 (var i = 0; i < files.length; i++) {
|
||||
var filePath = dirPath + '/' + files[i];
|
||||
fs.unlinkSync(filePath);
|
||||
}
|
||||
console.log('Controller log cleared');
|
||||
} catch (err) {
|
||||
console.error('cannot clear log: ' + err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clearDB() {
|
||||
var dirPath = rootDir + 'tmp/yunkong2-data/sqlite';
|
||||
var 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 (var i = 0; i < files.length; i++) {
|
||||
var filePath = dirPath + '/' + files[i];
|
||||
fs.unlinkSync(filePath);
|
||||
}
|
||||
console.log('Clear sqlite DB');
|
||||
} catch (err) {
|
||||
console.error('cannot clear DB: ' + err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setupController(cb) {
|
||||
installJsController(function (isInited) {
|
||||
clearControllerLog();
|
||||
clearDB();
|
||||
|
||||
if (!isInited) {
|
||||
restoreOriginalFiles();
|
||||
copyAdapterToController();
|
||||
}
|
||||
// read system.config object
|
||||
var dataDir = rootDir + 'tmp/' + appName + '-data/';
|
||||
|
||||
var 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;
|
||||
var isObjectConnected;
|
||||
var isStatesConnected;
|
||||
|
||||
var 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: function () {
|
||||
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
|
||||
var 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: function () {
|
||||
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) {
|
||||
setTimeout(function () {
|
||||
cb(false);
|
||||
}, 0);
|
||||
}
|
||||
} else {
|
||||
adapterStarted = false;
|
||||
pid.on('exit', function (code, signal) {
|
||||
if (pid) {
|
||||
console.log('child process terminated due to receipt of signal ' + signal);
|
||||
if (cb) cb();
|
||||
pid = null;
|
||||
}
|
||||
});
|
||||
|
||||
pid.on('close', function (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) {
|
||||
var timeout;
|
||||
if (objects) {
|
||||
console.log('Set system.adapter.' + pkg.name + '.0');
|
||||
objects.setObject('system.adapter.' + pkg.name + '.0', {
|
||||
common:{
|
||||
enabled: false
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
stopAdapter(function () {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
}
|
||||
|
||||
_stopController();
|
||||
|
||||
if (cb) {
|
||||
cb(true);
|
||||
cb = null;
|
||||
}
|
||||
});
|
||||
|
||||
timeout = setTimeout(function () {
|
||||
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) {
|
||||
var objects = JSON.parse(fs.readFileSync(rootDir + 'tmp/' + appName + '-data/objects.json').toString());
|
||||
var 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) {
|
||||
var objects = JSON.parse(fs.readFileSync(rootDir + 'tmp/' + appName + '-data/objects.json').toString());
|
||||
var 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;
|
||||
}
|
@ -0,0 +1,140 @@
|
||||
/* jshint -W097 */// jshint strict:false
|
||||
/*jslint node: true */
|
||||
var expect = require('chai').expect;
|
||||
var setup = require(__dirname + '/lib/setup');
|
||||
|
||||
var objects = null;
|
||||
var states = null;
|
||||
var onStateChanged = null;
|
||||
var onObjectChanged = null;
|
||||
var sendToID = 1;
|
||||
|
||||
var adapterShortName = setup.adapterName.substring(setup.adapterName.indexOf('.')+1);
|
||||
|
||||
function checkConnectionOfAdapter(cb, counter) {
|
||||
counter = counter || 0;
|
||||
console.log('Try check #' + counter);
|
||||
if (counter > 30) {
|
||||
if (cb) cb('Cannot check connection');
|
||||
return;
|
||||
}
|
||||
|
||||
states.getState('system.adapter.' + adapterShortName + '.0.alive', function (err, state) {
|
||||
if (err) console.error(err);
|
||||
if (state && state.val) {
|
||||
if (cb) cb();
|
||||
} else {
|
||||
setTimeout(function () {
|
||||
checkConnectionOfAdapter(cb, counter + 1);
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function checkValueOfState(id, value, cb, counter) {
|
||||
counter = counter || 0;
|
||||
if (counter > 20) {
|
||||
if (cb) cb('Cannot check value Of State ' + id);
|
||||
return;
|
||||
}
|
||||
|
||||
states.getState(id, function (err, state) {
|
||||
if (err) console.error(err);
|
||||
if (value === null && !state) {
|
||||
if (cb) cb();
|
||||
} else
|
||||
if (state && (value === undefined || state.val === value)) {
|
||||
if (cb) cb();
|
||||
} else {
|
||||
setTimeout(function () {
|
||||
checkValueOfState(id, value, cb, counter + 1);
|
||||
}, 500);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function sendTo(target, command, message, callback) {
|
||||
onStateChanged = function (id, state) {
|
||||
if (id === 'messagebox.system.adapter.test.0') {
|
||||
callback(state.message);
|
||||
}
|
||||
};
|
||||
|
||||
states.pushMessage('system.adapter.' + target, {
|
||||
command: command,
|
||||
message: message,
|
||||
from: 'system.adapter.test.0',
|
||||
callback: {
|
||||
message: message,
|
||||
id: sendToID++,
|
||||
ack: false,
|
||||
time: (new Date()).getTime()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
describe('Test ' + adapterShortName + ' adapter', function() {
|
||||
before('Test ' + adapterShortName + ' adapter: Start js-controller', function (_done) {
|
||||
this.timeout(600000); // because of first install from npm
|
||||
|
||||
setup.setupController(function () {
|
||||
var config = setup.getAdapterConfig();
|
||||
// enable adapter
|
||||
config.common.enabled = true;
|
||||
config.common.loglevel = 'debug';
|
||||
|
||||
//config.native.dbtype = 'sqlite';
|
||||
|
||||
setup.setAdapterConfig(config.common, config.native);
|
||||
|
||||
setup.startController(true, function(id, obj) {}, function (id, state) {
|
||||
if (onStateChanged) onStateChanged(id, state);
|
||||
},
|
||||
function (_objects, _states) {
|
||||
objects = _objects;
|
||||
states = _states;
|
||||
_done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
ENABLE THIS WHEN ADAPTER RUNS IN DEAMON MODE TO CHECK THAT IT HAS STARTED SUCCESSFULLY
|
||||
*/
|
||||
it('Test ' + adapterShortName + ' adapter: Check if adapter started', function (done) {
|
||||
this.timeout(60000);
|
||||
checkConnectionOfAdapter(function (res) {
|
||||
if (res) console.log(res);
|
||||
expect(res).not.to.be.equal('Cannot check connection');
|
||||
objects.setObject('system.adapter.test.0', {
|
||||
common: {
|
||||
|
||||
},
|
||||
type: 'instance'
|
||||
},
|
||||
function () {
|
||||
states.subscribeMessage('system.adapter.test.0');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
/**/
|
||||
|
||||
/*
|
||||
PUT YOUR OWN TESTS HERE USING
|
||||
it('Testname', function ( done) {
|
||||
...
|
||||
});
|
||||
|
||||
You can also use "sendTo" method to send messages to the started adapter
|
||||
*/
|
||||
|
||||
after('Test ' + adapterShortName + ' adapter: Stop js-controller', function (done) {
|
||||
this.timeout(10000);
|
||||
|
||||
setup.stopController(function (normalTerminated) {
|
||||
console.log('Adapter normal terminated: ' + normalTerminated);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,91 @@
|
||||
/* jshint -W097 */
|
||||
/* jshint strict:false */
|
||||
/* jslint node: true */
|
||||
/* jshint expr: true */
|
||||
var expect = require('chai').expect;
|
||||
var fs = require('fs');
|
||||
|
||||
describe('Test package.json and io-package.json', function() {
|
||||
it('Test package files', function (done) {
|
||||
console.log();
|
||||
|
||||
var fileContentIOPackage = fs.readFileSync(__dirname + '/../io-package.json', 'utf8');
|
||||
var ioPackage = JSON.parse(fileContentIOPackage);
|
||||
|
||||
var fileContentNPMPackage = fs.readFileSync(__dirname + '/../package.json', 'utf8');
|
||||
var 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('yunkong2') !== -1 ||
|
||||
ioPackage.common.title.indexOf('yunkong2') !== -1 ||
|
||||
ioPackage.common.title.indexOf('adapter') !== -1 ||
|
||||
ioPackage.common.title.indexOf('Adapter') !== -1
|
||||
) {
|
||||
console.log('WARNING: title contains Adapter or yunkong2. It is clear anyway, that it is adapter for yunkong2.');
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
var licenseFileExists = fs.existsSync(__dirname + '/../LICENSE');
|
||||
var 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();
|
||||
});
|
||||
});
|
Loading…
Reference in new issue