Initial commit
This commit is contained in:
commit
8628696294
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/node_modules
|
||||||
|
/.idea
|
10
.npmignore
Normal file
10
.npmignore
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
Gruntfile.js
|
||||||
|
tasks
|
||||||
|
node_modules
|
||||||
|
.idea
|
||||||
|
.gitignore
|
||||||
|
.git
|
||||||
|
.DS_Store
|
||||||
|
test
|
||||||
|
.travis.yml
|
||||||
|
appveyor.yml
|
23
.travis.yml
Normal file
23
.travis.yml
Normal file
@ -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
|
130
Gruntfile.js
Normal file
130
Gruntfile.js
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
// 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 dstDir = srcDir + '.build/';
|
||||||
|
var pkg = grunt.file.readJSON('package.json');
|
||||||
|
var iopackage = grunt.file.readJSON('io-package.json');
|
||||||
|
var version = (pkg && pkg.version) ? pkg.version : iopackage.common.version;
|
||||||
|
|
||||||
|
// Project configuration.
|
||||||
|
grunt.initConfig({
|
||||||
|
pkg: pkg,
|
||||||
|
replace: {
|
||||||
|
core: {
|
||||||
|
options: {
|
||||||
|
patterns: [
|
||||||
|
{
|
||||||
|
match: /var version = *'[\.0-9]*';/g,
|
||||||
|
replacement: "var version = '" + version + "';"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
match: /"version"\: *"[\.0-9]*",/g,
|
||||||
|
replacement: '"version": "' + version + '",'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
files: [
|
||||||
|
{
|
||||||
|
expand: true,
|
||||||
|
flatten: true,
|
||||||
|
src: [
|
||||||
|
srcDir + 'controller.js',
|
||||||
|
srcDir + 'package.json',
|
||||||
|
srcDir + 'io-package.json'
|
||||||
|
],
|
||||||
|
dest: srcDir
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 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_gruntfile: {
|
||||||
|
options: {
|
||||||
|
url: 'https://raw.githubusercontent.com/yunkong2/yunkong2.build/master/adapters/Gruntfile.js'
|
||||||
|
},
|
||||||
|
dest: 'Gruntfile.js'
|
||||||
|
},
|
||||||
|
get_utilsfile: {
|
||||||
|
options: {
|
||||||
|
url: 'https://raw.githubusercontent.com/yunkong2/yunkong2.build/master/adapters/utils.js'
|
||||||
|
},
|
||||||
|
dest: 'lib/utils.js'
|
||||||
|
},
|
||||||
|
get_jscsRules: {
|
||||||
|
options: {
|
||||||
|
url: 'https://raw.githubusercontent.com/yunkong2/yunkong2.js-controller/master/tasks/jscsRules.js'
|
||||||
|
},
|
||||||
|
dest: 'tasks/jscsRules.js'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
grunt.registerTask('updateReadme', function () {
|
||||||
|
var readme = grunt.file.read('README.md');
|
||||||
|
var pos = readme.indexOf('## Changelog\n');
|
||||||
|
if (pos != -1) {
|
||||||
|
var readmeStart = readme.substring(0, pos + '## Changelog\n'.length);
|
||||||
|
var readmeEnd = readme.substring(pos + '## Changelog\n'.length);
|
||||||
|
|
||||||
|
if (readme.indexOf(version) == -1) {
|
||||||
|
var timestamp = new Date();
|
||||||
|
var date = timestamp.getFullYear() + '-' +
|
||||||
|
("0" + (timestamp.getMonth() + 1).toString(10)).slice(-2) + '-' +
|
||||||
|
("0" + (timestamp.getDate()).toString(10)).slice(-2);
|
||||||
|
|
||||||
|
var news = "";
|
||||||
|
if (iopackage.common.whatsNew) {
|
||||||
|
for (var i = 0; i < iopackage.common.whatsNew.length; i++) {
|
||||||
|
if (typeof iopackage.common.whatsNew[i] == 'string') {
|
||||||
|
news += '* ' + iopackage.common.whatsNew[i] + '\n';
|
||||||
|
} else {
|
||||||
|
news += '* ' + iopackage.common.whatsNew[i].en + '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
grunt.file.write('README.md', readmeStart + '### ' + version + ' (' + date + ')\n' + (news ? news + '\n\n' : '\n') + readmeEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
grunt.loadNpmTasks('grunt-replace');
|
||||||
|
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||||
|
grunt.loadNpmTasks('grunt-jscs');
|
||||||
|
grunt.loadNpmTasks('grunt-http');
|
||||||
|
|
||||||
|
grunt.registerTask('default', [
|
||||||
|
'http',
|
||||||
|
'replace',
|
||||||
|
'updateReadme',
|
||||||
|
'jshint',
|
||||||
|
'jscs'
|
||||||
|
]);
|
||||||
|
grunt.registerTask('p', [
|
||||||
|
'replace',
|
||||||
|
'updateReadme'
|
||||||
|
]);
|
||||||
|
};
|
92
README.md
Normal file
92
README.md
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
![Logo](admin/geofency.png)
|
||||||
|
# yunkong2.geofency
|
||||||
|
====================
|
||||||
|
|
||||||
|
[![NPM version](http://img.shields.io/npm/v/yunkong2.geofency.svg)](https://www.npmjs.com/package/yunkong2.geofency)
|
||||||
|
[![Downloads](https://img.shields.io/npm/dm/yunkong2.geofency.svg)](https://www.npmjs.com/package/yunkong2.geofency)
|
||||||
|
|
||||||
|
[![NPM](https://nodei.co/npm/yunkong2.geofency.png?downloads=true)](https://nodei.co/npm/yunkong2.geofency/)
|
||||||
|
|
||||||
|
|
||||||
|
This Adapter is able to receive [geofency](http://www.geofency.com/) events when entering or leaving a defined area with your mobile device.
|
||||||
|
All values of the geofency-webhook of the request are stored under the name of the location in yunkong2.
|
||||||
|
|
||||||
|
## configuration on mobile device:
|
||||||
|
* for any location -> properties -> webhook settings:
|
||||||
|
* URL for entry & exit: <your yunkong2 Domain>:<configured port>/<any locationname>
|
||||||
|
* Post Format: JSON-encoded: enabled
|
||||||
|
* authentication: set user / password from yunkong2.geofency config
|
||||||
|
|
||||||
|
## in yunkong2 Forum (German)
|
||||||
|
http://forum.yunkong2.net/viewtopic.php?f=20&t=2076
|
||||||
|
|
||||||
|
## security note:
|
||||||
|
It is not recommended to expose this adapter to the public internet.
|
||||||
|
Some kind of WAF/proxy/entry Server should be put before yunkong2. (e.g. nginx is nice and easy to configure).
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
### 0.3.2 (2018-03-07)
|
||||||
|
* (Apollon77) Fix Authentication
|
||||||
|
|
||||||
|
### 0.3.0 (2017-10-04)
|
||||||
|
* (Apollon77) BREAKING!!! Make sure 'entry' is really a boolean as defined in object
|
||||||
|
|
||||||
|
### 0.2.0 (2017-06-09)
|
||||||
|
* (Apollon77) Add missing authentication check
|
||||||
|
* (Apollon77) Add option to send in data as Message when received over other ways
|
||||||
|
* (Apollon77) Add option not to start a webserver for cases where data are received using messages
|
||||||
|
|
||||||
|
### 0.1.5 (2016-09-19)
|
||||||
|
* (soef) support of certificates
|
||||||
|
|
||||||
|
### 0.1.4 (2016-03-29)
|
||||||
|
* (dschaedl) replaced geofency Icon (on request of bluefox)
|
||||||
|
|
||||||
|
### 0.1.3 (2016-03-29)
|
||||||
|
* (soef) fixed atHome and atHomeCount state creation
|
||||||
|
|
||||||
|
### 0.1.2 (2016-02-13)
|
||||||
|
* (soef) Dots in location name will be replaced by an underscore
|
||||||
|
|
||||||
|
### 0.1.1 (2016-02-01)
|
||||||
|
* (Pmant) Fix config page
|
||||||
|
|
||||||
|
### 0.1.0 (2016-01-26)
|
||||||
|
* (soef) Fix error with "at home" settings
|
||||||
|
|
||||||
|
### 0.0.4 (2016-01-24)
|
||||||
|
* (soef) Added some new states
|
||||||
|
|
||||||
|
### 0.0.3 (2016-01-21)
|
||||||
|
* (soef) Some modifications
|
||||||
|
* (bluefox) change type
|
||||||
|
|
||||||
|
### 0.0.2
|
||||||
|
* (dschaedl) moved to yunkong2/yunkong2.geofency
|
||||||
|
|
||||||
|
### 0.0.1
|
||||||
|
* (dschaedl) initial release
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 dschaedl <daniel.schaedler@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.
|
BIN
admin/geofency.png
Normal file
BIN
admin/geofency.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 71 KiB |
90
admin/index.html
Normal file
90
admin/index.html
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
<html>
|
||||||
|
|
||||||
|
<!-- these 4 files always have to be included -->
|
||||||
|
<link rel="stylesheet" type="text/css" href="../../lib/css/themes/jquery-ui/redmond/jquery-ui.min.css" />
|
||||||
|
<script type="text/javascript" src="../../lib/js/jquery-1.11.1.min.js"></script>
|
||||||
|
<script type="text/javascript" src="../../socket.io/socket.io.js"></script>
|
||||||
|
<script type="text/javascript" src="../../lib/js/jquery-ui-1.10.3.full.min.js"></script>
|
||||||
|
|
||||||
|
<!-- these two file always have to be included -->
|
||||||
|
<link rel="stylesheet" type="text/css" href="../../css/adapter.css" />
|
||||||
|
<script type="text/javascript" src="../../js/translate.js"></script>
|
||||||
|
<script type="text/javascript" src="../../js/adapter-settings.js"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="words.js"></script>
|
||||||
|
|
||||||
|
<!-- you have to define 2 functions in the global scope: -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
function load(settings, onChange) {
|
||||||
|
// example: select elements with id=key and class=value and insert value
|
||||||
|
if (settings.activate_server === undefined) settings.activate_server = true;
|
||||||
|
for (var key in settings) {
|
||||||
|
// example: select elements with id=key and class=value and insert value
|
||||||
|
if ($('#' + key + '.value').attr('type') == 'checkbox') {
|
||||||
|
$('#' + key + '.value').prop('checked', settings[key]).change(function () {
|
||||||
|
onChange();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$('#' + key + '.value').val(settings[key]).change(function () {
|
||||||
|
onChange();
|
||||||
|
}).keyup(function () {
|
||||||
|
$(this).trigger('change');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onChange(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function save(callback) {
|
||||||
|
var obj = {};
|
||||||
|
$('.value').each(function () {
|
||||||
|
var $this = $(this);
|
||||||
|
var id = $this.attr('id');
|
||||||
|
|
||||||
|
if ($this.attr('type') == 'checkbox') {
|
||||||
|
obj[id] = $this.prop('checked');
|
||||||
|
} else {
|
||||||
|
obj[id] = $this.val();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
callback(obj);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- you have to put your config page in a div with id adapter-container -->
|
||||||
|
<div id="adapter-container">
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td><img src="geofency.png" height="64" width="64" /></td>
|
||||||
|
<td style="padding-top: 20px;padding-left: 10px"><h3 class="translate">Geofency Adapter Settings</h3></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr><td class="translate">atHome</td><td colspan="5"><input class="value number" id="atHome" type="input" size="5" /></td></tr>
|
||||||
|
<tr><td class="translate">activate_server</td><td colspan="2"><input type="checkbox" class="value" id="activate_server" size="5" /></td></tr>
|
||||||
|
<tr><td class="translate">port</td><td colspan="2"><input class="value number" id="port" type="input" size="5" /></td></tr>
|
||||||
|
<tr><td class="translate">ssl</td><td colspan="2"><input type="checkbox" class="value" id="ssl" size="5" /></td></tr>
|
||||||
|
<tr><td class="translate">user</td><td colspan="2"><input class="value number" id="user" type="input" size="5" /></td></tr>
|
||||||
|
<tr><td class="translate">password</td><td colspan="2"><input class="value number" id="pass" type="input" size="5" /></td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
<h3 class="translate">port</h3>
|
||||||
|
<p class="translate">geofency is listening for events on this port</p>
|
||||||
|
<h3 class="translate">user / password</h3>
|
||||||
|
<p class="translate">set username and password for authentication of your geofenca device. Use the same values in your mobile app webhook settings</p>
|
||||||
|
<h3 class="translate">geofency mobile app</h3>
|
||||||
|
<p class="translate">
|
||||||
|
download geofency for your device<br />
|
||||||
|
* see <a target="blank" href="http://www.geofency.com/">Geofency website</a><br />
|
||||||
|
* for any new location -> properties -> webhook settings:<br />
|
||||||
|
-> URL for entry & exit: <your yunkong2 Domain>:<port from above>/<any location name ><br />
|
||||||
|
-> Post Format: JSON-encoded: enabled<br />
|
||||||
|
-> authentication: set user / password from above
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</html>
|
8
admin/words.js
Normal file
8
admin/words.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
systemDictionary = {
|
||||||
|
"port": {"de": "Port", "en": "Port", "ru": "Порт"},
|
||||||
|
"activate_server": {"de": "Server aktivieren", "en": "Activate Server", "ru": "Activate Server"},
|
||||||
|
"user": {"de": "Benutzer", "en": "User", "ru": "Пользователь"},
|
||||||
|
"password": {"de": "Passwort", "en": "Password", "ru": "Пароль" },
|
||||||
|
"atHome": {"de": "Ortsname für Zuhause", "en": "Name of home", "ru": "Имя места для дома"},
|
||||||
|
"ssl": {"de": "SSL (https://) verwenden", "en": "Use SLL (https://)", "ru": "Использовать SSL(https://)"}
|
||||||
|
};
|
25
appveyor.yml
Normal file
25
appveyor.yml
Normal file
@ -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'
|
305
geofency.js
Normal file
305
geofency.js
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* geofency adapter
|
||||||
|
* This Adapter is based on the geofency adapter of ccu.io
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* jshint -W097 */// jshint strict:false
|
||||||
|
/*jslint node: true */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var utils = require(__dirname + '/lib/utils'); // Get common adapter utils
|
||||||
|
|
||||||
|
var webServer = null;
|
||||||
|
var activate_server = false;
|
||||||
|
|
||||||
|
var adapter = utils.Adapter({
|
||||||
|
name: 'geofency',
|
||||||
|
|
||||||
|
unload: function (callback) {
|
||||||
|
try {
|
||||||
|
adapter.log.info("terminating http" + (webServer.settings.secure ? "s" : "") + " server on port " + webServer.settings.port);
|
||||||
|
callback();
|
||||||
|
} catch (e) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ready: function () {
|
||||||
|
adapter.log.info("Adapter got 'Ready' Signal - initiating Main function...");
|
||||||
|
main();
|
||||||
|
},
|
||||||
|
message: function (msg) {
|
||||||
|
processMessage(msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
checkCreateNewObjects();
|
||||||
|
if (adapter.config.activate_server !== undefined) activate_server = adapter.config.activate_server;
|
||||||
|
else activate_server = true;
|
||||||
|
if (activate_server) {
|
||||||
|
if (adapter.config.ssl) {
|
||||||
|
// subscribe on changes of permissions
|
||||||
|
adapter.subscribeForeignObjects('system.group.*');
|
||||||
|
adapter.subscribeForeignObjects('system.user.*');
|
||||||
|
|
||||||
|
if (!adapter.config.certPublic) {
|
||||||
|
adapter.config.certPublic = 'defaultPublic';
|
||||||
|
}
|
||||||
|
if (!adapter.config.certPrivate) {
|
||||||
|
adapter.config.certPrivate = 'defaultPrivate';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load certificates
|
||||||
|
adapter.getForeignObject('system.certificates', function (err, obj) {
|
||||||
|
if (err || !obj || !obj.native.certificates || !adapter.config.certPublic || !adapter.config.certPrivate || !obj.native.certificates[adapter.config.certPublic] || !obj.native.certificates[adapter.config.certPrivate]
|
||||||
|
) {
|
||||||
|
adapter.log.error('Cannot enable secure web server, because no certificates found: ' + adapter.config.certPublic + ', ' + adapter.config.certPrivate);
|
||||||
|
} else {
|
||||||
|
adapter.config.certificates = {
|
||||||
|
key: obj.native.certificates[adapter.config.certPrivate],
|
||||||
|
cert: obj.native.certificates[adapter.config.certPublic]
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
webServer = initWebServer(adapter.config);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
webServer = initWebServer(adapter.config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initWebServer(settings) {
|
||||||
|
|
||||||
|
var server = {
|
||||||
|
server: null,
|
||||||
|
settings: settings
|
||||||
|
};
|
||||||
|
|
||||||
|
if (settings.port) {
|
||||||
|
if (settings.ssl) {
|
||||||
|
if (!adapter.config.certificates) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.ssl) {
|
||||||
|
server.server = require('https').createServer(adapter.config.certificates, requestProcessor);
|
||||||
|
} else {
|
||||||
|
server.server = require('http').createServer(requestProcessor);
|
||||||
|
}
|
||||||
|
|
||||||
|
server.server.__server = server;
|
||||||
|
} else {
|
||||||
|
adapter.log.error('port missing');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server.server) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
server.server.listen(port);
|
||||||
|
adapter.log.info('http' + (settings.ssl ? 's' : '') + ' server listening on port ' + port);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server.server) {
|
||||||
|
return server;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function requestProcessor(req, res) {
|
||||||
|
var check_user = adapter.config.user;
|
||||||
|
var check_pass = adapter.config.pass;
|
||||||
|
|
||||||
|
// If they pass in a basic auth credential it'll be in a header called "Authorization" (note NodeJS lowercases the names of headers in its request object)
|
||||||
|
var auth = req.headers.authorization; // auth is in base64(username:password) so we need to decode the base64
|
||||||
|
adapter.log.debug("Authorization Header is: ", auth);
|
||||||
|
|
||||||
|
var username = '';
|
||||||
|
var password = '';
|
||||||
|
var request_valid = true;
|
||||||
|
if (auth && check_user.length > 0 && check_pass.length > 0) {
|
||||||
|
var tmp = auth.split(' '); // Split on a space, the original auth looks like "Basic Y2hhcmxlczoxMjM0NQ==" and we need the 2nd part
|
||||||
|
var buf = new Buffer(tmp[1], 'base64'); // create a buffer and tell it the data coming in is base64
|
||||||
|
var plain_auth = buf.toString(); // read it back out as a string
|
||||||
|
|
||||||
|
adapter.log.debug("Decoded Authorization ", plain_auth);
|
||||||
|
// At this point plain_auth = "username:password"
|
||||||
|
var creds = plain_auth.split(':'); // split on a ':'
|
||||||
|
username = creds[0];
|
||||||
|
password = creds[1];
|
||||||
|
if ((username != check_user) || (password != check_pass)) {
|
||||||
|
adapter.log.warn("User credentials invalid");
|
||||||
|
request_valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*else {
|
||||||
|
adapter.log.warn("Authorization Header missing but user/pass defined");
|
||||||
|
request_valid = false;
|
||||||
|
}*/
|
||||||
|
if (!request_valid) {
|
||||||
|
res.statusCode = 403;
|
||||||
|
res.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (req.method === 'POST') {
|
||||||
|
var body = '';
|
||||||
|
|
||||||
|
adapter.log.debug("request path:" + req.path);
|
||||||
|
|
||||||
|
req.on('data', function (chunk) {
|
||||||
|
body += chunk;
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on('end', function () {
|
||||||
|
var user = req.url.slice(1);
|
||||||
|
var jbody = JSON.parse(body);
|
||||||
|
|
||||||
|
handleWebhookRequest(user, jbody);
|
||||||
|
|
||||||
|
res.writeHead(200);
|
||||||
|
res.write("OK");
|
||||||
|
res.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res.writeHead(500);
|
||||||
|
res.write("Request error");
|
||||||
|
res.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleWebhookRequest(user, jbody) {
|
||||||
|
adapter.log.info("adapter geofency received webhook from device " + user + " with values: name: " + jbody.name + ", entry: " + jbody.entry);
|
||||||
|
var id = user + '.' + jbody.name.replace(/\s|\./g, '_');
|
||||||
|
|
||||||
|
// create Objects if not yet available
|
||||||
|
adapter.getObject(id, function (err, obj) {
|
||||||
|
if (err || !obj) return createObjects(id, jbody);
|
||||||
|
setStates(id, jbody);
|
||||||
|
setAtHome(user, jbody);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastStateNames = ["lastLeave", "lastEnter"],
|
||||||
|
stateAtHomeCount = "atHomeCount",
|
||||||
|
stateAtHome = "atHome";
|
||||||
|
|
||||||
|
|
||||||
|
function setStates(id, jbody) {
|
||||||
|
adapter.setState(id + '.entry', {val: ((jbody.entry == "1") ? true : false), ack: true});
|
||||||
|
|
||||||
|
var ts = adapter.formatDate(new Date(jbody.date), "YYYY-MM-DD hh:mm:ss");
|
||||||
|
adapter.setState(id + '.date', {val: ts, ack: true});
|
||||||
|
adapter.setState(id + '.' + lastStateNames[(jbody.entry == "1") ? 1 : 0], {val: ts, ack: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function createObjects(id, b) {
|
||||||
|
// create all Objects
|
||||||
|
var children = [];
|
||||||
|
|
||||||
|
var obj = {
|
||||||
|
type: 'state',
|
||||||
|
//parent: id,
|
||||||
|
common: {name: 'entry', read: true, write: true, type: 'boolean'},
|
||||||
|
native: {id: id}
|
||||||
|
};
|
||||||
|
adapter.setObjectNotExists(id + ".entry", obj);
|
||||||
|
children.push(obj);
|
||||||
|
obj = {
|
||||||
|
type: 'state',
|
||||||
|
//parent: id,
|
||||||
|
common: {name: 'date', read: true, write: true, type: 'string'},
|
||||||
|
native: {id: id}
|
||||||
|
};
|
||||||
|
adapter.setObjectNotExists(id + ".date", obj);
|
||||||
|
children.push(obj);
|
||||||
|
|
||||||
|
for (var i = 0; i < 2; i++) {
|
||||||
|
obj.common.name = lastStateNames[i];
|
||||||
|
adapter.setObjectNotExists(id + "." + lastStateNames[i], obj);
|
||||||
|
children.push(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
adapter.setObjectNotExists(id, {
|
||||||
|
type: 'device',
|
||||||
|
//children: children,
|
||||||
|
common: {id: id, name: b.name},
|
||||||
|
native: {name: b.name, lat: b.lat, long: b.long, radius: b.radius, device: b.device, beaconUUID: b.beaconUUID, major: b.major, minor: b.minor}
|
||||||
|
}, function (err, obj) {
|
||||||
|
if (!err && obj) setStates(id, b);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function setAtHome(userName, body) {
|
||||||
|
if (body.name.toLowerCase() !== adapter.config.atHome.toLowerCase()) return;
|
||||||
|
var atHomeCount, atHome;
|
||||||
|
adapter.getState(stateAtHomeCount, function (err, obj) {
|
||||||
|
if (err) return;
|
||||||
|
atHomeCount = obj ? obj.val : 0;
|
||||||
|
adapter.getState(stateAtHome, function (err, obj) {
|
||||||
|
if (err) return;
|
||||||
|
atHome = obj ? (obj.val ? JSON.parse(obj.val) : []) : [];
|
||||||
|
var idx = atHome.indexOf(userName);
|
||||||
|
if (body.entry === '1') {
|
||||||
|
if (idx < 0) {
|
||||||
|
atHome.push(userName);
|
||||||
|
adapter.setState(stateAtHome, JSON.stringify(atHome), true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (idx >= 0) {
|
||||||
|
atHome.splice(idx, 1);
|
||||||
|
adapter.setState(stateAtHome, JSON.stringify(atHome), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (atHomeCount !== atHome.length) adapter.setState(stateAtHomeCount, atHome.length, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function createAndSetObject(id, obj) {
|
||||||
|
adapter.setObjectNotExists(id, obj, function (err) {
|
||||||
|
adapter.setState(id, 0, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkCreateNewObjects() {
|
||||||
|
|
||||||
|
function doIt() {
|
||||||
|
var fs = require('fs'),
|
||||||
|
io = fs.readFileSync(__dirname + "/io-package.json"),
|
||||||
|
objs = JSON.parse(io);
|
||||||
|
|
||||||
|
for (var i = 0; i < objs.instanceObjects.length; i++) {
|
||||||
|
createAndSetObject(objs.instanceObjects[i]._id, objs.instanceObjects[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var timer = setTimeout(doIt, 2000);
|
||||||
|
adapter.getState(stateAtHome, function (err, obj) {
|
||||||
|
clearTimeout(timer);
|
||||||
|
if (!obj) {
|
||||||
|
doIt();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function processMessage(message) {
|
||||||
|
if (!message || !message.message.user || !message.message.data) return;
|
||||||
|
|
||||||
|
adapter.log.info('Message received = ' + JSON.stringify(message));
|
||||||
|
|
||||||
|
handleWebhookRequest(message.message.user, message.message.data);
|
||||||
|
}
|
92
io-package.json
Normal file
92
io-package.json
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
{
|
||||||
|
"common": {
|
||||||
|
"name": "geofency",
|
||||||
|
"version": "0.3.2",
|
||||||
|
"news": {
|
||||||
|
"0.3.2": {
|
||||||
|
"en": "Fix Authentication",
|
||||||
|
"de": "Fehler bei Zugangsprüfung behoben",
|
||||||
|
"ru": "Fix Authentication"
|
||||||
|
},
|
||||||
|
"0.3.0": {
|
||||||
|
"en": "Make sure 'entry' is really a boolean as defined in object",
|
||||||
|
"de": "Sicherstellen das 'entry' wirklich ein Boolean ist wie im Objekt definiert.",
|
||||||
|
"ru": "Make sure 'entry' is really a boolean as defined in object"
|
||||||
|
},
|
||||||
|
"0.2.0": {
|
||||||
|
"en": "Add Message handling to process webhook data received from other sources then own webserver",
|
||||||
|
"de": "Behandling von Messages hinzugefügt um Webhook-Daten aus anderen Quellen als dem eigenen Webserver zu verarbeiten",
|
||||||
|
"ru": "Add Message handling to process webhook data received from other sources then own webserver"
|
||||||
|
},
|
||||||
|
"0.1.6": {
|
||||||
|
"en": "Catch parse errors",
|
||||||
|
"de": "Bearbeite Parsefehler",
|
||||||
|
"ru": "Обработка ошибок парсинга"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "Geofency Adapter",
|
||||||
|
"desc": "listening for geofency events. Based on the location based mobile App (Geofency)",
|
||||||
|
"platform": "Javascript/Node.js",
|
||||||
|
"mode": "daemon",
|
||||||
|
"icon": "geofency.png",
|
||||||
|
"extIcon": "https://git.spacen.net/yunkong2/yunkong2.geofency/raw/master/admin/geofency.png",
|
||||||
|
"readme": "https://git.spacen.net/yunkong2/yunkong2.geofency",
|
||||||
|
"license": "MIT",
|
||||||
|
"npmLibs": [],
|
||||||
|
"type": "geoposition",
|
||||||
|
"keywords": [
|
||||||
|
"yunkong2",
|
||||||
|
"server",
|
||||||
|
"geofency",
|
||||||
|
"mobile app"
|
||||||
|
],
|
||||||
|
"loglevel": "info",
|
||||||
|
"enabled": true,
|
||||||
|
"localLink": "http://%ip%:%port%",
|
||||||
|
"messagebox": true,
|
||||||
|
"subscribe": "messagebox",
|
||||||
|
"authors": [
|
||||||
|
"Daniel Schaedler <daniel.schaedler@gmail.com>",
|
||||||
|
"Apollon77 <ingo@fischer-ka.de>"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"native": {
|
||||||
|
"activate_server": true,
|
||||||
|
"port": 7999,
|
||||||
|
"ssl": false,
|
||||||
|
"user": "geo",
|
||||||
|
"pass": "geo",
|
||||||
|
"atHome": "Home"
|
||||||
|
},
|
||||||
|
"objects": [],
|
||||||
|
"instanceObjects": [
|
||||||
|
{
|
||||||
|
"_id": "atHome",
|
||||||
|
"type": "state",
|
||||||
|
"common": {
|
||||||
|
"name": "atHome",
|
||||||
|
"type": "string",
|
||||||
|
"role": "state",
|
||||||
|
"read": true,
|
||||||
|
"write": false,
|
||||||
|
"def": 0,
|
||||||
|
"desc": "Present persons"
|
||||||
|
},
|
||||||
|
"native": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "atHomeCount",
|
||||||
|
"type": "state",
|
||||||
|
"common": {
|
||||||
|
"name": "atHomeCount",
|
||||||
|
"type": "number",
|
||||||
|
"role": "state",
|
||||||
|
"read": true,
|
||||||
|
"write": false,
|
||||||
|
"def": [],
|
||||||
|
"desc": "Number of present persons"
|
||||||
|
},
|
||||||
|
"native": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
83
lib/utils.js
Normal file
83
lib/utils.js
Normal file
@ -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;
|
39
package.json
Normal file
39
package.json
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"name": "yunkong2.geofency",
|
||||||
|
"version": "0.3.2",
|
||||||
|
"description": "geofency adapter for yunkong2",
|
||||||
|
"author": "Daniel Schaedler <daniel.schaedler@gmail.com>",
|
||||||
|
"contributors": [
|
||||||
|
"Apollon77 <ingo@fischer-ka.de>"
|
||||||
|
],
|
||||||
|
"homepage": "",
|
||||||
|
"license": "MIT",
|
||||||
|
"keywords": [
|
||||||
|
"yunkong2",
|
||||||
|
"geofency",
|
||||||
|
"home automation"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.spacen.net/yunkong2/yunkong2.geofency"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"request": "^2.67.0"
|
||||||
|
},
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://git.spacen.net/yunkong2/yunkong2.geofency/issues"
|
||||||
|
},
|
||||||
|
"main": "geofency.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "node node_modules/mocha/bin/mocha --exit"
|
||||||
|
}
|
||||||
|
}
|
17
tasks/jscs.js
Normal file
17
tasks/jscs.js
Normal file
@ -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')
|
||||||
|
}
|
||||||
|
};
|
36
tasks/jscsRules.js
Normal file
36
tasks/jscsRules.js
Normal file
@ -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"]
|
||||||
|
|
||||||
|
};
|
17
tasks/jshint.js
Normal file
17
tasks/jshint.js
Normal file
@ -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'
|
||||||
|
]
|
||||||
|
};
|
728
test/lib/setup.js
Normal file
728
test/lib/setup.js
Normal file
@ -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;
|
||||||
|
}
|
140
test/testAdapter.js
Normal file
140
test/testAdapter.js
Normal file
@ -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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
91
test/testPackageFiles.js
Normal file
91
test/testPackageFiles.js
Normal file
@ -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
Block a user