Initial commit

master
zhongjin 6 years ago
commit f35f8d0f43

6
.gitignore vendored

@ -0,0 +1,6 @@
.git
.idea
node_modules
nbproject
admin/i18n/flat.txt
admin/i18n/*/flat.txt

@ -0,0 +1,10 @@
gulpfile.js
admin/i18n
tasks
node_modules
.idea
.git
/node_modules
test
.travis.yml
appveyor.yml

@ -0,0 +1,19 @@
os:
- linux
- osx
language: node_js
node_js:
- '4'
- '6'
- '8'
before_script:
- 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,21 @@
The MIT License (MIT)
Copyright (c) 2018 Kirov Ilya <kirovilya@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,4 @@
![Logo](admin/onvif_logo.png)
# yunkong2.onvif
=================

@ -0,0 +1,9 @@
{
"Auto": "Auto",
"Manual": "Manual",
"My select": "Mein Auswahl",
"on save adapter restarts with new config immediately": "Beim Speichern von Einstellungen der Adapter wird sofort neu gestartet.",
"template adapter settings": "Beispiel",
"test1": "Test 1",
"test2": "Test 2"
}

@ -0,0 +1,9 @@
{
"Auto": "Auto",
"Manual": "Manual",
"My select": "My select",
"on save adapter restarts with new config immediately": "on save adapter restarts with new config immediately",
"template adapter settings": "template adapter settings",
"test1": "Test 1",
"test2": "Test 2"
}

@ -0,0 +1,9 @@
{
"Auto": "Auto",
"Manual": "Manual",
"My select": "Mi seleccion",
"on save adapter restarts with new config immediately": "en el adaptador de guardar se reinicia con nueva configuración de inmediato",
"template adapter settings": "configuración del adaptador de plantilla",
"test1": "Prueba 1",
"test2": "Prueba 2"
}

@ -0,0 +1,9 @@
{
"Auto": "Auto",
"Manual": "Manuel",
"My select": "Mon choix",
"on save adapter restarts with new config immediately": "sur l'adaptateur de sauvegarde redémarre avec la nouvelle config immédiatement",
"template adapter settings": "paramètres de l'adaptateur de modèle",
"test1": "Test 1",
"test2": "Test 2"
}

@ -0,0 +1,9 @@
{
"Auto": "Auto",
"Manual": "Manuale",
"My select": "La mia selezione",
"on save adapter restarts with new config immediately": "su save adapter si riavvia immediatamente con la nuova configurazione",
"template adapter settings": "impostazioni dell'adattatore del modello",
"test1": "Test 1",
"test2": "Test 2"
}

@ -0,0 +1,9 @@
{
"Auto": "Auto",
"Manual": "Met de hand",
"My select": "Mijn select",
"on save adapter restarts with new config immediately": "on save-adapter wordt onmiddellijk opnieuw opgestart met nieuwe config",
"template adapter settings": "sjabloon-adapterinstellingen",
"test1": "Test 1",
"test2": "Test 2"
}

@ -0,0 +1,9 @@
{
"Auto": "Auto",
"Manual": "Manual",
"My select": "Meu selecionado",
"on save adapter restarts with new config immediately": "no adaptador de salvar reinicia com nova configuração imediatamente",
"template adapter settings": "configurações do adaptador de modelo",
"test1": "Teste 1",
"test2": "Teste 2"
}

@ -0,0 +1,9 @@
{
"Auto": "Автоматически",
"Manual": "Вручную",
"My select": "Выбор",
"on save adapter restarts with new config immediately": "При сохранении настроек адаптера он сразу же перезапускается",
"template adapter settings": "Пример",
"test1": "Тест 1",
"test2": "Тест 2"
}

@ -0,0 +1,84 @@
<html>
<!-- This file is deprecated!!!!! Please use index_m.html -->
<!-- This file is required only for backward compatibility and will be deleted soon -->
<!-- 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>
<!-- optional: use jqGrid
<link rel="stylesheet" type="text/css" href="../../lib/css/jqGrid/ui.jqgrid-4.5.4.css"/>
<script type="text/javascript" src="../../lib/js/jqGrid/jquery.jqGrid-4.5.4.min.js"></script>
<script type="text/javascript" src="../../lib/js/jqGrid/i18n/grid.locale-all.js"></script>
-->
<!-- optional: use multiselect
<link rel="stylesheet" type="text/css" href="../../lib/css/jquery.multiselect-1.13.css"/>
<script type="text/javascript" src="../../lib/js/jquery.multiselect-1.13.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">
// the function loadSettings has to exist ...
function load(settings, onChange) {
// example: select elements with id=key and class=value and insert value
if (!settings) return;
$('.value').each(function () {
var $key = $(this);
var id = $key.attr('id');
if ($key.attr('type') === 'checkbox') {
// do not call onChange direct, because onChange could expect some arguments
$key.prop('checked', settings[id]).change(function() {
onChange();
});
} else {
// do not call onChange direct, because onChange could expect some arguments
$key.val(settings[id]).change(function() {
onChange();
}).keyup(function() {
onChange();
});
}
});
onChange(false);
}
// ... and the function save has to exist.
// you have to make sure the callback is called with the settings object as first param!
function save(callback) {
// example: select elements with class=value and build settings object
var obj = {};
$('.value').each(function () {
var $this = $(this);
if ($this.attr('type') === 'checkbox') {
obj[$this.attr('id')] = $this.prop('checked');
} else {
obj[$this.attr('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="onvif_logo.png"/></td>
<td><h3 class="translate">template adapter settings</h3></td>
</tr></table>
<p class="translate">on save adapter restarts with new config immediately</p>
</div>
</html>

@ -0,0 +1,353 @@
<html>
<head>
<!-- these 4 files always have to be included -->
<link rel="stylesheet" type="text/css" href="../../lib/css/materialize.css">
<link rel="stylesheet" type="text/css" href="../../css/adapter.css"/>
<script type="text/javascript" src="../../lib/js/jquery-3.2.1.min.js"></script>
<script type="text/javascript" src="../../socket.io/socket.io.js"></script>
<!-- these files always have to be included -->
<script type="text/javascript" src="../../js/translate.js"></script>
<script type="text/javascript" src="../../lib/js/materialize.js"></script>
<script type="text/javascript" src="../../js/adapter-settings.js"></script>
<script type="text/javascript" src="words.js"></script>
<style>
.m .col .select-wrapper+label {
top: -26px;
}
.m span{
font-size: 0.9em;
}
</style>
<!-- you have to define 2 functions in the global scope: -->
<script type="text/javascript">
var namespace = 'onvif.' + instance,
namespaceLen = namespace.length;
// the function loadSettings has to exist ...
function load(settings, onChange) {
// example: select elements with id=key and class=value and insert value
if (!settings) return;
$('.value').each(function () {
var $key = $(this);
var id = $key.attr('id');
if ($key.attr('type') === 'checkbox') {
// do not call onChange direct, because onChange could expect some arguments
$key.prop('checked', settings[id]).change(function() {
onChange();
});
} else {
// do not call onChange direct, because onChange could expect some arguments
$key.val(settings[id]).change(function() {
onChange();
}).keyup(function() {
onChange();
});
}
});
onChange(false);
M.updateTextFields(); // function Materialize.updateTextFields(); to reinitialize all the Materialize labels on the page if you are dynamically adding inputs.
}
// ... and the function save has to exist.
// you have to make sure the callback is called with the settings object as first param!
function save(callback) {
// example: select elements with class=value and build settings object
var obj = {};
$('.value').each(function () {
var $this = $(this);
if ($this.attr('type') === 'checkbox') {
obj[$this.attr('id')] = $this.prop('checked');
} else {
obj[$this.attr('id')] = $this.val();
}
});
callback(obj);
}
function startDiscovery(){
var options = {
start_range: $('#start_range').val(),
end_range: $('#end_range').val(),
ports: $('#ports').val(),
user: $('#user').val(),
pass: $('#pass').val()
};
console.log(options);
sendTo(null, 'discovery', options, function (result) {
console.log(result);
});
}
$(document).ready(function(){
$('#discovery').off('click').on('click', function () {
startDiscovery();
});
$('.modal').modal();
getDevices();
socket.on('objectChange', function (id, obj) {
if (id.substring(0, namespaceLen) !== namespace) return;
if (obj && obj.type == "device") {
getDevices();
}
});
// react to changes
socket.on('stateChange', function (id, state) {
// only watch our own states
if (id.substring(0, namespaceLen) !== namespace) return;
console.log("State change announced: " + id + " and obj = " + JSON.stringify(state));
if (state) {
if (id.match(/\.discoveryRunning$/)) {
if (state.val) {
$('#discovery').addClass('pulse disabled');
} else {
$('#discovery').removeClass('pulse disabled');
}
}
}
});
});
function getCard(dev) {
var title = dev.common.name,
id = dev._id,
info = '',
buttons = '<a name="delete" class="btn-floating waves-effect waves-light right hoverable black"><i class="material-icons tiny">delete</i></a>',
card = '<div id="' + id + '" class="device col s12 m6 l4 xl3">'+
'<div class="card hoverable">'+
'<div class="card-content">'+
'<span class="card-title">'+title+'</span>'+
'<div class="card-image"><img id="img' + id + '" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="/></div>'+
info+
buttons+
'</div>'+
'</div>'+
'</div>';
return card;
}
function showDevices(devices) {
var html = '';
for (var i=0;i < devices.length; i++) {
var d = devices[i];
var card = getCard(d);
html += card;
}
$('#devices').html(html);
for (var i=0;i < devices.length; i++) {
var d = devices[i];
getSnapshot(d._id);
}
$("a.btn-floating[name='delete']").click(function() {
var dev_block = $(this).parents("div.device"),
id = dev_block.attr("id"),
name = dev_block.find(".card-title").text();
deleteConfirmation(id, name);
});
// $("a.btn-floating[name='edit']").click(function(e) {
// var dev_block = $(this).parents("div.device"),
// id = dev_block.attr("id"),
// name = dev_block.find(".card-title").text();
// // editName(id, name);
// openReval(e, id, name);
// });
// $("a.btn[name='done']").click(function(e) {
// var dev_block = $(this).parents("div.device"),
// id = dev_block.attr("id"),
// name = dev_block.find("input").val();
// closeReval(e, id, name);
// });
// $("a.btn-flat[name='close']").click(function(e) {
// closeReval(e);
// });
}
function deleteConfirmation(id, name) {
var text = 'Do you really whant to delete device "'+name+'" ('+id+')?';
$('#modaldelete').find("h3").text("Delete confirmation");
$('#modaldelete').find("p").text(text);
$("#modaldelete a.btn[name='yes']").unbind("click");
$("#modaldelete a.btn[name='yes']").click(function(e) {
deleteDevice(id);
});
$('#modaldelete').modal('open');
}
function deleteDevice(id) {
sendTo(null, 'deleteDevice', {id: id}, function (msg) {
//console.log(msg);
if (msg) {
if (msg.error) {
showMessage(msg.error.code, _('Error'), 'alert');
} else {
getDevices();
}
}
});
}
function getDevices() {
sendTo(null, 'getDevices', {}, function (msg) {
//console.log(msg);
if (msg) {
if (msg.error) {
showMessage(msg.error, _('Error'), 'alert');
} else {
showDevices(msg);
}
}
});
}
function arrayBufferDataUri(raw) {
var base64 = '';
var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
var bytes = new Uint8Array(raw.rawImage);
var byteLength = bytes.byteLength;
var byteRemainder = byteLength % 3;
var mainLength = byteLength - byteRemainder;
var a, b, c, d;
var chunk;
// Main loop deals with bytes in chunks of 3
for (var i = 0; i < mainLength; i = i + 3) {
// Combine the three bytes into a single integer
chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
// Use bitmasks to extract 6-bit segments from the triplet
a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18
b = (chunk & 258048) >> 12; // 258048 = (2^6 - 1) << 12
c = (chunk & 4032) >> 6; // 4032 = (2^6 - 1) << 6
d = chunk & 63; // 63 = 2^6 - 1
// Convert the raw binary segments to the appropriate ASCII encoding
base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d];
}
// Deal with the remaining bytes and padding
if (byteRemainder == 1) {
chunk = bytes[mainLength];
a = (chunk & 252) >> 2 // 252 = (2^6 - 1) << 2;
// Set the 4 least significant bits to zero
b = (chunk & 3) << 4 // 3 = 2^2 - 1;
base64 += encodings[a] + encodings[b] + '==';
}
else if (byteRemainder == 2) {
chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1];
a = (chunk & 16128) >> 8 // 16128 = (2^6 - 1) << 8;
b = (chunk & 1008) >> 4 // 1008 = (2^6 - 1) << 4;
// Set the 2 least significant bits to zero
c = (chunk & 15) << 2 // 15 = 2^4 - 1;
base64 += encodings[a] + encodings[b] + encodings[c] + '=';
}
return "data:"+raw.mimeType+";base64," + base64;
}
function getSnapshot(id){
sendTo(null, 'getSnapshot', {id: id}, function (msg) {
if (msg) {
if (msg.error) {
showMessage(msg.error, _('Error'), 'alert');
} else {
$("[id='img"+id+"']").attr('src', arrayBufferDataUri(msg));
}
}
});
}
</script>
</head>
<body>
<!-- you have to put your config page in a div with id adapter-container -->
<div class="m adapter-container">
<div class="row">
<!-- Forms are the standard way to receive user inputted data.
Learn more http://materializecss.com/forms.html-->
<div class="row valign-wrapper">
<div class="col s6">
<img src="onvif_logo.png">
</div>
<div class="col s6 right-align">
<!--<a class="btn-floating waves-effect waves-light btn-large green tooltipped center-align hoverable modal-trigger" href="#modal_add"><i class="material-icons large">add</i></a>-->
<a class="btn-floating waves-effect waves-light btn-large blue tooltipped center-align hoverable modal-trigger" href="#modal_discovery"><i class="material-icons large">youtube_searched_for</i></a>
</div>
</div>
</div>
<div id="devices" class="row">
</div>
</div>
<div class="materialize-dialogs m">
<div id="modal_discovery" class="modal">
<div class="modal-content">
<div class="title translate">Scan cameras</div>
<div class="row">
<div class="input-field col s12 m6 l4 xl3">
<input class="value" type="text" id="start_range"/>
<label class="translate" for="start_range" data-lang="startRange">Start range</label>
</div>
<div class="input-field col s12 m6 l4 xl3">
<input class="value" type="text" id="end_range"/>
<label class="translate" for="end_range">End range</label>
</div>
<div class="input-field col s12 m6 l4 xl3">
<input class="value" type="text" id="ports"/>
<label class="translate" for="ports">Port list</label>
</div>
</div>
<div class="row">
<div class="input-field col s12 m6 l4 xl4">
<input class="value" type="text" id="user"/>
<label class="translate" for="user">User name</label>
</div>
<div class="input-field col s12 m6 l4 xl4">
<input class="value" type="password" id="pass"/>
<label class="translate" for="pass">Password</label>
</div>
</div>
</div>
<div class="modal-footer">
<a id="discovery" href="#!" class="modal-action modal-close waves-effect waves-green btn-flat translate" data-result="true">Start scan</a>
<a href="#!" class="modal-action modal-close waves-effect waves-green btn-flat translate">Cancel</a>
</div>
</div>
<div class="materialize-dialogs m">
<div id="modal_add" class="modal">
<div class="modal-content">
<div class="title translate">Add camera</div>
<div class="row">
<div class="input-field col s12 m6 l4 xl3">
<input class="value" id="ip"/>
<label class="translate" for="ip">IP</label>
</div>
<div class="input-field col s12 m6 l4 xl3">
<input class="value" id="port"/>
<label class="translate" for="port">Port</label>
</div>
</div>
<div class="row">
<div class="input-field col s12 m6 l4 xl4">
<input class="value" id="c_user"/>
<label class="translate" for="c_user">User name</label>
</div>
<div class="input-field col s12 m6 l4 xl4">
<input class="value" id="c_pass"/>
<label class="translate" for="c_pass">Password</label>
</div>
</div>
</div>
<div class="modal-footer">
<a id="add" href="#!" class="modal-action modal-close waves-effect waves-green btn-flat translate" data-result="true">Create</a>
<a href="#!" class="modal-action modal-close waves-effect waves-green btn-flat translate">Cancel</a>
</div>
</div>
</div>
<div class="materialize-dialogs m">
<div id="modaldelete" class="modal">
<div class="modal-content">
<h3>Modal Header</h3>
<p>A bunch of text</p>
</div>
<div class="modal-footer">
<a name="yes" href="#!" class="modal-action modal-close waves-effect waves-green btn green">Yes</a>
<a href="#!" class="modal-action modal-close waves-effect waves-red btn-flat">Cancel</a>
</div>
</div>
</div>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

@ -0,0 +1,13 @@
// DO NOT EDIT THIS FILE!!! IT WILL BE AUTOMATICALLY GENERATED FROM src/i18n
/*global systemDictionary:true */
'use strict';
systemDictionary = {
"Auto": { "en": "Auto", "de": "Auto", "ru": "Автоматически", "pt": "Auto", "nl": "Auto", "fr": "Auto", "it": "Auto", "es": "Auto"},
"Manual": { "en": "Manual", "de": "Manual", "ru": "Вручную", "pt": "Manual", "nl": "Met de hand", "fr": "Manuel", "it": "Manuale", "es": "Manual"},
"My select": { "en": "My select", "de": "Mein Auswahl", "ru": "Выбор", "pt": "Meu selecionado", "nl": "Mijn select", "fr": "Mon choix", "it": "La mia selezione", "es": "Mi seleccion"},
"on save adapter restarts with new config immediately": {"en": "on save adapter restarts with new config immediately", "de": "Beim Speichern von Einstellungen der Adapter wird sofort neu gestartet.", "ru": "При сохранении настроек адаптера он сразу же перезапускается", "pt": "no adaptador de salvar reinicia com nova configuração imediatamente", "nl": "on save-adapter wordt onmiddellijk opnieuw opgestart met nieuwe config", "fr": "sur l'adaptateur de sauvegarde redémarre avec la nouvelle config immédiatement", "it": "su save adapter si riavvia immediatamente con la nuova configurazione", "es": "en el adaptador de guardar se reinicia con nueva configuración de inmediato"},
"template adapter settings": { "en": "template adapter settings", "de": "Beispiel", "ru": "Пример", "pt": "configurações do adaptador de modelo", "nl": "sjabloon-adapterinstellingen", "fr": "paramètres de l'adaptateur de modèle", "it": "impostazioni dell'adattatore del modello", "es": "configuración del adaptador de plantilla"},
"test1": { "en": "Test 1", "de": "Test 1", "ru": "Тест 1", "pt": "Teste 1", "nl": "Test 1", "fr": "Test 1", "it": "Test 1", "es": "Prueba 1"},
"test2": { "en": "Test 2", "de": "Test 2", "ru": "Тест 2", "pt": "Teste 2", "nl": "Test 2", "fr": "Test 2", "it": "Test 2", "es": "Prueba 2"}
};

