(bluefox) initial commit

This commit is contained in:
GermanBluefox
2018-03-11 01:38:23 +01:00
commit d586cb4088
19 changed files with 2783 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
node_modules
.idea
tmp
admin/i18n/flat.txt
admin/i18n/*/flat.txt
iob_npm.done
package-lock.json
+9
View File
@@ -0,0 +1,9 @@
gulpfile.js
tasks
tmp
test
.travis.yml
appveyor.yml
admin/i18n
iob_npm.done
package-lock.json
+31
View File
@@ -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
+21
View File
@@ -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.
+38
View File
@@ -0,0 +1,38 @@
![Logo](admin/hass.png)
# ioBroker.hass
=================
[![NPM version](http://img.shields.io/npm/v/iobroker.hass.svg)](https://www.npmjs.com/package/iobroker.hass)
[![Downloads](https://img.shields.io/npm/dm/iobroker.hass.svg)](https://www.npmjs.com/package/iobroker.hass)
[![NPM](https://nodei.co/npm/iobroker.hass.png?downloads=true)](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.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

+76
View File
@@ -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>
+76
View File
@@ -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>
+19
View File
@@ -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>'"
}
};
+28
View File
@@ -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
View File
@@ -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']);
+84
View File
@@ -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
View File
@@ -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;
+64
View File
@@ -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;
+783
View File
@@ -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);
}
});*/
}
+45
View File
@@ -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"
}
+696
View File
@@ -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;
}
+125
View File
@@ -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();
});
});
});
+47
View File
@@ -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();
});
});