(bluefox) initial commit
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
node_modules
|
||||||
|
.idea
|
||||||
|
tmp
|
||||||
|
admin/i18n/flat.txt
|
||||||
|
admin/i18n/*/flat.txt
|
||||||
|
iob_npm.done
|
||||||
|
package-lock.json
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
gulpfile.js
|
||||||
|
tasks
|
||||||
|
tmp
|
||||||
|
test
|
||||||
|
.travis.yml
|
||||||
|
appveyor.yml
|
||||||
|
admin/i18n
|
||||||
|
iob_npm.done
|
||||||
|
package-lock.json
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
os:
|
||||||
|
- linux
|
||||||
|
- osx
|
||||||
|
language: node_js
|
||||||
|
node_js:
|
||||||
|
- '4'
|
||||||
|
- '6'
|
||||||
|
- '8'
|
||||||
|
services:
|
||||||
|
- mysql
|
||||||
|
- postgresql
|
||||||
|
before_install:
|
||||||
|
- 'if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export export CXX=g++-4.8; fi'
|
||||||
|
- npm install -g npm@3
|
||||||
|
before_script:
|
||||||
|
- 'if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi'
|
||||||
|
- 'if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install mariadb && mysql.server start; fi'
|
||||||
|
- 'if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then rm -rf /usr/local/var/postgres; fi'
|
||||||
|
- 'if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then initdb /usr/local/var/postgres; fi'
|
||||||
|
- 'if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then pg_ctl -D /usr/local/var/postgres start; fi'
|
||||||
|
- 'if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then sleep 5; fi'
|
||||||
|
- 'if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then createuser -s postgres; fi'
|
||||||
|
- sleep 15
|
||||||
|
- npm install winston@2.3.1
|
||||||
|
- 'npm install https://github.com/ioBroker/ioBroker.js-controller/tarball/master --production'
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
sources:
|
||||||
|
- ubuntu-toolchain-r-test
|
||||||
|
packages:
|
||||||
|
- g++-4.8
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2018 bluefox <dogafox@gmail.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|

|
||||||
|
# ioBroker.hass
|
||||||
|
=================
|
||||||
|
|
||||||
|
[](https://www.npmjs.com/package/iobroker.hass)
|
||||||
|
[](https://www.npmjs.com/package/iobroker.hass)
|
||||||
|
|
||||||
|
[](https://nodei.co/npm/iobroker.hass/)
|
||||||
|
|
||||||
|
|
||||||
|
This adapter allows connect Home Assistant to ioBroker.
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
#### 0.1.0
|
||||||
|
* (bluefox) initial release
|
||||||
|
|
||||||
|
## License
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2018 bluefox <dogafox@gmail.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 7.5 KiB |
@@ -0,0 +1,76 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" type="text/css" href="../../lib/css/themes/jquery-ui/redmond/jquery-ui.min.css"/>
|
||||||
|
<link rel="stylesheet" type="text/css" href="../../css/adapter.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>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
function load(settings, onChange) {
|
||||||
|
if (!settings) return;
|
||||||
|
|
||||||
|
if (settings.password === undefined) settings.password = '';
|
||||||
|
if (settings.prompt === undefined) settings.prompt = 'hass>';
|
||||||
|
|
||||||
|
$('.value').each(function () {
|
||||||
|
var key = $(this).attr('id');
|
||||||
|
var $value = $('#' + key + '.value');
|
||||||
|
if ($value.attr('type') === 'checkbox') {
|
||||||
|
$value.prop('checked', settings[key]).change(function() {
|
||||||
|
onChange();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$value.val(settings[key]).change(function() {
|
||||||
|
onChange();
|
||||||
|
}).keyup(function() {
|
||||||
|
onChange();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Signal to admin, that no changes yet
|
||||||
|
onChange(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function save(callback) {
|
||||||
|
var obj = {};
|
||||||
|
|
||||||
|
if ($('#password').val() !== $('#passwordRepeat').val()) {
|
||||||
|
showMessage(_('Passwords missmatch!'), _('Warning'), 'alert');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.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>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="adapter-container">
|
||||||
|
<table><tr>
|
||||||
|
<td><img src="hass.png"/></td>
|
||||||
|
<td><h3 class="translate">FHEM adapter settings</h3></td>
|
||||||
|
</tr></table>
|
||||||
|
<table>
|
||||||
|
<tr><td><label class="translate" for="host">Home assistant IP:</label></td><td class="admin-icon"></td><td><input class="value" id="host" /></td></tr>
|
||||||
|
<tr><td><label class="translate" for="port">Home assistant WS Port:</label></td><td class="admin-icon"></td><td><input class="value" id="port" size="5" maxlength="5"/></td></tr>
|
||||||
|
<tr><td><label class="translate" for="password">Password:</label></td><td class="admin-icon"></td><td><input class="value" id="password" type="password"/></td></tr>
|
||||||
|
<tr><td><label class="translate" for="passwordRepeat">Password repeat:</label></td><td class="admin-icon"></td><td><input type="password" id="passwordRepeat"/></td></tr>
|
||||||
|
<tr><td><label class="translate" for="secure">Secure:</label></td><td class="admin-icon"></td><td><input class="value" id="secure" type="checkbox"/></td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" type="text/css" href="../../lib/css/themes/jquery-ui/redmond/jquery-ui.min.css"/>
|
||||||
|
<link rel="stylesheet" type="text/css" href="../../css/adapter.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>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
function load(settings, onChange) {
|
||||||
|
if (!settings) return;
|
||||||
|
|
||||||
|
if (settings.password === undefined) settings.password = '';
|
||||||
|
if (settings.prompt === undefined) settings.prompt = 'hass>';
|
||||||
|
|
||||||
|
$('.value').each(function () {
|
||||||
|
var key = $(this).attr('id');
|
||||||
|
var $value = $('#' + key + '.value');
|
||||||
|
if ($value.attr('type') === 'checkbox') {
|
||||||
|
$value.prop('checked', settings[key]).change(function() {
|
||||||
|
onChange();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$value.val(settings[key]).change(function() {
|
||||||
|
onChange();
|
||||||
|
}).keyup(function() {
|
||||||
|
onChange();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Signal to admin, that no changes yet
|
||||||
|
onChange(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function save(callback) {
|
||||||
|
var obj = {};
|
||||||
|
|
||||||
|
if ($('#password').val() !== $('#passwordRepeat').val()) {
|
||||||
|
showMessage(_('Passwords missmatch!'), _('Warning'), 'alert');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.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>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="adapter-container">
|
||||||
|
<table><tr>
|
||||||
|
<td><img src="hass.png"/></td>
|
||||||
|
<td><h3 class="translate">FHEM adapter settings</h3></td>
|
||||||
|
</tr></table>
|
||||||
|
<table>
|
||||||
|
<tr><td><label class="translate" for="host">Home assistant IP:</label></td><td class="admin-icon"></td><td><input class="value" id="host" /></td></tr>
|
||||||
|
<tr><td><label class="translate" for="port">Home assistant WS Port:</label></td><td class="admin-icon"></td><td><input class="value" id="port" size="5" maxlength="5"/></td></tr>
|
||||||
|
<tr><td><label class="translate" for="password">Password:</label></td><td class="admin-icon"></td><td><input class="value" id="password" type="password"/></td></tr>
|
||||||
|
<tr><td><label class="translate" for="passwordRepeat">Password repeat:</label></td><td class="admin-icon"></td><td><input type="password" id="passwordRepeat"/></td></tr>
|
||||||
|
<tr><td><label class="translate" for="secure">Secure:</label></td><td class="admin-icon"></td><td><input class="value" id="secure" type="checkbox"/></td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
systemDictionary = {
|
||||||
|
"FHEM adapter settings":{"de": "FHEM Adapter Einstellungen", "ru": "Настройки драйвера FHEM"},
|
||||||
|
"FHEM Telnet IP:": {"de": "FHEM Telnet IP:", "ru": "FHEM Telnet IP:"},
|
||||||
|
"FHEM Telnet Port:": {"en": "FHEM Telnet Port:", "de": "FHEM Telnet Port:", "ru": "FHEM Telnet порт:"},
|
||||||
|
"Password:": {"en": "Password:", "de": "Kennwort:", "ru": "Пароль:"},
|
||||||
|
"Password repeat:": {"en": "Password repeat:", "de": "Kennwort-Woederholung:", "ru": "Подтверждение пароля:"},
|
||||||
|
"Passwords missmatch!": {"en": "Passwords missmatch!", "de": "Kennworte sind unterschiedlich!", "ru": "Пароли не совпадают!"},
|
||||||
|
"Prompt:": {"en": "Prompt:", "de": "Prompt:", "ru": "Промпт:"},
|
||||||
|
"tooltip_password": {
|
||||||
|
"en": "Optional. Only if FHEM requires password.",
|
||||||
|
"de": "Optional. Nur falls in FHEM konfiguriert",
|
||||||
|
"ru": "Не обязательно. Только если настроено в FHEM"
|
||||||
|
},
|
||||||
|
"tooltip_prompt": {
|
||||||
|
"en": "You can change standard prompt from 'hass>' to something else.",
|
||||||
|
"de": "Man kann hier ein eigenes Prompt setzen. Default ist 'hass>'",
|
||||||
|
"ru": "Можно задать собственный промпт. По умолчанию 'hass>'"
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
version: 'test-{build}'
|
||||||
|
os: Visual Studio 2013
|
||||||
|
environment:
|
||||||
|
matrix:
|
||||||
|
- nodejs_version: '4'
|
||||||
|
- nodejs_version: '6'
|
||||||
|
- nodejs_version: '8'
|
||||||
|
platform:
|
||||||
|
- x86
|
||||||
|
- x64
|
||||||
|
clone_folder: 'c:\projects\%APPVEYOR_PROJECT_NAME%'
|
||||||
|
services:
|
||||||
|
- mssql2014
|
||||||
|
- mysql
|
||||||
|
- postgresql
|
||||||
|
install:
|
||||||
|
- ps: 'Install-Product node $env:nodejs_version $env:platform'
|
||||||
|
- npm install -g npm@3
|
||||||
|
- npm install
|
||||||
|
- npm install winston@2.3.1
|
||||||
|
- 'npm install https://github.com/ioBroker/ioBroker.js-controller/tarball/master --production'
|
||||||
|
test_script:
|
||||||
|
- echo %cd%
|
||||||
|
- node --version
|
||||||
|
- npm --version
|
||||||
|
- ps: Start-Sleep -s 15
|
||||||
|
- npm test
|
||||||
|
build: 'off'
|
||||||
+401
@@ -0,0 +1,401 @@
|
|||||||
|
'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 = '// DO NOT EDIT THIS FILE!!! IT WILL BE AUTOMATICALLY GENERATED FROM src/i18n\n';
|
||||||
|
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 = text.replace(/},\n$/, '}\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].replace(/<\/ i>/g, '</i>').replace(/<\/ b>/g, '</b>').replace(/<\/ span>/g, '</span>').replace(/% s/g, ' %s');
|
||||||
|
});
|
||||||
|
|
||||||
|
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('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,84 @@
|
|||||||
|
{
|
||||||
|
"common": {
|
||||||
|
"name": "hass",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"title": "Home Assistant",
|
||||||
|
"desc": {
|
||||||
|
"en": "Home Assistant connection for ioBroker",
|
||||||
|
"de": "Home Assistant Verbindung für ioBroker",
|
||||||
|
"ru": "Подключение Home Assistant для ioBroker",
|
||||||
|
"pt": "Conexão do Home Assistant para ioBroker",
|
||||||
|
"nl": "Home Assistant-verbinding voor ioBroker",
|
||||||
|
"fr": "Connexion Home Assistant pour ioBroker",
|
||||||
|
"it": "Connessione Home Assistant per ioBroker",
|
||||||
|
"es": "Conexión de Home Assistant para ioBroker",
|
||||||
|
"pl": "Połączenie Home Assistant dla ioBroker"
|
||||||
|
},
|
||||||
|
"news": {
|
||||||
|
"0.1.0" : {
|
||||||
|
"en": "initial version",
|
||||||
|
"de": "erste Version",
|
||||||
|
"ru": "Первоначальная версия",
|
||||||
|
"pt": "versão inicial",
|
||||||
|
"nl": "eerste versie",
|
||||||
|
"fr": "version initiale",
|
||||||
|
"it": "versione iniziale",
|
||||||
|
"es": "versión inicial",
|
||||||
|
"pl": "początkowa wersja"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"localLink": "http://%host%:8123",
|
||||||
|
"platform": "Javascript/Node.js",
|
||||||
|
"mode": "daemon",
|
||||||
|
"icon": "hass.png",
|
||||||
|
"enabled": true,
|
||||||
|
"extIcon": "https://raw.githubusercontent.com/ioBroker/ioBroker.hass/master/admin/hass.png",
|
||||||
|
"keywords": ["HASS", "Home", "assistant"],
|
||||||
|
"readme": "https://github.com/ioBroker/ioBroker.hass/blob/master/README.md",
|
||||||
|
"loglevel": "info",
|
||||||
|
"type": "iot-systems",
|
||||||
|
"authors": [
|
||||||
|
"bluefox <dogafox@gmail.com>"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"native": {
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"port": 8123,
|
||||||
|
"password": "",
|
||||||
|
"secure": false
|
||||||
|
},
|
||||||
|
"objects": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"instanceObjects": [
|
||||||
|
{
|
||||||
|
"_id": "info",
|
||||||
|
"type": "channel",
|
||||||
|
"common": {
|
||||||
|
"name": "Information"
|
||||||
|
},
|
||||||
|
"native": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "info.connection",
|
||||||
|
"type": "state",
|
||||||
|
"common": {
|
||||||
|
"role": "indicator.connected",
|
||||||
|
"name": "If connected to HASS server",
|
||||||
|
"type": "boolean",
|
||||||
|
"read": true,
|
||||||
|
"write": false,
|
||||||
|
"def": false
|
||||||
|
},
|
||||||
|
"native": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "entities",
|
||||||
|
"type": "channel",
|
||||||
|
"common": {
|
||||||
|
"name": "HASS entities"
|
||||||
|
},
|
||||||
|
"native": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
+233
@@ -0,0 +1,233 @@
|
|||||||
|
var util = require('util');
|
||||||
|
var EventEmitter = require('events').EventEmitter;
|
||||||
|
var WebSocket = require('ws');
|
||||||
|
|
||||||
|
function HASS(options, log) {
|
||||||
|
if (!(this instanceof HASS)) return new HASS(options);
|
||||||
|
|
||||||
|
options = options || {};
|
||||||
|
options.host = options.host || '127.0.0.1';
|
||||||
|
options.port = parseInt(options.port, 10) || 8123;
|
||||||
|
|
||||||
|
var ERRORS = {
|
||||||
|
1: 'ERR_CANNOT_CONNECT',
|
||||||
|
2: 'ERR_INVALID_AUTH',
|
||||||
|
3: 'ERR_CONNECTION_LOST'
|
||||||
|
};
|
||||||
|
|
||||||
|
this.socket = null;
|
||||||
|
var that = this;
|
||||||
|
var currentId = 1;
|
||||||
|
var requests = {};
|
||||||
|
var connected;
|
||||||
|
var connectTimeout = null;
|
||||||
|
|
||||||
|
function subscribeEvents(socket, callback) {
|
||||||
|
if (socket && typeof socket.send === 'function') {
|
||||||
|
var id = currentId++;
|
||||||
|
requests[id] = {type: 'subscribe_events', ts: Date.now(), cb: callback};
|
||||||
|
|
||||||
|
socket.send(JSON.stringify({
|
||||||
|
id: id,
|
||||||
|
type: 'subscribe_events'
|
||||||
|
/*event_type: 'state_changed'*/
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
callback && callback('not connected');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getConfig(socket, callback) {
|
||||||
|
if (socket && typeof socket.send === 'function') {
|
||||||
|
var id = currentId++;
|
||||||
|
requests[id] = {type: 'get_config', cb: callback, ts: Date.now()};
|
||||||
|
socket.send(JSON.stringify({
|
||||||
|
id: id,
|
||||||
|
type: 'get_config'
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
callback && callback('not connected');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStates(socket, callback) {
|
||||||
|
if (socket && typeof socket.send === 'function') {
|
||||||
|
var id = currentId++;
|
||||||
|
requests[id] = {type: 'get_states', cb: callback, ts: Date.now()};
|
||||||
|
socket.send(JSON.stringify({
|
||||||
|
id: id,
|
||||||
|
type: 'get_states'
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
callback && callback('not connected');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getServices(socket, callback) {
|
||||||
|
if (socket && typeof socket.send === 'function') {
|
||||||
|
var id = currentId++;
|
||||||
|
requests[id] = {type: 'get_services', cb: callback, ts: Date.now()};
|
||||||
|
socket.send(JSON.stringify({
|
||||||
|
id: id,
|
||||||
|
type: 'get_services'
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
callback && callback('not connected');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function callService(socket, service, domain, serviceData, callback) {
|
||||||
|
if (socket && typeof socket.send === 'function') {
|
||||||
|
var id = currentId++;
|
||||||
|
requests[id] = {type: 'call_service', cb: callback, ts: Date.now()};
|
||||||
|
socket.send(JSON.stringify({
|
||||||
|
id: id,
|
||||||
|
type: 'call_service',
|
||||||
|
domain: domain || 'light',
|
||||||
|
service: service,
|
||||||
|
service_data: serviceData
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
callback && callback('not connected');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendAuth(socket, pass) {
|
||||||
|
if (socket && typeof socket.send === 'function') {
|
||||||
|
socket.send(JSON.stringify({
|
||||||
|
type: 'auth',
|
||||||
|
api_password: pass
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initSocket(socket) {
|
||||||
|
socket.on('message', function (msg) {
|
||||||
|
//log.debug(msg);
|
||||||
|
|
||||||
|
var response = JSON.parse(msg);
|
||||||
|
if (response.type === 'auth_required') {
|
||||||
|
if (!options.password) {
|
||||||
|
that.emit('error', 'Password required. Connection closed');
|
||||||
|
socket.terminate();
|
||||||
|
} else {
|
||||||
|
setTimeout(function () {
|
||||||
|
sendAuth(socket, options.password);
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
if (response.type === 'auth_ok') {
|
||||||
|
setImmediate(function () {
|
||||||
|
subscribeEvents(socket, function (err) {
|
||||||
|
if (!err) {
|
||||||
|
connected = true;
|
||||||
|
that.emit('connected');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else if (response.id === undefined) {
|
||||||
|
log.error('Invalid answer: ' + msg);
|
||||||
|
} else {
|
||||||
|
if (response.type === 'result' && requests[response.id]) {
|
||||||
|
log.debug('got answer for ' + requests[response.id].type);
|
||||||
|
if (typeof requests[response.id].cb === 'function') {
|
||||||
|
requests[response.id].cb(!response.success, response.result);
|
||||||
|
delete requests[response.id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('error', function (err) {
|
||||||
|
socket = null;
|
||||||
|
if (err && err.message.indexOf('RSV2 and RSV3 must be clear') !== -1) {
|
||||||
|
// ignore deflate error
|
||||||
|
} else {
|
||||||
|
log.error(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
socket.on('open', function () {
|
||||||
|
if (!connected) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
socket.on('close', function () {
|
||||||
|
that.socket = null;
|
||||||
|
if (connected) {
|
||||||
|
connected = false;
|
||||||
|
that.emit('disconnected');
|
||||||
|
|
||||||
|
}
|
||||||
|
if (!connectTimeout) {
|
||||||
|
setTimeout(function () {
|
||||||
|
connectTimeout = null;
|
||||||
|
that.connect();
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isConnected = function () {
|
||||||
|
return connected;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getConfig = function (callback) {
|
||||||
|
if (!connected) {
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
callback('not connected');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
getConfig(this.socket, callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getStates = function (callback) {
|
||||||
|
if (!connected) {
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
callback('not connected');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
getStates(this.socket, callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getServices = function (callback) {
|
||||||
|
if (!connected) {
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
callback('not connected');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
getServices(this.socket, callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.callService = function (service, domain, serviceData, callback) {
|
||||||
|
if (!connected) {
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
callback('not connected');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
callService(this.socket, service, domain, serviceData, callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.connect = function () {
|
||||||
|
if (connectTimeout) {
|
||||||
|
clearTimeout(connectTimeout);
|
||||||
|
connectTimeout = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.socket = new WebSocket('ws' + (options.secure ? 's' : '') + '://' + options.host + ':' + options.port + '/api/websocket', {
|
||||||
|
perMessageDeflate: false
|
||||||
|
});
|
||||||
|
|
||||||
|
initSocket(this.socket);
|
||||||
|
};
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// extend the EventEmitter class using our class
|
||||||
|
util.inherits(HASS, EventEmitter);
|
||||||
|
|
||||||
|
module.exports = HASS;
|
||||||
@@ -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,783 @@
|
|||||||
|
/* jshint -W097 */
|
||||||
|
/* jshint strict:false */
|
||||||
|
/* jslint node: true */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var utils = require(__dirname + '/lib/utils');
|
||||||
|
var HASS = require(__dirname + '/lib/hass');
|
||||||
|
|
||||||
|
var adapter = utils.Adapter('hass');
|
||||||
|
|
||||||
|
var connected = false;
|
||||||
|
var hass;
|
||||||
|
var hassObjects = {};
|
||||||
|
|
||||||
|
// is called if a subscribed state changes
|
||||||
|
adapter.on('stateChange', function (id, state) {
|
||||||
|
// you can use the ack flag to detect if it is status (true) or command (false)
|
||||||
|
if (state && !state.ack) {
|
||||||
|
if (!connected) {
|
||||||
|
adapter.log.warn('Cannot send command to "' + id + '", because not connected');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/*if (id === adapter.namespace + '.' + '.info.resync') {
|
||||||
|
queue.push({command: 'resync'});
|
||||||
|
processQueue();
|
||||||
|
} else */
|
||||||
|
if (hassObjects[id]) {
|
||||||
|
if (!hassObjects[id].common.write) {
|
||||||
|
adapter.log.warn('Object ' + id + ' is not writable!');
|
||||||
|
} else {
|
||||||
|
var serviceData = {};
|
||||||
|
var fields = hassObjects[id].native.fields;
|
||||||
|
|
||||||
|
for (var field in fields) {
|
||||||
|
if (!fields.hasOwnProperty(field)) continue;
|
||||||
|
|
||||||
|
if (fields === 'entity_id') {
|
||||||
|
serviceData.entity_id = hassObjects[id].native.entity_id
|
||||||
|
} else {
|
||||||
|
serviceData.entity_id = state.val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hass.callService(hassObjects[id].native.type, undefined, serviceData, function (err) {
|
||||||
|
if (err) {
|
||||||
|
adapter.log.error('Cannot control ' + id + ': ' + err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Some message was sent to adapter instance over message box. Used by email, pushover, text2speech, ...
|
||||||
|
adapter.on('message', function (obj) {
|
||||||
|
if (typeof obj === 'object' && obj.message) {
|
||||||
|
if (obj.command === 'send') {
|
||||||
|
// e.g. send email or pushover or whatever
|
||||||
|
console.log('send command');
|
||||||
|
|
||||||
|
// Send response in callback if required
|
||||||
|
if (obj.callback) adapter.sendTo(obj.from, obj.command, 'Message received', obj.callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// is called when databases are connected and adapter received configuration.
|
||||||
|
// start here!
|
||||||
|
adapter.on('ready', main);
|
||||||
|
|
||||||
|
function getUnit(name) {
|
||||||
|
name = name.toLowerCase();
|
||||||
|
if (name.indexOf('temperature') !== -1) {
|
||||||
|
return '°C';
|
||||||
|
} else if (name.indexOf('humidity') !== -1) {
|
||||||
|
return '%';
|
||||||
|
} else if (name.indexOf('pressure') !== -1) {
|
||||||
|
return 'hPa';
|
||||||
|
} else if (name.indexOf('degrees') !== -1) {
|
||||||
|
return '°';
|
||||||
|
} else if (name.indexOf('speed') !== -1) {
|
||||||
|
return 'kmh';
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncStates(states, cb) {
|
||||||
|
if (!states || !states.length) {
|
||||||
|
cb();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var state = states.shift();
|
||||||
|
var id = state.id;
|
||||||
|
delete state.id;
|
||||||
|
|
||||||
|
adapter.setForeignState(id, state, function (err) {
|
||||||
|
if (err) adapter.log.error(err);
|
||||||
|
setImmediate(syncStates, states, cb);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncObjects(objects, cb) {
|
||||||
|
if (!objects || !objects.length) {
|
||||||
|
cb();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var obj = objects.shift();
|
||||||
|
hassObjects[obj._id] = obj;
|
||||||
|
|
||||||
|
var start = Date.now();
|
||||||
|
|
||||||
|
adapter.getForeignObject(obj._id, function (err, oldObj) {
|
||||||
|
console.log('getForeignObject: ' + (Date.now() - start));
|
||||||
|
|
||||||
|
if (err) adapter.log.error(err);
|
||||||
|
|
||||||
|
if (!oldObj) {
|
||||||
|
adapter.log.debug('Create "' + obj._id + '"');
|
||||||
|
hassObjects[obj._id] = obj;
|
||||||
|
start = Date.now();
|
||||||
|
adapter.setForeignObject(obj._id, obj, function (err) {
|
||||||
|
console.log('setForeignObject: ' + (Date.now() - start));
|
||||||
|
if (err) adapter.log.error(err);
|
||||||
|
|
||||||
|
setImmediate(syncObjects, objects, cb);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
hassObjects[obj._id] = oldObj;
|
||||||
|
if (JSON.stringify(obj.native) !== JSON.stringify(oldObj.native)) {
|
||||||
|
oldObj.native = obj.native;
|
||||||
|
|
||||||
|
adapter.log.debug('Update "' + obj._id + '"');
|
||||||
|
start = Date.now();
|
||||||
|
adapter.setForeignObject(obj._id, oldObj, function (err) {
|
||||||
|
console.log('ssetForeignObject: ' + (Date.now() - start));
|
||||||
|
if (err) adapter.log.error(err);
|
||||||
|
setImmediate(syncObjects, objects, cb);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setImmediate(syncObjects, objects, cb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncRoom(room, members, cb) {
|
||||||
|
adapter.getForeignObject('enum.rooms.' + room, function (err, obj) {
|
||||||
|
if (!obj) {
|
||||||
|
obj = {
|
||||||
|
_id: 'enum.rooms.' + room,
|
||||||
|
type: 'enum',
|
||||||
|
common: {
|
||||||
|
name: room,
|
||||||
|
members: members
|
||||||
|
},
|
||||||
|
native: {}
|
||||||
|
};
|
||||||
|
adapter.log.debug('Update "' + obj._id + '"');
|
||||||
|
adapter.setForeignObject(obj._id, obj, function (err) {
|
||||||
|
if (err) adapter.log.error(err);
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
obj.common = obj.common || {};
|
||||||
|
obj.common.members = obj.common.members || [];
|
||||||
|
var changed = false;
|
||||||
|
for (var m = 0; m < members.length; m++) {
|
||||||
|
if (obj.common.members.indexOf(members[m]) === -1) {
|
||||||
|
changed = true;
|
||||||
|
obj.common.members.push(members[m]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
adapter.log.debug('Update "' + obj._id + '"');
|
||||||
|
adapter.setForeignObject(obj._id, obj, function (err) {
|
||||||
|
if (err) adapter.log.error(err);
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncRooms(rooms, cb) {
|
||||||
|
for (var r in rooms) {
|
||||||
|
if (!rooms.hasOwnProperty(r)) continue;
|
||||||
|
if (rooms[r]) {
|
||||||
|
syncRoom(r, rooms[r], function () {
|
||||||
|
setTimeout(syncRooms, 0, rooms, cb);
|
||||||
|
});
|
||||||
|
rooms[r] = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cb) cb();
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------<<<
|
||||||
|
function parseObjects(objs, cb) {
|
||||||
|
var rooms = {};
|
||||||
|
var objects = [];
|
||||||
|
var states = [];
|
||||||
|
var id;
|
||||||
|
var obj;
|
||||||
|
var name;
|
||||||
|
var ignoreStates = ['getConfig', 'getRegRaw', 'regBulk', 'regSet', 'deviceMsg', 'CommandAccepted'];
|
||||||
|
|
||||||
|
for (var i = 0; i < objs.length; i++) {
|
||||||
|
try {
|
||||||
|
name = objs[i].Name.replace(/\./g, '_');
|
||||||
|
|
||||||
|
if (objs[i].Attributes && objs[i].Attributes.room === 'hidden') continue;
|
||||||
|
|
||||||
|
id = adapter.namespace + '.' + name;
|
||||||
|
|
||||||
|
objects.push({
|
||||||
|
_id: id,
|
||||||
|
type: 'channel',
|
||||||
|
common: {
|
||||||
|
name: objs[i].Name
|
||||||
|
},
|
||||||
|
native: objs[i]
|
||||||
|
});
|
||||||
|
|
||||||
|
if (objs[i].Attributes && objs[i].Attributes.room) {
|
||||||
|
var rrr = objs[i].Attributes.room.split(',');
|
||||||
|
for (var r = 0; r < rrr.length; r++) {
|
||||||
|
rrr[r] = rrr[r].trim();
|
||||||
|
rooms[rrr[r]] = rooms[rrr[r]] || [];
|
||||||
|
rooms[rrr[r]].push(adapter.namespace + '.' + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var isOn = false;
|
||||||
|
var isOff = false;
|
||||||
|
var setStates = {};
|
||||||
|
|
||||||
|
if (objs[i].PossibleSets) {
|
||||||
|
var attrs = objs[i].PossibleSets.split(' ');
|
||||||
|
for (var a = 0; a < attrs.length; a++) {
|
||||||
|
if (!attrs[a]) continue;
|
||||||
|
var parts = attrs[a].split(':');
|
||||||
|
|
||||||
|
// ignore some useless "sets"
|
||||||
|
if (ignoreStates.indexOf(parts[0]) !== -1) continue;
|
||||||
|
|
||||||
|
var stateName = parts[0].replace(/\./g, '_');
|
||||||
|
id = adapter.namespace + '.' + name + '.' + stateName;
|
||||||
|
|
||||||
|
|
||||||
|
if (parts[0] === 'off') isOff = true;
|
||||||
|
if (parts[0] === 'on') isOn = true;
|
||||||
|
|
||||||
|
obj = {
|
||||||
|
_id: id,
|
||||||
|
type: 'state',
|
||||||
|
common: {
|
||||||
|
name: objs[i].Name + ' ' + parts[0],
|
||||||
|
read: false,
|
||||||
|
write: true
|
||||||
|
},
|
||||||
|
native: {
|
||||||
|
Name: objs[i].Name,
|
||||||
|
Attribute: parts[0]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (parts[1]) {
|
||||||
|
var _states = parts[1].split(',');
|
||||||
|
// adapter.log.info('LausiD "' + obj._id + ' : ' + _states + '"');
|
||||||
|
// obj.common.states = JSON.stringify(_states);
|
||||||
|
obj.common.states = '';
|
||||||
|
|
||||||
|
if (parseFloat(_states[0]) == _states[0]) {
|
||||||
|
obj.common.type = 'number';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.common.type = obj.common.type || 'string';
|
||||||
|
obj.common.role = 'command';
|
||||||
|
// edit 08.03.17 LausiD
|
||||||
|
// detect pct,Volume,GroupVolume,brightness
|
||||||
|
if (parts[0] === 'pct' || parts[0] === 'Volume' || parts[0] === 'GroupVolume' || parts[0] === 'brightness') {
|
||||||
|
// obj.common.write = true;
|
||||||
|
// obj.common.unit= '%';
|
||||||
|
obj.common.type = 'number';
|
||||||
|
obj.common.min = '0';
|
||||||
|
obj.common.max = '100';
|
||||||
|
obj.common.role = 'command.dim.100';
|
||||||
|
}
|
||||||
|
// detect bri,sat
|
||||||
|
if (parts[0] === 'bri' || parts[0] === 'sat') {
|
||||||
|
// obj.common.write = true;
|
||||||
|
// obj.common.unit = '%';
|
||||||
|
obj.common.type = 'number';
|
||||||
|
obj.common.min = '0';
|
||||||
|
obj.common.max = '254';
|
||||||
|
obj.common.role = 'command.dim.254';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (parts[0].indexOf('RGB') !== -1) {
|
||||||
|
obj.common.role = 'light.color.rgb';
|
||||||
|
obj.native.rgb = true;
|
||||||
|
}
|
||||||
|
if (parts[0].indexOf('HSV') !== -1) {
|
||||||
|
obj.common.role = 'light.color.hsv';
|
||||||
|
obj.native.hsv = true;
|
||||||
|
}
|
||||||
|
objects.push(obj);
|
||||||
|
setStates[stateName] = obj;
|
||||||
|
//console.log(' ' + obj._id + ': ' + (parts[1] || ''));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (objs[i].Attributes[attr]) {
|
||||||
|
// for (var attr in objs[i].Attributes) {
|
||||||
|
adapter.log.info('LausiD '+ attr);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* if (!objs[i].Readings.hasOwnProperty(attr)) continue;
|
||||||
|
// ignore some useless states
|
||||||
|
if (ignoreStates.indexOf(attr) !== -1) continue;
|
||||||
|
|
||||||
|
var stateName = attr.replace(/\./g, '_');
|
||||||
|
id = adapter.namespace + '.' + name + '.' + stateName;
|
||||||
|
//adapter.log.info('LausiD '+ id);
|
||||||
|
var combined = false;
|
||||||
|
if (setStates[stateName]) {
|
||||||
|
combined = true;
|
||||||
|
obj = setStates[stateName];
|
||||||
|
obj.common.read = true;
|
||||||
|
obj.common.unit = getUnit(attr);
|
||||||
|
} else {
|
||||||
|
obj = {
|
||||||
|
_id: id,
|
||||||
|
type: 'state',
|
||||||
|
common: {
|
||||||
|
name: objs[i].Name + ' ' + attr,
|
||||||
|
read: true,
|
||||||
|
write: false,
|
||||||
|
unit: getUnit(attr)
|
||||||
|
},
|
||||||
|
native: {
|
||||||
|
Name: objs[i].Name,
|
||||||
|
Attribute: attr
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} */
|
||||||
|
|
||||||
|
|
||||||
|
if (objs[i].Readings) {
|
||||||
|
for (var attr in objs[i].Readings) {
|
||||||
|
if (!objs[i].Readings.hasOwnProperty(attr)) continue;
|
||||||
|
// ignore some useless states
|
||||||
|
if (ignoreStates.indexOf(attr) !== -1) continue;
|
||||||
|
|
||||||
|
var stateName = attr.replace(/\./g, '_');
|
||||||
|
id = adapter.namespace + '.' + name + '.' + stateName;
|
||||||
|
var combined = false;
|
||||||
|
if (setStates[stateName]) {
|
||||||
|
combined = true;
|
||||||
|
obj = setStates[stateName];
|
||||||
|
obj.common.read = true;
|
||||||
|
obj.common.unit = getUnit(attr);
|
||||||
|
} else {
|
||||||
|
obj = {
|
||||||
|
_id: id,
|
||||||
|
type: 'state',
|
||||||
|
common: {
|
||||||
|
name: objs[i].Name + ' ' + attr,
|
||||||
|
read: true,
|
||||||
|
write: false,
|
||||||
|
unit: getUnit(attr)
|
||||||
|
},
|
||||||
|
native: {
|
||||||
|
Name: objs[i].Name,
|
||||||
|
Attribute: attr
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (objs[i].Readings[attr]) {
|
||||||
|
var val = convertFhemValue(objs[i].Readings[attr].Value);
|
||||||
|
obj.common.type = obj.common.type || typeof val;
|
||||||
|
obj.common.role = obj.common.role || 'value';
|
||||||
|
|
||||||
|
states.push({
|
||||||
|
id: obj._id,
|
||||||
|
val: val,
|
||||||
|
ts: objs[i].Readings[attr].Time ? new Date(objs[i].Readings[attr].Time).getTime() : new Date().getTime(),
|
||||||
|
ack: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// detect pct
|
||||||
|
if (attr === 'pct' || attr === 'Volume' || attr === 'GroupVolume' || attr === 'brightness') {
|
||||||
|
obj.common.unit = '%';
|
||||||
|
}
|
||||||
|
// detect bri,sat
|
||||||
|
if (attr === 'bri' || attr === 'sat') {
|
||||||
|
obj.common.unit = '%';
|
||||||
|
}
|
||||||
|
|
||||||
|
// detect state
|
||||||
|
if (attr === 'state') {
|
||||||
|
obj.common.write = true;
|
||||||
|
obj.native.onoff = true;
|
||||||
|
obj.common.role = 'switch';
|
||||||
|
}
|
||||||
|
// detect on/off state
|
||||||
|
if (isOff && isOn && attr === 'state') {
|
||||||
|
obj.common.write = true;
|
||||||
|
obj.native.onoff = true;
|
||||||
|
obj.common.role = 'switch';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!combined) objects.push(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete objs[i].Readings;
|
||||||
|
}
|
||||||
|
setStates = null;
|
||||||
|
|
||||||
|
/*id = adapter.namespace + '.' + name + '.lastError';
|
||||||
|
obj = {
|
||||||
|
_id: id,
|
||||||
|
type: 'state',
|
||||||
|
common: {
|
||||||
|
name: objs[i].Name + ' lastError',
|
||||||
|
read: true,
|
||||||
|
write: false,
|
||||||
|
def: '',
|
||||||
|
type: 'string',
|
||||||
|
role: 'error'
|
||||||
|
},
|
||||||
|
native: objs[i]
|
||||||
|
};
|
||||||
|
objects.push(obj);*/
|
||||||
|
|
||||||
|
/*id = adapter.namespace + '.' + objs[i].Name + '.validity';
|
||||||
|
obj = {
|
||||||
|
_id: id,
|
||||||
|
type: 'state',
|
||||||
|
common: {
|
||||||
|
name: objs[i].Name + ' validity',
|
||||||
|
read: true,
|
||||||
|
write: false,
|
||||||
|
def: '',
|
||||||
|
type: 'string',
|
||||||
|
role: 'state.quality'
|
||||||
|
},
|
||||||
|
native: objs[i]
|
||||||
|
};
|
||||||
|
objects.push(obj);*/
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
adapter.log.error('Cannot process object: ' + JSON.stringify(objs[i]));
|
||||||
|
adapter.log.error('Cannot process object: ' + err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
syncObjects(objects, function () {
|
||||||
|
syncRooms(rooms, function () {
|
||||||
|
syncStates(states, cb);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function readValue(id, cb) {
|
||||||
|
telnetOut.send('get ' + hassObjects[id].native.Name + ' ' + hassObjects[id].native.Attribute, function (err, result) {
|
||||||
|
if (err) adapter.log.error('readValue: ' + err);
|
||||||
|
// MeinWetter city => Berlin
|
||||||
|
if (result) {
|
||||||
|
result = convertFhemValue(result.substring(hassObjects[id].native.Name.length + hassObjects[id].native.Attribute + 5));
|
||||||
|
if (result !== '') {
|
||||||
|
adapter.setForeignState(id, result, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cb) cb();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeValue(id, val, cb) {
|
||||||
|
var cmd;
|
||||||
|
var val_org = val;
|
||||||
|
if (val === undefined || val === null) val = '';
|
||||||
|
// edit LausiD 05.03.17
|
||||||
|
// May be RGB
|
||||||
|
if (hassObjects[id].native.Attribute === 'rgb') val = val.substring(1);
|
||||||
|
|
||||||
|
// if (typeof val === 'string' && val[0] === '#' && val.length > 3) val = val.substring(1);
|
||||||
|
// if (hassObjects[id].native.rgb) {
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (hassObjects[id].native.Attribute === 'state') {
|
||||||
|
if (val === '1' || val === 1 || val === 'on' || val === 'true' || val === true) val = 'on';
|
||||||
|
if (val === '0' || val === 0 || val === 'off' || val === 'false' || val === false) val = 'off';
|
||||||
|
cmd = 'set ' + hassObjects[id].native.Name + ' ' + val;
|
||||||
|
// adapter.log.info(adapter.namespace + '.' + hassObjects[id].native.Name + '.' + hassObjects[id].native.Attribute + '.' + val_org + ' ==> ' + cmd);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cmd = 'set ' + hassObjects[id].native.Name + ' ' + hassObjects[id].native.Attribute + ' ' + val;
|
||||||
|
}
|
||||||
|
adapter.log.info(adapter.namespace + '.' + hassObjects[id].native.Name + '.' + hassObjects[id].native.Attribute + '.' + val_org + ' ==> writeFHEM: ' + cmd);
|
||||||
|
// edit end LausiD 05.03.17
|
||||||
|
|
||||||
|
telnetOut.send(cmd, function (err, result) {
|
||||||
|
if (err) adapter.log.error('writeValue: ' + err);
|
||||||
|
if (cb) cb();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var knownAttributes = {
|
||||||
|
azimuth: {write: false, read: true, unit: '°'},
|
||||||
|
elevation: {write: false, read: true, unit: '°'}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var ERRORS = {
|
||||||
|
1: 'ERR_CANNOT_CONNECT',
|
||||||
|
2: 'ERR_INVALID_AUTH',
|
||||||
|
3: 'ERR_CONNECTION_LOST'
|
||||||
|
};
|
||||||
|
var mapTypes = {
|
||||||
|
'string': 'string',
|
||||||
|
'number': 'number',
|
||||||
|
'object': 'mixed',
|
||||||
|
'boolean': 'boolean'
|
||||||
|
};
|
||||||
|
var skipServices = [
|
||||||
|
'persistent_notification'
|
||||||
|
];
|
||||||
|
|
||||||
|
function parseStates(entities, services, callback) {
|
||||||
|
var objs = [];
|
||||||
|
var states = [];
|
||||||
|
var obj;
|
||||||
|
var channel;
|
||||||
|
for (var e = 0; e < entities.length; e++) {
|
||||||
|
var entity = entities[e];
|
||||||
|
if (!entity) continue;
|
||||||
|
|
||||||
|
var name = entity.attributes && entity.attributes.friendly_name ? entity.attributes.friendly_name : entity.entity_id;
|
||||||
|
var desc = entity.attributes && entity.attributes.attribution ? entity.attributes.attribution : undefined;
|
||||||
|
|
||||||
|
channel = {
|
||||||
|
_id: adapter.namespace + '.entities.' + entity.entity_id,
|
||||||
|
common: {
|
||||||
|
name: name
|
||||||
|
},
|
||||||
|
type: 'channel',
|
||||||
|
native: {
|
||||||
|
entity_id: entity.entity_id
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (desc) channel.common.desc = desc;
|
||||||
|
objs.push(channel);
|
||||||
|
|
||||||
|
var lc = entity.last_changed ? new Date(entity.last_changed).getTime() : undefined;
|
||||||
|
var ts = entity.last_updated ? new Date(entity.last_updated).getTime() : undefined;
|
||||||
|
|
||||||
|
if (entity.state !== undefined) {
|
||||||
|
obj = {
|
||||||
|
_id: adapter.namespace + '.entities.' + entity.entity_id + '.state',
|
||||||
|
type: 'state',
|
||||||
|
common: {
|
||||||
|
name: name + ' STATE',
|
||||||
|
read: true,
|
||||||
|
write: false
|
||||||
|
},
|
||||||
|
native: {
|
||||||
|
entity_id: entity.entity_id
|
||||||
|
}
|
||||||
|
};
|
||||||
|
objs.push(obj);
|
||||||
|
states.push({id: obj._id, lc: lc, ts: ts, val: entity.state, ack: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entity.attributes) {
|
||||||
|
for (var attr in entity.attributes) {
|
||||||
|
if (entity.attributes.hasOwnProperty(attr)) {
|
||||||
|
if (attr === 'friendly_name') continue;
|
||||||
|
|
||||||
|
var common;
|
||||||
|
if (knownAttributes[attr]) {
|
||||||
|
common = Object.assign({}, knownAttributes[attr]);
|
||||||
|
} else {
|
||||||
|
common = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = {
|
||||||
|
_id: adapter.namespace + '.entities.' + entity.entity_id + '.' + attr,
|
||||||
|
type: 'state',
|
||||||
|
common: common,
|
||||||
|
native: {
|
||||||
|
entity_id: entity.entity_id,
|
||||||
|
attr: attr
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (!common.name) {
|
||||||
|
common.name = name + ' ' + attr.replace(/_/g, ' ');
|
||||||
|
}
|
||||||
|
if (common.read === undefined) {
|
||||||
|
common.read = true;
|
||||||
|
}
|
||||||
|
if (common.write === undefined) {
|
||||||
|
common.write = false;
|
||||||
|
}
|
||||||
|
if (common.type === undefined) {
|
||||||
|
common.type = mapTypes[typeof entity.attributes[attr]];
|
||||||
|
}
|
||||||
|
|
||||||
|
objs.push(obj);
|
||||||
|
states.push({id: obj._id, lc: lc, ts: ts, val: entity.attributes[attr], ack: true});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var serviceType = entity.entity_id.split('.')[0];
|
||||||
|
|
||||||
|
if (services[serviceType] && skipServices.indexOf(serviceType) === -1) {
|
||||||
|
var service = services[serviceType];
|
||||||
|
for (var s in service) {
|
||||||
|
if (service.hasOwnProperty(s)) {
|
||||||
|
obj = {
|
||||||
|
_id: adapter.namespace + '.entities.' + entity.entity_id + '.' + s,
|
||||||
|
type: 'state',
|
||||||
|
common: {
|
||||||
|
desc: service.description,
|
||||||
|
read: false,
|
||||||
|
write: true
|
||||||
|
},
|
||||||
|
native: {
|
||||||
|
field: service.fields,
|
||||||
|
entity_id: entity.entity_id,
|
||||||
|
attr: s,
|
||||||
|
type: serviceType
|
||||||
|
}
|
||||||
|
};
|
||||||
|
objs.push(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
syncObjects(objs, function () {
|
||||||
|
syncStates(states, callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
adapter.config.host = adapter.config.host || '127.0.0.1';
|
||||||
|
adapter.config.port = parseInt(adapter.config.port, 10) || 8123;
|
||||||
|
|
||||||
|
adapter.setState('info.connection', false, true);
|
||||||
|
|
||||||
|
// in this template all states changes inside the adapters namespace are subscribed
|
||||||
|
adapter.subscribeStates('*');
|
||||||
|
|
||||||
|
hass = new HASS(adapter.config, adapter.log);
|
||||||
|
|
||||||
|
hass.on('error', function (err) {
|
||||||
|
adapter.log.error(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
hass.on('connected', function () {
|
||||||
|
if (!connected) {
|
||||||
|
adapter.log.debug('Connected');
|
||||||
|
connected = true;
|
||||||
|
adapter.setState('info.connection', true, true);
|
||||||
|
|
||||||
|
hass.getConfig(function (err, config) {
|
||||||
|
if (err) {
|
||||||
|
adapter.log.error('Cannot read config: ' + err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//adapter.log.debug(JSON.stringify(config));
|
||||||
|
setTimeout(function () {
|
||||||
|
hass.getStates(function (err, states) {
|
||||||
|
if (err) {
|
||||||
|
return adapter.log.error('Cannot read states: ' + err);
|
||||||
|
}
|
||||||
|
//adapter.log.debug(JSON.stringify(states));
|
||||||
|
setTimeout(function () {
|
||||||
|
hass.getServices(function (err, services) {
|
||||||
|
if (err) {
|
||||||
|
adapter.log.error('Cannot read states: ' + err);
|
||||||
|
} else {
|
||||||
|
//adapter.log.debug(JSON.stringify(services));
|
||||||
|
parseStates(states, services, function () {
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
hass.on('disconnected', function () {
|
||||||
|
if (connected) {
|
||||||
|
adapter.log.debug('Disconnected');
|
||||||
|
connected = false;
|
||||||
|
adapter.setState('info.connection', false, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
hass.connect();
|
||||||
|
|
||||||
|
/*const ssocket = new WebSocket('ws://' + adapter.config.host + ':' + adapter.config.port + '/api/websocket');
|
||||||
|
ssocket.addEventListener('message', function (msg) {
|
||||||
|
console.log(JSON.stringify(msg.a));
|
||||||
|
});
|
||||||
|
ssocket.addEventListener('close', function () {
|
||||||
|
console.log('Closed');
|
||||||
|
});
|
||||||
|
ssocket.addEventListener('error', function (err) {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
hass.createConnection('ws://' + adapter.config.host + ':' + adapter.config.port + '/api/websocket').then(function (conn) {
|
||||||
|
if (!connected) {
|
||||||
|
adapter.log.debug('Connected');
|
||||||
|
connected = true;
|
||||||
|
adapter.setState('info.connection', true, true);
|
||||||
|
}
|
||||||
|
socket = conn;
|
||||||
|
hass.subscribeEntities(conn, function (entities) {
|
||||||
|
console.log('New entities!', entities);
|
||||||
|
var attrs = Object.keys(entities);
|
||||||
|
for (var a = 0; a < attrs.length; a++) {
|
||||||
|
console.log(attrs[a] + ': ' + entities[attrs[a]].state);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
hass.subscribeConfig(conn, function (config) {
|
||||||
|
console.log('New config!', config)
|
||||||
|
});
|
||||||
|
conn.addEventListener('ready', function () {
|
||||||
|
if (!connected) {
|
||||||
|
adapter.log.debug('Connected');
|
||||||
|
connected = true;
|
||||||
|
adapter.setState('info.connection', true, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
conn.addEventListener('disconnected', function () {
|
||||||
|
if (connected) {
|
||||||
|
adapter.log.debug('Disconnected');
|
||||||
|
connected = false;
|
||||||
|
adapter.setState('info.connection', false, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
conn.getStates(function (states) {
|
||||||
|
console.log('getStates: ', states)
|
||||||
|
});
|
||||||
|
|
||||||
|
}).catch(function (err) {
|
||||||
|
if (err === 2) {
|
||||||
|
adapter.log.error('Invalid password!.');
|
||||||
|
} else {
|
||||||
|
adapter.log.error('Connection failed with code', ERRORS[err] || err);
|
||||||
|
}
|
||||||
|
socket = null;
|
||||||
|
|
||||||
|
if (connected) {
|
||||||
|
adapter.log.debug('Disconnected');
|
||||||
|
connected = false;
|
||||||
|
adapter.setState('info.connection', false, true);
|
||||||
|
}
|
||||||
|
});*/
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"name": "iobroker.hass",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Home Assistant",
|
||||||
|
"author": {
|
||||||
|
"name": "bluefox",
|
||||||
|
"email": "dogafox@gmail.com"
|
||||||
|
},
|
||||||
|
"contributors": [
|
||||||
|
{
|
||||||
|
"name": "bluefox",
|
||||||
|
"email": "dogafox@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"homepage": "https://github.com/ioBroker/ioBroker.hass",
|
||||||
|
"license": "MIT",
|
||||||
|
"keywords": [
|
||||||
|
"ioBroker",
|
||||||
|
"hass",
|
||||||
|
"REST API",
|
||||||
|
"IoT"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/ioBroker/ioBroker.hass"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"home-assistant-js-websocket": "^1.1.4",
|
||||||
|
"websocket": "^1.0.25",
|
||||||
|
"ws": "^5.0.0"
|
||||||
|
},
|
||||||
|
"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://github.com/ioBroker/ioBroker.hass/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://github.com/' + 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/iobroker-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,125 @@
|
|||||||
|
/* 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 connected', function (done) {
|
||||||
|
this.timeout(60000);
|
||||||
|
setTimeout(function () {
|
||||||
|
states.getState('hass.0.info.connection', function (err, state) {
|
||||||
|
if (err) console.error(err);
|
||||||
|
expect(err).to.be.not.ok;
|
||||||
|
expect(state).to.be.ok;
|
||||||
|
expect(state.val).to.be.true;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}, 5000);
|
||||||
|
});
|
||||||
|
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user