@ -0,0 +1,21 @@
version: 'test-{build}'
environment:
matrix:
- nodejs_version: '4'
- nodejs_version: '6'
- nodejs_version: '8'
platform:
- x86
- x64
clone_folder: 'c:\projects\%APPVEYOR_PROJECT_NAME%'
install:
- ps: 'Install-Product node $env:nodejs_version $env:platform'
- 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'

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

@ -0,0 +1,3 @@
# Das ist die Dokumentation
(Picture)[img/picture.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

@ -0,0 +1,3 @@
# This is Documentation
(Picture)[img/picture.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

@ -0,0 +1,3 @@
# This is Documentation
(Picture)[img/picture.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

@ -0,0 +1,3 @@
# This is Documentation
(Picture)[img/picture.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

@ -0,0 +1,3 @@
# This is Documentation
(Picture)[img/picture.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

@ -0,0 +1,3 @@
# This is Documentation
(Picture)[img/picture.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

@ -0,0 +1,3 @@
# This is Documentation
(Picture)[img/picture.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

@ -0,0 +1,3 @@
# Это документация
(Picture)[img/picture.png)

@ -0,0 +1,485 @@
'use strict';
var gulp = require('gulp');
var fs = require('fs');
var pkg = require('./package.json');
var iopackage = require('./io-package.json');
var version = (pkg && pkg.version) ? pkg.version : iopackage.common.version;
/*var appName = getAppName();
function getAppName() {
var parts = __dirname.replace(/\\/g, '/').split('/');
return parts[parts.length - 1].split('.')[0].toLowerCase();
}
*/
const fileName = 'words.js';
var languages = {
en: {},
de: {},
ru: {},
pt: {},
nl: {},
fr: {},
it: {},
es: {},
pl: {}
};
function lang2data(lang, isFlat) {
var str = isFlat ? '' : '{\n';
var count = 0;
for (var w in lang) {
if (lang.hasOwnProperty(w)) {
count++;
if (isFlat) {
str += (lang[w] === '' ? (isFlat[w] || w) : lang[w]) + '\n';
} else {
var key = ' "' + w.replace(/"/g, '\\"') + '": ';
str += key + '"' + lang[w].replace(/"/g, '\\"') + '",\n';
}
}
}
if (!count) return isFlat ? '' : '{\n}';
if (isFlat) {
return str;
} else {
return str.substring(0, str.length - 2) + '\n}';
}
}
function readWordJs(src) {
try {
var words;
if (fs.existsSync(src + 'js/' + fileName)) {
words = fs.readFileSync(src + 'js/' + fileName).toString();
} else {
words = fs.readFileSync(src + fileName).toString();
}
var lines = words.split(/\r\n|\r|\n/g);
var i = 0;
while (!lines[i].match(/^systemDictionary = {/)) {
i++;
}
lines.splice(0, i);
// remove last empty lines
i = lines.length - 1;
while (!lines[i]) {
i--;
}
if (i < lines.length - 1) {
lines.splice(i + 1);
}
lines[0] = lines[0].replace('systemDictionary = ', '');
lines[lines.length - 1] = lines[lines.length - 1].trim().replace(/};$/, '}');
words = lines.join('\n');
var resultFunc = new Function('return ' + words + ';');
return resultFunc();
} catch (e) {
return null;
}
}
function padRight(text, totalLength) {
return text + (text.length < totalLength ? new Array(totalLength - text.length).join(' ') : '');
}
function writeWordJs(data, src) {
var text = '';
text += '/*global systemDictionary:true */\n';
text += '\'use strict\';\n\n';
text += 'systemDictionary = {\n';
for (var word in data) {
if (data.hasOwnProperty(word)) {
text += ' ' + padRight('"' + word.replace(/"/g, '\\"') + '": {', 50);
var line = '';
for (var lang in data[word]) {
if (data[word].hasOwnProperty(lang)) {
line += '"' + lang + '": "' + padRight(data[word][lang].replace(/"/g, '\\"') + '",', 50) + ' ';
}
}
if (line) {
line = line.trim();
line = line.substring(0, line.length - 1);
}
text += line + '},\n';
}
}
text += '};';
if (fs.existsSync(src + 'js/' + fileName)) {
fs.writeFileSync(src + 'js/' + fileName, text);
} else {
fs.writeFileSync(src + '' + fileName, text);
}
}
const EMPTY = '';
function words2languages(src) {
var langs = Object.assign({}, languages);
var data = readWordJs(src);
if (data) {
for (var word in data) {
if (data.hasOwnProperty(word)) {
for (var lang in data[word]) {
if (data[word].hasOwnProperty(lang)) {
langs[lang][word] = data[word][lang];
// pre-fill all other languages
for (var j in langs) {
if (langs.hasOwnProperty(j)) {
langs[j][word] = langs[j][word] || EMPTY;
}
}
}
}
}
}
if (!fs.existsSync(src + 'i18n/')) {
fs.mkdirSync(src + 'i18n/');
}
for (var l in langs) {
if (!langs.hasOwnProperty(l)) continue;
var keys = Object.keys(langs[l]);
keys.sort();
var obj = {};
for (var k = 0; k < keys.length; k++) {
obj[keys[k]] = langs[l][keys[k]];
}
if (!fs.existsSync(src + 'i18n/' + l)) {
fs.mkdirSync(src + 'i18n/' + l);
}
fs.writeFileSync(src + 'i18n/' + l + '/translations.json', lang2data(obj));
}
} else {
console.error('Cannot read or parse ' + fileName);
}
}
function words2languagesFlat(src) {
var langs = Object.assign({}, languages);
var data = readWordJs(src);
if (data) {
for (var word in data) {
if (data.hasOwnProperty(word)) {
for (var lang in data[word]) {
if (data[word].hasOwnProperty(lang)) {
langs[lang][word] = data[word][lang];
// pre-fill all other languages
for (var j in langs) {
if (langs.hasOwnProperty(j)) {
langs[j][word] = langs[j][word] || EMPTY;
}
}
}
}
}
}
var keys = Object.keys(langs.en);
keys.sort();
for (var l in langs) {
if (!langs.hasOwnProperty(l)) continue;
var obj = {};
for (var k = 0; k < keys.length; k++) {
obj[keys[k]] = langs[l][keys[k]];
}
langs[l] = obj;
}
if (!fs.existsSync(src + 'i18n/')) {
fs.mkdirSync(src + 'i18n/');
}
for (var ll in langs) {
if (!langs.hasOwnProperty(ll)) continue;
if (!fs.existsSync(src + 'i18n/' + ll)) {
fs.mkdirSync(src + 'i18n/' + ll);
}
fs.writeFileSync(src + 'i18n/' + ll + '/flat.txt', lang2data(langs[ll], langs.en));
}
fs.writeFileSync(src + 'i18n/flat.txt', keys.join('\n'));
} else {
console.error('Cannot read or parse ' + fileName);
}
}
function languagesFlat2words(src) {
var dirs = fs.readdirSync(src + 'i18n/');
var langs = {};
var bigOne = {};
var order = Object.keys(languages);
dirs.sort(function (a, b) {
var posA = order.indexOf(a);
var posB = order.indexOf(b);
if (posA === -1 && posB === -1) {
if (a > b) return 1;
if (a < b) return -1;
return 0;
} else if (posA === -1) {
return -1;
} else if (posB === -1) {
return 1;
} else {
if (posA > posB) return 1;
if (posA < posB) return -1;
return 0;
}
});
var keys = fs.readFileSync(src + 'i18n/flat.txt').toString().split('\n');
for (var l = 0; l < dirs.length; l++) {
if (dirs[l] === 'flat.txt') continue;
var lang = dirs[l];
var values = fs.readFileSync(src + 'i18n/' + lang + '/flat.txt').toString().split('\n');
langs[lang] = {};
keys.forEach(function (word, i) {
langs[lang][word] = values[i];
});
var words = langs[lang];
for (var word in words) {
if (words.hasOwnProperty(word)) {
bigOne[word] = bigOne[word] || {};
if (words[word] !== EMPTY) {
bigOne[word][lang] = words[word];
}
}
}
}
// read actual words.js
var aWords = readWordJs();
var temporaryIgnore = ['pt', 'fr', 'nl', 'flat.txt'];
if (aWords) {
// Merge words together
for (var w in aWords) {
if (aWords.hasOwnProperty(w)) {
if (!bigOne[w]) {
console.warn('Take from actual words.js: ' + w);
bigOne[w] = aWords[w]
}
dirs.forEach(function (lang) {
if (temporaryIgnore.indexOf(lang) !== -1) return;
if (!bigOne[w][lang]) {
console.warn('Missing "' + lang + '": ' + w);
}
});
}
}
}
writeWordJs(bigOne, src);
}
function languages2words(src) {
var dirs = fs.readdirSync(src + 'i18n/');
var langs = {};
var bigOne = {};
var order = Object.keys(languages);
dirs.sort(function (a, b) {
var posA = order.indexOf(a);
var posB = order.indexOf(b);
if (posA === -1 && posB === -1) {
if (a > b) return 1;
if (a < b) return -1;
return 0;
} else if (posA === -1) {
return -1;
} else if (posB === -1) {
return 1;
} else {
if (posA > posB) return 1;
if (posA < posB) return -1;
return 0;
}
});
for (var l = 0; l < dirs.length; l++) {
if (dirs[l] === 'flat.txt') continue;
var lang = dirs[l];
langs[lang] = fs.readFileSync(src + 'i18n/' + lang + '/translations.json').toString();
langs[lang] = JSON.parse(langs[lang]);
var words = langs[lang];
for (var word in words) {
if (words.hasOwnProperty(word)) {
bigOne[word] = bigOne[word] || {};
if (words[word] !== EMPTY) {
bigOne[word][lang] = words[word];
}
}
}
}
// read actual words.js
var aWords = readWordJs();
var temporaryIgnore = ['pt', 'fr', 'nl', 'it'];
if (aWords) {
// Merge words together
for (var w in aWords) {
if (aWords.hasOwnProperty(w)) {
if (!bigOne[w]) {
console.warn('Take from actual words.js: ' + w);
bigOne[w] = aWords[w]
}
dirs.forEach(function (lang) {
if (temporaryIgnore.indexOf(lang) !== -1) return;
if (!bigOne[w][lang]) {
console.warn('Missing "' + lang + '": ' + w);
}
});
}
}
}
writeWordJs(bigOne, src);
}
gulp.task('adminWords2languages', function (done) {
words2languages('./admin/');
done();
});
gulp.task('adminWords2languagesFlat', function (done) {
words2languagesFlat('./admin/');
done();
});
gulp.task('adminLanguagesFlat2words', function (done) {
languagesFlat2words('./admin/');
done();
});
gulp.task('adminLanguages2words', function (done) {
languages2words('./admin/');
done();
});
gulp.task('updatePackages', function (done) {
iopackage.common.version = pkg.version;
iopackage.common.news = iopackage.common.news || {};
if (!iopackage.common.news[pkg.version]) {
var news = iopackage.common.news;
var newNews = {};
newNews[pkg.version] = {
en: 'news',
de: 'neues',
ru: 'новое'
};
iopackage.common.news = Object.assign(newNews, news);
}
fs.writeFileSync('io-package.json', JSON.stringify(iopackage, null, 4));
done();
});
gulp.task('rename', function () {
var newname;
var author = '@@Author@@';
var email = '@@email@@';
for (var a = 0; a < process.argv.length; a++) {
if (process.argv[a] === '--name') {
newname = process.argv[a + 1]
} else if (process.argv[a] === '--email') {
email = process.argv[a + 1]
} else if (process.argv[a] === '--author') {
author = process.argv[a + 1]
}
}
console.log('Try to rename to "' + newname + '"');
if (!newname) {
console.log('Please write the new template name, like: "gulp 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/template.png')) {
fs.renameSync(__dirname + '/admin/template.png', __dirname + '/admin/' + newname + '.png');
}
if (fs.existsSync(__dirname + '/widgets/template.html')) {
fs.renameSync(__dirname + '/widgets/template.html', __dirname + '/widgets/' + newname + '.html');
}
if (fs.existsSync(__dirname + '/widgets/template/js/template.js')) {
fs.renameSync(__dirname + '/widgets/template/js/template.js', __dirname + '/widgets/template/js/' + newname + '.js');
}
if (fs.existsSync(__dirname + '/widgets/template')) {
fs.renameSync(__dirname + '/widgets/template', __dirname + '/widgets/' + newname);
}
var patterns = [
{
match: /template/g,
replacement: newname
},
{
match: /Template/g,
replacement: newname ? (newname[0].toUpperCase() + newname.substring(1)) : 'Template'
},
{
match: /@@Author@@/g,
replacement: author
},
{
match: /@@email@@/g,
replacement: email
}
];
var files = [
__dirname + '/io-package.json',
__dirname + '/LICENSE',
__dirname + '/package.json',
__dirname + '/README.md',
__dirname + '/main.js',
__dirname + '/Gruntfile.js',
__dirname + '/widgets/' + newname +'.html',
__dirname + '/www/index.html',
__dirname + '/admin/index.html',
__dirname + '/admin/index_m.html',
__dirname + '/widgets/' + newname + '/js/' + newname +'.js',
__dirname + '/widgets/' + newname + '/css/style.css'
];
files.forEach(function (f) {
try {
if (fs.existsSync(f)) {
var data = fs.readFileSync(f).toString('utf-8');
for (var r = 0; r < patterns.length; r++) {
data = data.replace(patterns[r].match, patterns[r].replacement);
}
fs.writeFileSync(f, data);
}
} catch (e) {
}
});
});
gulp.task('updateReadme', function (done) {
var readme = fs.readFileSync('README.md').toString();
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.news && iopackage.common.news[pkg.version]) {
news += '* ' + iopackage.common.news[pkg.version].en;
}
fs.writeFileSync('README.md', readmeStart + '### ' + version + ' (' + date + ')\n' + (news ? news + '\n\n' : '\n') + readmeEnd);
}
}
done();
});
gulp.task('default', ['updatePackages', 'updateReadme']);

@ -0,0 +1,78 @@
{
"common": {
"name": "onvif",
"version": "0.0.1",
"news": {
"0.0.1": {
"en": "initial adapter",
"de": "Initiale Version",
"ru": "Первоначальный адаптер",
"pt": "Versão inicial",
"fr": "Version initiale",
"nl": "Eerste release"
}
},
"title": "Onvif adapter",
"titleLang": {
"en": "Onvif adapter",
"de": "Onvif Vorlagenadapter",
"ru": "Адаптер Onvif",
"pt": "Adaptador Onvif",
"nl": "Onvif sjabloonadapter",
"fr": "Onvif adaptateur",
"it": "Adattatore Onvif",
"es": "Adaptador Onvif"
},
"desc": {
"en": "Onvif Adapter",
"de": "Onvif Adapter",
"ru": "Onvif драйвер",
"pt": "Onvif adaptador",
"fr": "Onvif adaptateur",
"nl": "Onvif Adapter",
"it": "Adattatore Onvif",
"es": "Adaptador Onvif"
},
"authors": [
"Kirov Ilya <kirovilya@gmail.com>"
],
"docs": {
"en": "docs/en/admin.md",
"ru": "docs/ru/admin.md",
"de": "docs/de/admin.md",
"es": "docs/es/admin.md",
"it": "docs/it/admin.md",
"fr": "docs/fr/admin.md",
"nl": "docs/nl/admin.md",
"pt": "docs/pt/admin.md"
},
"platform": "Javascript/Node.js",
"mode": "daemon",
"icon": "onvif.png",
"materialize": true,
"enabled": true,
"extIcon": "https://git.spacen.net/kirovilya/yunkong2.onvif/raw/master/admin/onvif.png",
"keywords": ["onvif", "camera"],
"readme": "https://git.spacen.net/kirovilya/yunkong2.onvif/blob/master/README.md",
"loglevel": "info",
"type": "general",
"messagebox": true
},
"native": {
},
"instanceObjects": [
{
"_id": "discoveryRunning",
"type": "state",
"common": {
"name": "Scannig mode",
"type": "boolean",
"read": true,
"write": false,
"def": false
},
"native": {}
}
]
}

@ -0,0 +1,64 @@
var controllerDir;
var appName;
function getAppName() {
var parts = __dirname.replace(/\\/g, '/').split('/');
return parts[parts.length - 2].split('.')[0];
}
// Get js-controller directory to load libs
function getControllerDir(isInstall) {
var fs = require('fs');
// Find the js-controller location
var controllerDir = __dirname.replace(/\\/g, '/');
controllerDir = controllerDir.split('/');
if (controllerDir[controllerDir.length - 3] === 'adapter') {
controllerDir.splice(controllerDir.length - 3, 3);
controllerDir = controllerDir.join('/');
} else if (controllerDir[controllerDir.length - 3] === 'node_modules') {
controllerDir.splice(controllerDir.length - 3, 3);
controllerDir = controllerDir.join('/');
if (fs.existsSync(controllerDir + '/node_modules/' + appName + '.js-controller')) {
controllerDir += '/node_modules/' + appName + '.js-controller';
} else if (fs.existsSync(controllerDir + '/node_modules/' + appName.toLowerCase() + '.js-controller')) {
controllerDir += '/node_modules/' + appName.toLowerCase() + '.js-controller';
} else if (!fs.existsSync(controllerDir + '/controller.js')) {
if (!isInstall) {
console.log('Cannot find js-controller');
process.exit(10);
} else {
process.exit();
}
}
} else if (fs.existsSync(__dirname + '/../../node_modules/' + appName.toLowerCase() + '.js-controller')) {
controllerDir.splice(controllerDir.length - 2, 2);
return controllerDir.join('/') + '/node_modules/' + appName.toLowerCase() + '.js-controller';
} else {
if (!isInstall) {
console.log('Cannot find js-controller');
process.exit(10);
} else {
process.exit();
}
}
return controllerDir;
}
// Read controller configuration file
function getConfig() {
var fs = require('fs');
if (fs.existsSync(controllerDir + '/conf/' + appName + '.json')) {
return JSON.parse(fs.readFileSync(controllerDir + '/conf/' + appName + '.json'));
} else if (fs.existsSync(controllerDir + '/conf/' + appName.toLowerCase() + '.json')) {
return JSON.parse(fs.readFileSync(controllerDir + '/conf/' + appName.toLowerCase() + '.json'));
} else {
throw new Error('Cannot find ' + controllerDir + '/conf/' + appName + '.json');
}
}
appName = getAppName();
controllerDir = getControllerDir(typeof process !== 'undefined' && process.argv && process.argv.indexOf('--install') !== -1);
exports.controllerDir = controllerDir;
exports.getConfig = getConfig;
exports.Adapter = require(controllerDir + '/lib/adapter.js');
exports.appName = appName;

@ -0,0 +1,542 @@
/**
*
* Onvif adapter
*
*/
/* jshint -W097 */// jshint strict:false
/*jslint node: true */
'use strict';
// you have to require the utils module and call adapter function
var utils = require(__dirname + '/lib/utils'); // Get common adapter utils
// you have to call the adapter function and pass a options object
// name has to be set and has to be equal to adapters folder name and main file name excluding extension
// adapter will be restarted automatically every time as the configuration changed, e.g system.adapter.template.0
var adapter = new utils.Adapter('onvif');
var Cam = require('onvif').Cam;
var flow = require('nimble');
require('onvif-snapshot');
var url = require('url');
var inherits = require('util').inherits;
var isDiscovery = false;
var cameras = {};
function override(child, fn) {
child.prototype[fn.name] = fn;
fn.inherited = child.super_.prototype[fn.name];
}
// overload Cam to preserve original hostname
function MyCam(options, callback) {
MyCam.super_.call(this, options, callback);
}
inherits(MyCam, Cam);
override(MyCam, function getSnapshotUri(options, callback) {
getSnapshotUri.inherited.call(this, options, function(err, res){
if(!err) {
const parsedAddress = url.parse(res.uri);
// If host for service and default host dirrers, also if preserve address property set
// we substitute host, hostname and port from settings
if (this.hostname !== parsedAddress.hostname) {
adapter.log.debug('need replace '+res.uri);
res.uri = res.uri.replace(parsedAddress.hostname, this.hostname);
adapter.log.debug('after replace '+res.uri);
}
}
if (callback) callback(err, res);
});
});
// is called when adapter shuts down - callback has to be called under any circumstances!
adapter.on('unload', function (callback) {
if (isDiscovery) {
adapter && adapter.setState && adapter.setState('discoveryRunning', false, true);
isDiscovery = false;
}
try {
adapter.log.debug('cleaned everything up...');
callback();
} catch (e) {
callback();
}
});
// is called if a subscribed object changes
adapter.on('objectChange', function (id, obj) {
// Warning, obj can be null if it was deleted
adapter.log.debug('objectChange ' + id + ' ' + JSON.stringify(obj));
});
// is called if a subscribed state changes
adapter.on('stateChange', function (id, state) {
// Warning, state can be null if it was deleted
adapter.log.debug('stateChange ' + id + ' ' + JSON.stringify(state));
// you can use the ack flag to detect if it is status (true) or command (false)
if (state && !state.ack) {
adapter.log.debug('ack is not set!');
}
});
// Some message was sent to adapter instance over message box. Used by email, pushover, text2speech, ...
adapter.on('message', function (obj) {
if (!obj || !obj.command) return;
switch (obj.command) {
case 'discovery':
adapter.log.debug('Received "discovery" event');
discovery(obj.message, function (error, newInstances, devices) {
isDiscovery = false;
adapter.log.debug('Discovery finished');
adapter.setState('discoveryRunning', false, true);
adapter.sendTo(obj.from, obj.command, {
error: error,
devices: devices,
newInstances: newInstances
}, obj.callback);
});
break;
case 'getDevices':
adapter.log.debug('Received "getDevices" event');
getDevices(obj.from, obj.command, obj.message, obj.callback);
break;
case 'deleteDevice':
adapter.log.debug('Received "deleteDevice" event');
deleteDevice(obj.from, obj.command, obj.message, obj.callback);
break;
case 'getSnapshot':
adapter.log.debug('Received "getSnapshot" event');
getSnapshot(obj.from, obj.command, obj.message, obj.callback);
break;
default:
adapter.log.debug('Unknown message: ' + JSON.stringify(obj));
break;
}
});
// is called when databases are connected and adapter received configuration.
// start here!
adapter.on('ready', function () {
main();
});
function main() {
isDiscovery = false;
adapter.setState('discoveryRunning', false, true);
// in this template all states changes inside the adapters namespace are subscribed
adapter.subscribeStates('*');
// connect to cameras
startCameras();
}
function getSnapshot(from, command, message, callback){
var camId = message.id,
cam = cameras[camId];
adapter.log.debug('getSnapshot: ' + JSON.stringify(message));
if (cam) {
// get snapshot
cam.getSnapshot((err, data) => {
if(err) throw err;
//adapter.log.debug(JSON.stringify(data));
adapter.sendTo(from, command, data, callback);
});
}
}
function camEvents(camMessage) {
adapter.log.debug('camEvents: ' + JSON.stringify(camMessage));
}
function startCameras(){
cameras = {};
adapter.log.debug('startCameras');
adapter.getDevices((err, result) => {
adapter.log.debug('startCameras: ' + JSON.stringify(result));
for (var item in result) {
let dev = result[item],
devData = dev.common.data,
cam;
//updateState(dev._id, 'connected', false, {type: 'boolean'});
cam = new MyCam({
hostname: devData.ip,
port: devData.port,
username: devData.user,
password: devData.pass,
timeout : 5000,
preserveAddress: true
}, function(err) {
if (!err) {
adapter.log.debug('capabilities: ' + JSON.stringify(cam.capabilities));
adapter.log.debug('uri: ' + JSON.stringify(cam.uri));
//updateState(dev._id, 'connected', true, {type: 'boolean'});
cameras[dev._id] = cam;
cam.on('event', camEvents);
} else {
adapter.log.info('startCameras err=' + err +' dev='+ JSON.stringify(devData));
}
});
}
});
}
function updateState(dev_id, name, value, common) {
var id = dev_id + '.' + name;
adapter.getObject(dev_id, function(err, obj) {
if (obj) {
let new_common = {
name: name,
role: (common != undefined && common.role == undefined) ? 'value' : common.role,
read: true,
write: (common != undefined && common.write == undefined) ? false : true
};
if (common != undefined) {
if (common.type != undefined) {
new_common.type = common.type;
}
if (common.unit != undefined) {
new_common.unit = common.unit;
}
if (common.states != undefined) {
new_common.states = common.states;
}
}
adapter.extendObject(id, {type: 'state', common: new_common});
adapter.setState(id, value, true);
} else {
adapter.log.info('no device '+dev_id);
}
});
}
function deleteDevice(from, command, msg, callback) {
var id = msg.id,
dev_id = id.replace(adapter.namespace+'.', '');
adapter.log.info('delete device '+dev_id);
adapter.deleteDevice(dev_id, function(){
adapter.sendTo(from, command, {}, callback);
});
}
function getDevices(from, command, message, callback){
var rooms;
adapter.getEnums('enum.rooms', function (err, list) {
if (!err){
rooms = list['enum.rooms'];
}
adapter.getDevices((err, result) => {
if (result) {
var devices = [], cnt = 0, len = result.length;
for (var item in result) {
if (result[item]._id) {
var id = result[item]._id.substr(adapter.namespace.length + 1);
let devInfo = result[item];
devInfo.rooms = [];
for (var room in rooms) {
if (!rooms[room] || !rooms[room].common || !rooms[room].common.members)
continue;
if (rooms[room].common.members.indexOf(devInfo._id) !== -1) {
devInfo.rooms.push(rooms[room].common.name);
}
}
cnt++;
devices.push(devInfo);
if (cnt==len) {
adapter.log.debug('getDevices result: ' + JSON.stringify(devices));
adapter.sendTo(from, command, devices, callback);
}
// adapter.getState(result[item]._id+'.paired', function(err, state){
// cnt++;
// if (state) {
// devInfo.paired = state.val;
// }
// devices.push(devInfo);
// if (cnt==len) {
// adapter.log.info('getDevices result: ' + JSON.stringify(devices));
// adapter.sendTo(from, command, devices, callback);
// }
// });
}
}
if (len == 0) {
adapter.log.debug('getDevices result: ' + JSON.stringify(devices));
adapter.sendTo(from, command, devices, callback);
}
}
});
});
}
function discovery(options, callback) {
if (isDiscovery) {
return callback && callback('Yet running');
}
isDiscovery = true;
adapter.setState('discoveryRunning', true, true);
var start_range = options.start_range, //'192.168.1.1'
end_range = options.end_range || options.start_range, //'192.168.1.254'
port_list = options.ports || '80, 7575, 8000, 8080, 8081',
port_list = port_list.split(',').map(item => item.trim()),
user = options.user || 'admin', // 'admin'
pass = options.pass || 'admin'; // 'admin'
var ip_list = generate_range(start_range, end_range);
if (ip_list.length === 1 && ip_list[0] === '0.0.0.0') {
ip_list = [options.start_range];
}
var devices = [], counter = 0, scanLen = ip_list.length * port_list.length;
// try each IP address and each Port
ip_list.forEach(function(ip_entry) {
port_list.forEach(function(port_entry) {
adapter.log.debug(ip_entry + ' ' + port_entry);
new MyCam({
hostname: ip_entry,
username: user,
password: pass,
port: port_entry,
timeout : 5000,
preserveAddress: true
}, function CamFunc(err) {
counter++;
if (err) {
if (counter == scanLen) processScannedDevices(devices, callback);
return;
}
var cam_obj = this;
var got_date;
var got_info;
var got_live_stream_tcp;
var got_live_stream_udp;
var got_live_stream_multicast;
var got_recordings;
var got_replay_stream;
// Use Nimble to execute each ONVIF function in turn
// This is used so we can wait on all ONVIF replies before
// writing to the console
flow.series([
function(callback) {
cam_obj.getSystemDateAndTime(function(err, date, xml) {
if (!err) got_date = date;
callback();
});
},
function(callback) {
cam_obj.getDeviceInformation(function(err, info, xml) {
if (!err) got_info = info;
callback();
});
},
function(callback) {
try {
cam_obj.getStreamUri({
protocol: 'RTSP',
stream: 'RTP-Unicast'
}, function(err, stream, xml) {
if (!err) got_live_stream_tcp = stream;
callback();
});
} catch(err) {callback();}
},
function(callback) {
try {
cam_obj.getStreamUri({
protocol: 'UDP',
stream: 'RTP-Unicast'
}, function(err, stream, xml) {
if (!err) got_live_stream_udp = stream;
callback();
});
} catch(err) {callback();}
},
function(callback) {
try {
cam_obj.getStreamUri({
protocol: 'UDP',
stream: 'RTP-Multicast'
}, function(err, stream, xml) {
if (!err) got_live_stream_multicast = stream;
callback();
});
} catch(err) {callback();}
},
function(callback) {
cam_obj.getRecordings(function(err, recordings, xml) {
if (!err) got_recordings = recordings;
callback();
});
},
function(callback) {
// Get Recording URI for the first recording on the NVR
if (got_recordings) {
//adapter.log.debug('got_recordings='+JSON.stringify(got_recordings));
if (Array.isArray(got_recordings)) {
got_recordings = got_recordings[0];
}
cam_obj.getReplayUri({
protocol: 'RTSP',
recordingToken: got_recordings.recordingToken
}, function(err, stream, xml) {
if (!err) got_replay_stream = stream;
callback();
});
} else {
callback();
}
},
function(localcallback) {
adapter.log.debug('------------------------------');
adapter.log.debug('Host: ' + ip_entry + ' Port: ' + port_entry);
adapter.log.debug('Date: = ' + got_date);
adapter.log.debug('Info: = ' + JSON.stringify(got_info));
if (got_live_stream_tcp) {
adapter.log.debug('First Live TCP Stream: = ' + got_live_stream_tcp.uri);
}
if (got_live_stream_udp) {
adapter.log.debug('First Live UDP Stream: = ' + got_live_stream_udp.uri);
}
if (got_live_stream_multicast) {
adapter.log.debug('First Live Multicast Stream: = ' + got_live_stream_multicast.uri);
}
if (got_replay_stream) {
adapter.log.debug('First Replay Stream: = ' + got_replay_stream.uri);
}
adapter.log.debug('capabilities: ' + JSON.stringify(cam_obj.capabilities));
adapter.log.debug('------------------------------');
devices.push({
id: getId(ip_entry+':'+port_entry),
name: ip_entry+':'+port_entry,
ip: ip_entry,
port: port_entry,
user: user,
pass: pass,
ip: ip_entry,
port: port_entry,
cam_date: got_date,
info: got_info,
live_stream_tcp: got_live_stream_tcp,
live_stream_udp: got_live_stream_udp,
live_stream_multicast: got_live_stream_multicast,
replay_stream: got_replay_stream
});
localcallback();
if (counter == scanLen) processScannedDevices(devices, callback);
}
]); // end flow
});
}); // foreach
}); // foreach
}
function processScannedDevices(devices, callback) {
// check if device is new
var newInstances = [], currDevs = [];
adapter.getDevices((err, result) => {
if(result) {
for (var item in result) {
if (result[item]._id) {
currDevs.push(result[item]._id);
}
}
}
for (var devInd in devices) {
var dev = devices[devInd];
if (currDevs.indexOf(dev.id) == -1) {
newInstances.push(dev);
// create new camera
updateDev(dev.id, dev.name, dev);
}
}
startCameras();
if (callback) callback(newInstances);
});
}
function updateDev(dev_id, dev_name, devData) {
// create dev
adapter.setObjectNotExists(dev_id, {
type: 'device',
common: {name: dev_name, data: devData}
}, {}, function (obj) {
adapter.getObject(dev_id, function(err, obj) {
if (!err && obj) {
// if update
adapter.extendObject(dev_id, {
type: 'device',
common: {data: devData}
});
startCameras();
}
});
});
}
function getId(addr) {
return addr.replace(/\./g, '_').replace(':', '_');
}
function generate_range(start_ip, end_ip) {
var start_long = toLong(start_ip);
var end_long = toLong(end_ip);
if (start_long > end_long) {
var tmp=start_long;
start_long=end_long
end_long=tmp;
}
var range_array = [];
var i;
for (i=start_long; i<=end_long;i++) {
range_array.push(fromLong(i));
}
return range_array;
}
//toLong taken from NPM package 'ip'
function toLong(ip) {
var ipl = 0;
ip.split('.').forEach(function(octet) {
ipl <<= 8;
ipl += parseInt(octet);
});
return(ipl >>> 0);
};
//fromLong taken from NPM package 'ip'
function fromLong(ipl) {
return ((ipl >>> 24) + '.' +
(ipl >> 16 & 255) + '.' +
(ipl >> 8 & 255) + '.' +
(ipl & 255) );
};

@ -0,0 +1,44 @@
{
"name": "yunkong2.onvif",
"version": "0.0.1",
"description": "yunkong2 onvif dapter",
"author": {
"name": "Kirov Ilya",
"email": "kirovilya@gmail.com"
},
"contributors": [
{
"name": "Kirov Ilya",
"email": "kirovilya@gmail.com"
}
],
"homepage": "https://git.spacen.net/kirovilya/yunkong2.onvif",
"license": "MIT",
"keywords": [
"yunkong2",
"onvif",
"camera"
],
"repository": {
"type": "git",
"url": "git+https://git.spacen.net/kirovilya/yunkong2.onvif.git"
},
"dependencies": {
"onvif": "^0.6.0",
"nimble": "^0.0.2",
"onvif-snapshot": "^1.0.2"
},
"devDependencies": {
"gulp": "^3.9.1",
"mocha": "^4.1.0",
"chai": "^4.1.2"
},
"main": "main.js",
"scripts": {
"test": "node node_modules/mocha/bin/mocha --exit"
},
"bugs": {
"url": "https://git.spacen.net/kirovilya/yunkong2.onvif/issues"
},
"readmeFilename": "README.md"
}

@ -0,0 +1,696 @@
/* 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));
}
}
fs.writeFileSync(targetFile, fs.readFileSync(source));
}
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);
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, targetFolder);
}
});
}
}
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();
}
if (cb) cb();
});
}
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);
}
}
},
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!!');
startAdapter(objects, states, callback);
}
},
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,47 @@
/* 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) {
var fileContentIOPackage = fs.readFileSync(__dirname + '/../io-package.json');
var ioPackage = JSON.parse(fileContentIOPackage);
var fileContentNPMPackage = fs.readFileSync(__dirname + '/../package.json');
var npmPackage = JSON.parse(fileContentNPMPackage);
expect(ioPackage).to.be.an('object');
expect(npmPackage).to.be.an('object');
expect(ioPackage.common.version).to.exist;
expect(npmPackage.version).to.exist;
if (!expect(ioPackage.common.version).to.be.equal(npmPackage.version)) {
console.log('ERROR: Version numbers in package.json and io-package.json differ!!');
}
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!');
}
expect(ioPackage.common.authors).to.exist;
if (ioPackage.common.name.indexOf('template') !== 0) {
if (Array.isArray(ioPackage.common.authors)) {
expect(ioPackage.common.authors.length).to.not.be.equal(0);
if (ioPackage.common.authors.length === 1) {
expect(ioPackage.common.authors[0]).to.not.be.equal('my Name <my@email.com>');
}
}
else {
expect(ioPackage.common.authors).to.not.be.equal('my Name <my@email.com>');
}
}
else {
console.log('Testing for set authors field in io-package skipped because template adapter');
}
done();
});
});
Loading…
Cancel
Save