@ -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>
|
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 27 KiB |
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'
|
After Width: | Height: | Size: 2.3 KiB |
@ -0,0 +1,3 @@
|
||||
# Das ist die Dokumentation
|
||||
|
||||
(Picture)[img/picture.png)
|
After Width: | Height: | Size: 2.3 KiB |
@ -0,0 +1,3 @@
|
||||
# This is Documentation
|
||||
|
||||
(Picture)[img/picture.png)
|
After Width: | Height: | Size: 2.3 KiB |
@ -0,0 +1,3 @@
|
||||
# This is Documentation
|
||||
|
||||
(Picture)[img/picture.png)
|
After Width: | Height: | Size: 2.3 KiB |
@ -0,0 +1,3 @@
|
||||
# This is Documentation
|
||||
|
||||
(Picture)[img/picture.png)
|
After Width: | Height: | Size: 2.3 KiB |
@ -0,0 +1,3 @@
|
||||
# This is Documentation
|
||||
|
||||
(Picture)[img/picture.png)
|
After Width: | Height: | Size: 2.3 KiB |
@ -0,0 +1,3 @@
|
||||
# This is Documentation
|
||||
|
||||
(Picture)[img/picture.png)
|
After Width: | Height: | Size: 2.3 KiB |
@ -0,0 +1,3 @@
|
||||
# This is Documentation
|
||||
|
||||
(Picture)[img/picture.png)
|
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();
|
||||
});
|
||||
});
|