Initial commit

master
zhongjin 6 years ago
commit 5a308ad8ba

7
.gitignore vendored

@ -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

@ -0,0 +1,23 @@
os:
- linux
- osx
language: node_js
node_js:
- '4'
- '6'
- '8'
- '10'
before_script:
- export NPMVERSION=$(echo "$($(which npm) -v)"|cut -c1)
- 'if [[ $NPMVERSION == 5 ]]; then npm install -g npm@5; fi'
- npm -v
- npm install winston@2.3.1
- 'npm install https://github.com/ioBroker/ioBroker.js-controller/tarball/master --production'
env:
- CXX=g++-4.8
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8

@ -0,0 +1,9 @@
The MIT License (MIT)
Copyright (c) 2014-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,108 @@
![Logo](admin/pushover.png)
# ioBroker pushover Adapter
==============
[![NPM version](http://img.shields.io/npm/v/iobroker.pushover.svg)](https://www.npmjs.com/package/iobroker.pushover)
[![Downloads](https://img.shields.io/npm/dm/iobroker.pushover.svg)](https://www.npmjs.com/package/iobroker.pushover)
[![NPM](https://nodei.co/npm/iobroker.pushover.png?downloads=true)](https://nodei.co/npm/iobroker.pushover/)
Send pushover notifications from ioBroker.
## Configuration
First of all it is required an account on pushover.
![Pushover configuration](img/Screen0.png)
![API Token](img/Screen1.png)
![Group Token](img/Screen3.png)
## Usage
To send notification from ScriptEngine just write:
```javascript
// send notification to all instances of pushover adapter
sendTo("pushover", "message body");
// send notification to specific instance of pushover adapter
sendTo("pushover.1", "message body");
// To specify subject or other options
sendTo("pushover", {
message: 'Test text', // mandatory - your text message
title: 'SweetHome', // optional - your message's title, otherwise your app's name is used
sound: 'magic', // optional - the name of one of the sounds supported by device clients to override the user's default sound choice
// pushover, bike, bugle, cashregister, classical, cosmic, falling,
// gamelan, incoming, intermission, magic, mechanical, pianobar, siren,
// spacealarm, tugboat, alien, climb, persistent, echo, updown, none
priority: -1, // optional
// -1 to always send as a quiet notification,
// 1 to display as high-priority and bypass the user's quiet hours, or
// 2 to also require confirmation from the user
token: 'API/KEY token' // optional
// add other than configurated token to the call
url, // optional - a supplementary URL to show with your message
url_title, // optional - a title for your supplementary URL, otherwise just the URL is shown
device, // optional - your user's device name to send the message directly to that device, rather than all of the user's devices
timestamp // optional - a Unix timestamp of your message's date and time to display to the user, rather than the time your message is received by our API
});
```
## Changelog
### 1.1.0 (2018-09-02)
* (bluefox) Admin3 is supported now
### 1.0.4 (2017-10-22)
* (janhuddel) callback is now possible (to receive the receipt from pushover if you use priority = 2)
### 1.0.3 (2017-10-21)
* (Tan-DE) Change priorities in blockly
### 1.0.2 (2016-10-12)
* (bluefox) support of blockly
### 1.0.1 (2016-08-28)
* (bluefox) filter out double messages
### 1.0.0 (2016-06-01)
* (bluefox) fix timestamp
* (bluefox) update grunt packages
### 0.1.1 (2015-05-03)
* (bluefox) add readme link
### 0.1.0 (2015-01-03)
* (bluefox) enable npm install
### 0.0.4 (2014-11-22)
* (bluefox) support of new naming concept
### 0.0.3 (2014-10-08)
* (bluefox) add "daemon" mode to "subscribe"
## License
The MIT License (MIT)
Copyright (c) 2014-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,217 @@
'use strict';
goog.provide('Blockly.JavaScript.Sendto');
goog.require('Blockly.JavaScript');
/// --- SendTo pushover --------------------------------------------------
Blockly.Words['pushover'] = {'en': 'pushover', 'de': 'pushover', 'ru': 'pushover'};
Blockly.Words['pushover_message'] = {'en': 'message', 'de': 'Meldung', 'ru': 'сообщение'};
Blockly.Words['pushover_title'] = {'en': 'title (optional)', 'de': 'Betreff (optional)', 'ru': 'заголовок (не обяз.)'};
Blockly.Words['pushover_sound'] = {'en': 'sound', 'de': 'Klang', 'ru': 'звук'};
Blockly.Words['pushover_priority'] = {'en': 'priority', 'de': 'Priorität', 'ru': 'приоритет'};
Blockly.Words['pushover_url'] = {'en': 'URL (optional)', 'de': 'URL (optional)', 'ru': 'URL (не обяз.)'};
Blockly.Words['pushover_url_title'] = {'en': 'URL title (optional)', 'de': 'URL Betreff (optional)', 'ru': 'заголовок для URL (не обяз.)'};
Blockly.Words['pushover_device'] = {'en': 'device ID (optional)', 'de': 'Gerät ID (optional)', 'ru': 'ID устройства (не обяз.)'};
Blockly.Words['pushover_timestamp'] = {'en': 'time in ms (optional)', 'de': 'Zeit in ms (optional)', 'ru': 'время в мс (не обяз.)'};
Blockly.Words['pushover_normal'] = {'en': 'default', 'de': 'Normal', 'ru': 'по умолчанию'};
Blockly.Words['pushover_high'] = {'en': 'high priority', 'de': 'Hohe Priorität', 'ru': 'приоритетное'};
Blockly.Words['pushover_quiet'] = {'en': 'quiet', 'de': 'Leise', 'ru': 'тихое'};
Blockly.Words['pushover_confirmation'] = {'en': 'with confirmation', 'de': 'Mit Bestätigung', 'ru': 'с подтверждением'};
Blockly.Words['pushover_sound_default'] = {'en': 'default', 'de': 'normal', 'ru': 'по умолчанию'};
Blockly.Words['pushover_sound_pushover'] = {'en': 'pushover', 'de': 'pushover', 'ru': 'pushover'};
Blockly.Words['pushover_sound_bike'] = {'en': 'bike', 'de': 'bike', 'ru': 'bike'};
Blockly.Words['pushover_sound_bugle'] = {'en': 'bugle', 'de': 'bugle', 'ru': 'bugle'};
Blockly.Words['pushover_sound_cashregister'] = {'en': 'cashregister', 'de': 'cashregister', 'ru': 'cashregister'};
Blockly.Words['pushover_sound_classical'] = {'en': 'classical', 'de': 'classical', 'ru': 'classical'};
Blockly.Words['pushover_sound_cosmic'] = {'en': 'cosmic', 'de': 'cosmic', 'ru': 'cosmic'};
Blockly.Words['pushover_sound_falling'] = {'en': 'falling', 'de': 'falling', 'ru': 'falling'};
Blockly.Words['pushover_sound_gamelan'] = {'en': 'gamelan', 'de': 'gamelan', 'ru': 'gamelan'};
Blockly.Words['pushover_sound_incoming'] = {'en': 'incoming', 'de': 'incoming', 'ru': 'incoming'};
Blockly.Words['pushover_sound_intermission'] = {'en': 'intermission', 'de': 'intermission', 'ru': 'intermission'};
Blockly.Words['pushover_sound_magic'] = {'en': 'magic', 'de': 'magic', 'ru': 'magic'};
Blockly.Words['pushover_sound_mechanical'] = {'en': 'mechanical', 'de': 'mechanical', 'ru': 'mechanical'};
Blockly.Words['pushover_sound_pianobar'] = {'en': 'pianobar', 'de': 'pianobar', 'ru': 'pianobar'};
Blockly.Words['pushover_sound_siren'] = {'en': 'siren', 'de': 'siren', 'ru': 'siren'};
Blockly.Words['pushover_sound_spacealarm'] = {'en': 'spacealarm', 'de': 'spacealarm', 'ru': 'spacealarm'};
Blockly.Words['pushover_sound_tugboat'] = {'en': 'tugboat', 'de': 'tugboat', 'ru': 'tugboat'};
Blockly.Words['pushover_sound_alien'] = {'en': 'alien', 'de': 'alien', 'ru': 'alien'};
Blockly.Words['pushover_sound_climb'] = {'en': 'climb', 'de': 'climb', 'ru': 'climb'};
Blockly.Words['pushover_sound_persistent'] = {'en': 'persistent', 'de': 'persistent', 'ru': 'persistent'};
Blockly.Words['pushover_sound_echo'] = {'en': 'echo', 'de': 'echo', 'ru': 'echo'};
Blockly.Words['pushover_sound_updown'] = {'en': 'updown', 'de': 'updown', 'ru': 'updown'};
Blockly.Words['pushover_sound_none'] = {'en': 'none', 'de': 'keins', 'ru': 'без звука'};
Blockly.Words['pushover_log'] = {'en': 'log level', 'de': 'Loglevel', 'ru': 'Протокол'};
Blockly.Words['pushover_log_none'] = {'en': 'none', 'de': 'keins', 'ru': 'нет'};
Blockly.Words['pushover_log_info'] = {'en': 'info', 'de': 'info', 'ru': 'инфо'};
Blockly.Words['pushover_log_debug'] = {'en': 'debug', 'de': 'debug', 'ru': 'debug'};
Blockly.Words['pushover_log_warn'] = {'en': 'warning', 'de': 'warning', 'ru': 'warning'};
Blockly.Words['pushover_log_error'] = {'en': 'error', 'de': 'error', 'ru': 'ошибка'};
Blockly.Words['pushover_anyInstance'] = {'en': 'all instances', 'de': 'Alle Instanzen', 'ru': 'На все драйвера'};
Blockly.Words['pushover_tooltip'] = {'en': 'Send message to pushover', 'de': 'Sende eine Meldung über Telegram', 'ru': 'Послать сообщение через Pushover'};
Blockly.Words['pushover_help'] = {'en': 'https://github.com/ioBroker/ioBroker.pushover/blob/master/README.md', 'de': 'https://github.com/ioBroker/ioBroker.pushover/blob/master/README.md', 'ru': 'https://github.com/ioBroker/ioBroker.pushover/blob/master/README.md'};
Blockly.Sendto.blocks['pushover'] =
'<block type="pushover">'
+ ' <value name="INSTANCE">'
+ ' </value>'
+ ' <value name="MESSAGE">'
+ ' <shadow type="text">'
+ ' <field name="TEXT">text</field>'
+ ' </shadow>'
+ ' </value>'
+ ' <value name="TITLE">'
+ ' </value>'
+ ' <value name="SOUND">'
+ ' </value>'
+ ' <value name="PRIORITY">'
+ ' </value>'
+ ' <value name="URL">'
+ ' </value>'
+ ' <value name="URL_TITLE">'
+ ' </value>'
+ ' <value name="DEVICE">'
+ ' </value>'
+ ' <value name="TIMESTAMP">'
+ ' </value>'
+ ' <value name="LOG">'
+ ' </value>'
+ '</block>';
Blockly.Blocks['pushover'] = {
init: function() {
this.appendDummyInput('INSTANCE')
.appendField(Blockly.Words['pushover'][systemLang])
.appendField(new Blockly.FieldDropdown([[Blockly.Words['pushover_anyInstance'][systemLang], ""], ["pushover.0", ".0"], ["pushover.1", ".1"], ["pushover.2", ".2"], ["pushover.3", ".3"], ["pushover.4", ".4"]]), "INSTANCE");
this.appendValueInput('MESSAGE')
.appendField(Blockly.Words['pushover_message'][systemLang]);
this.appendDummyInput('SOUND')
.appendField(Blockly.Words['pushover_sound'][systemLang])
.appendField(new Blockly.FieldDropdown([
[Blockly.Words['pushover_sound_default'][systemLang], ""],
[Blockly.Words['pushover_sound_pushover'][systemLang], "pushover"],
[Blockly.Words['pushover_sound_bike'][systemLang], "bike"],
[Blockly.Words['pushover_sound_bugle'][systemLang], "bugle"],
[Blockly.Words['pushover_sound_cashregister'][systemLang], "cashregister"],
[Blockly.Words['pushover_sound_classical'][systemLang], "classical"],
[Blockly.Words['pushover_sound_cosmic'][systemLang], "cosmic"],
[Blockly.Words['pushover_sound_falling'][systemLang], "falling"],
[Blockly.Words['pushover_sound_gamelan'][systemLang], "gamelan"],
[Blockly.Words['pushover_sound_incoming'][systemLang], "incoming"],
[Blockly.Words['pushover_sound_intermission'][systemLang], "intermission"],
[Blockly.Words['pushover_sound_magic'][systemLang], "magic"],
[Blockly.Words['pushover_sound_mechanical'][systemLang], "mechanical"],
[Blockly.Words['pushover_sound_pianobar'][systemLang], "pianobar"],
[Blockly.Words['pushover_sound_siren'][systemLang], "siren"],
[Blockly.Words['pushover_sound_spacealarm'][systemLang], "spacealarm"],
[Blockly.Words['pushover_sound_tugboat'][systemLang], "tugboat"],
[Blockly.Words['pushover_sound_alien'][systemLang], "alien"],
[Blockly.Words['pushover_sound_climb'][systemLang], "climb"],
[Blockly.Words['pushover_sound_persistent'][systemLang], "persistent"],
[Blockly.Words['pushover_sound_echo'][systemLang], "echo"],
[Blockly.Words['pushover_sound_updown'][systemLang], "updown"],
[Blockly.Words['pushover_sound_none'][systemLang], "none"]
]), 'SOUND');
this.appendDummyInput('PRIORITY')
.appendField(Blockly.Words['pushover_priority'][systemLang])
.appendField(new Blockly.FieldDropdown([
[Blockly.Words['pushover_normal'][systemLang], "0"],
[Blockly.Words['pushover_high'][systemLang], "1"],
[Blockly.Words['pushover_quiet'][systemLang], "-1"],
[Blockly.Words['pushover_confirmation'][systemLang], "2"]
]), 'PRIORITY');
var input = this.appendValueInput('TITLE')
.setCheck('String')
.appendField(Blockly.Words['pushover_title'][systemLang]);
if (input.connection) input.connection._optional = true;
input = this.appendValueInput("URL")
.setCheck('String')
.appendField(Blockly.Words['pushover_url'][systemLang]);
if (input.connection) input.connection._optional = true;
input = this.appendValueInput('URL_TITLE')
.setCheck('String')
.appendField(Blockly.Words['pushover_url_title'][systemLang]);
if (input.connection) input.connection._optional = true;
input = this.appendValueInput('DEVICE')
.setCheck('String')
.appendField(Blockly.Words['pushover_device'][systemLang]);
if (input.connection) input.connection._optional = true;
input = this.appendValueInput('TIMESTAMP')
.setCheck('Date')
.appendField(Blockly.Words['pushover_timestamp'][systemLang]);
if (input.connection) input.connection._optional = true;
this.appendDummyInput('LOG')
.appendField(Blockly.Words['pushover_log'][systemLang])
.appendField(new Blockly.FieldDropdown([
[Blockly.Words['pushover_log_none'][systemLang], ''],
[Blockly.Words['pushover_log_info'][systemLang], 'log'],
[Blockly.Words['pushover_log_debug'][systemLang], 'debug'],
[Blockly.Words['pushover_log_warn'][systemLang], 'warn'],
[Blockly.Words['pushover_log_error'][systemLang], 'error']
]), 'LOG');
this.setInputsInline(false);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(Blockly.Sendto.HUE);
this.setTooltip(Blockly.Words['pushover_tooltip'][systemLang]);
this.setHelpUrl(Blockly.Words['pushover_help'][systemLang]);
}
};
Blockly.JavaScript['pushover'] = function(block) {
var dropdown_instance = block.getFieldValue('INSTANCE');
var logLevel = block.getFieldValue('LOG');
var message = Blockly.JavaScript.valueToCode(block, 'MESSAGE', Blockly.JavaScript.ORDER_ATOMIC);
var text = '{\n';
text += ' message: ' + message + ',\n';
text += ' sound: "' + block.getFieldValue('SOUND') + '",\n';
var value = parseInt(block.getFieldValue('PRIORITY'), 10);
if (value) text += ' priority: ' + value + ',\n';
if (value === 2) {
text += ' retry: 60,\n';
text += ' expire: 3600,\n';
}
value = Blockly.JavaScript.valueToCode(block, 'URL', Blockly.JavaScript.ORDER_ATOMIC);
if (value) text += ' url: ' + value + ',\n';
value = Blockly.JavaScript.valueToCode(block, 'URL_TITLE', Blockly.JavaScript.ORDER_ATOMIC);
if (value) text += ' url_title: ' + value + ',\n';
value = Blockly.JavaScript.valueToCode(block, 'TITLE', Blockly.JavaScript.ORDER_ATOMIC);
if (value) text += ' title: ' + value + ',\n';
value = Blockly.JavaScript.valueToCode(block, 'DEVICE', Blockly.JavaScript.ORDER_ATOMIC);
if (value) text += ' device: ' + value + ',\n';
value = Blockly.JavaScript.valueToCode(block, 'TIMESTAMP', Blockly.JavaScript.ORDER_ATOMIC);
if (value) text += ' timestamp: ' + value + ',\n';
text = text.substring(0, text.length - 2);
text += '\n';
text += '}';
var logText;
if (logLevel) {
logText = 'console.' + logLevel + '("pushover: " + ' + message + ');\n'
} else {
logText = '';
}
return 'sendTo("pushover' + dropdown_instance + '", "send", ' + text + ');\n' + logText;
};

@ -0,0 +1,168 @@
<html>
<head>
<!-- these 4 files always have to be included -->
<link rel="stylesheet" type="text/css" href="../../lib/css/themes/jquery-ui/redmond/jquery-ui.min.css"/>
<script type="text/javascript" src="../../lib/js/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="../../socket.io/socket.io.js"></script>
<script type="text/javascript" src="../../lib/js/jquery-ui-1.10.3.full.min.js"></script>
<!-- these two file always have to be included -->
<link rel="stylesheet" type="text/css" href="../../css/adapter.css"/>
<script type="text/javascript" src="../../js/translate.js"></script>
<script type="text/javascript" src="../../js/adapter-settings.js"></script>
<script src="words.js"></script>
<!-- you have to define 2 functions in the global scope: -->
<script type="text/javascript">
var rooms = [];
var roomsSelect = "";
var devices = [];
var processed = 1000;
var isStarted = null;
// the function loadSettings has to exist ...
function load(settings, onChange) {
for (var key in settings) {
if ($('#' + key + '.value').attr('type') === 'checkbox') {
$('#' + key + '.value').prop('checked', settings[key]).on('change', function() {
onChange();
});
} else {
$('#' + key + '.value').val(settings[key]).on('change', function() {
changed = true;
$('#save').button("enable");
}).on('keyup', function() {
onChange();
});
}
}
onChange(false);
$('#test').button().click(test);
}
function test() {
//var newValue = JSON.stringify(getSettings());
if (!common.enabled) {
showMessage('Enable first the adapter to test notification.');
return;
}
if (changed) {
showMessage('First save the settings');
return;
}
sendTo('pushover.' + instance, 'send', 'This is test notification!');
showMessage('Check the log or your pushover app!');
}
function saveHelper(obj, id, value) {
var ids = id.split('_');
if (ids.length === 1) {
obj[id] = value;
} else if (ids.length === 2) {
if (!obj[ids[0]]) obj[ids[0]] = {};
obj[ids[0]][ids[1]] = value;
} else if (ids.length === 3) {
if (ids[2] == "pass") {
value = encrypt("Zgfr56gFe87jJOM", value);
}
if (!obj[ids[0]]) obj[ids[0]] = {};
if (!obj[ids[0]][ids[1]]) obj[ids[0]][ids[1]] = {};
obj[ids[0]][ids[1]][ids[2]] = value;
}
return obj;
}
function getSettings() {
var obj = {};
$('.value').each(function () {
var $this = $(this);
var id = $this.attr('id');
if ($this.attr('type') === 'checkbox') {
obj = saveHelper(obj, id, $this.prop('checked'))
} else {
obj = saveHelper(obj, id, $this.val())
}
});
return obj;
}
function save(callback) {
callback(getSettings());
}
</script>
</head>
<body>
<!-- you have to put your config page in a div with id adapter-container -->
<div id="adapter-container">
<table><tr>
<td><img src="pushover.png"></td>
<td style="padding-top: 20px;padding-left: 10px"><h3 class="translate">Pushover adapter settings</h3></td>
</tr></table>
<h4 class="translate">Pushover settings</h4>
<table>
<tr>
<td class="translate">Group Key:</td>
<td><input id="user" type="text" size="50" class="value"/></td>
<td class="translate">Group Key of Delivery Group</td>
</tr>
<tr>
<td class="translate">Token:</td>
<td><input id="token" type="text" size="50" class="value"/></td>
<td class="translate">API Token/Key of Application</td>
</tr>
<tr><td colspan="3"><br><h4 class="translate">Notification settings</h4></td></tr>
<tr>
<td class="translate">Title:</td>
<td><input id="title" type="text" size="50" class="value"/></td>
<td></td>
</tr>
<tr>
<td class="translate">Sound:</td>
<td><select id="sound" class="value">
<option value=""> default </option>
<option value="pushover"> pushover </option>
<option value="bike"> bike </option>
<option value="bugle"> bugle</option>
<option value="cashregister"> cashregister </option>
<option value="classical"> classical </option>
<option value="cosmic"> cosmic </option>
<option value="falling"> falling</option>
<option value="gamelan"> gamelan </option>
<option value="incoming"> incoming </option>
<option value="intermission"> intermission </option>
<option value="magic"> magic </option>
<option value="mechanical"> mechanical </option>
<option value="pianobar"> pianobar </option>
<option value="siren"> siren</option>
<option value="spacealarm"> spacealarm </option>
<option value="tugboat"> tugboat </option>
<option value="alien"> alien</option>
<option value="climb"> climb </option>
<option value="persistent"> persistent </option>
<option value="echo"> echo</option>
<option value="updown"> updown </option>
<option value="none"> none</option>
</select></td>
<td></td>
</tr>
<tr>
<td class="translate">Priority:</td>
<td><select id="priority" class="value">
<option value="-1" class="translate">quiet</option>
<option value="0" class="translate">default</option>
<option value="1" class="translate">high-priority</option>
<!--option value="2">require confirmation</option-->
</select></td>
<td></td>
</tr>
</table>
<button id="test" class="translate">Test</button>
</div>
</body>
</html>

@ -0,0 +1,183 @@
<html>
<head>
<link rel="stylesheet" type="text/css" href="../../lib/css/materialize.css">
<link rel="stylesheet" type="text/css" href="../../css/adapter.css"/>
<script type="text/javascript" src="../../lib/js/jquery-3.2.1.min.js"></script>
<script type="text/javascript" src="../../socket.io/socket.io.js"></script>
<script type="text/javascript" src="../../lib/js/ace-1.2.0/ace.js"></script>
<!-- these files always have to be included -->
<script type="text/javascript" src="../../js/translate.js"></script>
<script type="text/javascript" src="../../lib/js/materialize.js"></script>
<script type="text/javascript" src="../../js/adapter-settings.js"></script>
<script type="text/javascript" src="words.js"></script>
<!-- you have to define 2 functions in the global scope: -->
<script type="text/javascript">
// the function loadSettings has to exist ...
function load(settings, onChange) {
for (var key in settings) {
if (!settings.hasOwnProperty(key)) continue;
var $value = $('#' + key + '.value');
if ($value.attr('type') === 'checkbox') {
$value.prop('checked', settings[key]).on('change', function() {
onChange();
});
} else {
$value.val(settings[key]).on('change', function() {
changed = true;
$('#save').button("enable");
}).on('keyup', function() {
onChange();
});
}
}
onChange(false);
$('#test').click(test);
}
function test() {
//var newValue = JSON.stringify(getSettings());
if (!common.enabled) {
showMessage('Enable first the adapter to test notification.');
return;
}
if (changed) {
showMessage('First save the settings');
return;
}
sendTo('pushover.' + instance, 'send', 'This is test notification!');
showMessage('Check the log or your pushover app!');
}
function saveHelper(obj, id, value) {
var ids = id.split('_');
if (ids.length === 1) {
obj[id] = value;
} else if (ids.length === 2) {
if (!obj[ids[0]]) obj[ids[0]] = {};
obj[ids[0]][ids[1]] = value;
} else if (ids.length === 3) {
if (ids[2] === 'pass') {
value = encrypt("Zgfr56gFe87jJOM", value);
}
if (!obj[ids[0]]) obj[ids[0]] = {};
if (!obj[ids[0]][ids[1]]) obj[ids[0]][ids[1]] = {};
obj[ids[0]][ids[1]][ids[2]] = value;
}
return obj;
}
function getSettings() {
var obj = {};
$('.value').each(function () {
var $this = $(this);
var id = $this.attr('id');
if ($this.attr('type') === 'checkbox') {
obj = saveHelper(obj, id, $this.prop('checked'))
} else {
obj = saveHelper(obj, id, $this.val())
}
});
return obj;
}
function save(callback) {
callback(getSettings());
}
</script>
</head>
<body>
<!-- you have to put your config page in a div with id adapter-container -->
<div id="adapter-container">
<div class="m adapter-container">
<div class="row">
<div class="col s12">
<ul class="tabs">
<li class="tab col s4"><a href="#tab-main" class="translate active">Pushover settings</a></li>
<li class="tab col s4"><a href="#tab-notification" class="translate">Notification settings</a></li>
</ul>
</div>
<div id="tab-main" class="col s12 page">
<div class="row">
<div class="col s6 m4 l2">
<img src="pushover.png" class="logo">
</div>
</div>
<div class="row">
<div class="input-field col s12 m6 l4 xl3">
<input id="user" type="text" size="50" class="value"/>
<label for="user" class="translate">Group Key</label>
<span class="translate">Group Key of Delivery Group</span>
</div>
</div>
<div class="row">
<div class="input-field col s12 m6 l4 xl3">
<input id="token" type="text" size="50" class="value"/>
<label for="token" class="translate">Token:</label>
<span class="translate">API Token/Key of Application</span>
</div>
</div>
</div>
<div id="tab-notification" class="col s12 page">
<div class="row">
<div class="input-field col s12 m6 l4 xl3">
<input id="title" type="text" size="50" class="value"/>
<label for="title" class="translate">Title</label>
</div>
</div>
<div class="row">
<div class="input-field col s12 m6 l4 xl3">
<select id="sound" class="value">
<option value=""> default </option>
<option value="pushover"> pushover </option>
<option value="bike"> bike </option>
<option value="bugle"> bugle</option>
<option value="cashregister"> cashregister </option>
<option value="classical"> classical </option>
<option value="cosmic"> cosmic </option>
<option value="falling"> falling</option>
<option value="gamelan"> gamelan </option>
<option value="incoming"> incoming </option>
<option value="intermission"> intermission </option>
<option value="magic"> magic </option>
<option value="mechanical"> mechanical </option>
<option value="pianobar"> pianobar </option>
<option value="siren"> siren</option>
<option value="spacealarm"> spacealarm </option>
<option value="tugboat"> tugboat </option>
<option value="alien"> alien</option>
<option value="climb"> climb </option>
<option value="persistent"> persistent </option>
<option value="echo"> echo</option>
<option value="updown"> updown </option>
<option value="none"> none</option>
</select>
<label for="sound" class="translate">Sound</label>
</div>
</div>
<div class="row">
<div class="input-field col s12 m6 l4 xl3">
<select id="priority" class="value">
<option value="-1" class="translate">quiet</option>
<option value="0" class="translate">default</option>
<option value="1" class="translate">high-priority</option>
<!--option value="2">require confirmation</option-->
</select>
<label for="priority" class="translate">Priority</label>
</div>
</div>
<div class="row">
<div class="col s4">
<a id="test" class="waves-effect waves-light btn"><i class="material-icons left">notifications_active</i><span class="translate">Test</span></a>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

@ -0,0 +1,23 @@
// DO NOT EDIT THIS FILE!!! IT WILL BE AUTOMATICALLY GENERATED FROM src/i18n
/*global systemDictionary:true */
'use strict';
systemDictionary = {
"Pushover adapter settings": { "en": "Pushover adapter settings", "de": "Pushover Adaptereinstellungen", "ru": "Настройки адаптера Pushover", "pt": "Configurações do adaptador pushover", "nl": "Pushover adapter-instellingen", "fr": "Paramètres de l'adaptateur de pushover", "it": "Impostazioni dell'adattatore pushover", "es": "Configuración del adaptador Pushover", "pl": "Ustawienia adaptera pushover"},
"Pushover settings": { "en": "Pushover settings", "de": "Pushover Einstellungen", "ru": "Настройки Pushover", "pt": "Configurações pushover", "nl": "Pushover instellingen", "fr": "Paramètres de pushover", "it": "Impostazioni pushover", "es": "Configuración de Pushover", "pl": "Ustawienia pushover"},
"Group Key": { "en": "Group Key", "de": "Gruppentaste", "ru": "Ключ группы", "pt": "Chave de grupo", "nl": "Groepssleutel", "fr": "Clé de groupe", "it": "Chiave di gruppo", "es": "Clave grupal", "pl": "Klucz grupowy"},
"Token": { "en": "Token", "de": "Zeichen", "ru": "знак", "pt": "Símbolo", "nl": "blijk", "fr": "Jeton", "it": "Gettone", "es": "Simbólico", "pl": "Znak"},
"Title": { "en": "Title", "de": "Titel", "ru": "заглавие", "pt": "Título", "nl": "Titel", "fr": "Titre", "it": "Titolo", "es": "Título", "pl": "Tytuł"},
"Sound": { "en": "Sound", "de": "Klingen", "ru": "звук", "pt": "Som", "nl": "Geluid", "fr": "Du son", "it": "Suono", "es": "Sonar", "pl": "Dźwięk"},
"Priority": { "en": "Priority", "de": "Priorität", "ru": "приоритет", "pt": "Prioridade", "nl": "Prioriteit", "fr": "Priorité", "it": "Priorità", "es": "Prioridad", "pl": "Priorytet"},
"Test": { "en": "Test", "de": "Prüfung", "ru": "Тест", "pt": "Teste", "nl": "Test", "fr": "Tester", "it": "Test", "es": "Prueba", "pl": "Test"},
"Notification settings": { "en": "Notification settings", "de": "Benachrichtigungseinstellungen", "ru": "Настройки уведомлений", "pt": "Configurações de notificação", "nl": "Notificatie instellingen", "fr": "Paramètres de notification", "it": "Impostazioni di notifica", "es": "Configuración de las notificaciones", "pl": "Ustawienia powiadomień"},
"Group Key of Delivery Group": { "en": "Group Key of Delivery Group", "de": "Gruppenschlüssel der Liefergruppe", "ru": "Групповой ключ группы доставки", "pt": "Chave de Grupo do Grupo de Entrega", "nl": "Groepssleutel van leveringsgroep", "fr": "Group Key of Delivery Group", "it": "Gruppo chiave del gruppo di consegna", "es": "Grupo clave del grupo de entrega", "pl": "Klucz grupowy grupy dostaw"},
"API Token/Key of Application": { "en": "API Token/Key of Application", "de": "API Token / Schlüssel der Anwendung", "ru": "API-токен / ключ приложения", "pt": "Token da API / chave de aplicação", "nl": "API-token / sleutel van de applicatie", "fr": "API Token / clé d'application", "it": "Token API / Chiave di applicazione", "es": "Token API / Clave de aplicación", "pl": "Token API / klucz aplikacji"},
"quiet": { "en": "quiet", "de": "ruhig", "ru": "тихо", "pt": "quieto", "nl": "rustig", "fr": "silencieux", "it": "silenzioso", "es": "tranquilo", "pl": "cichy"},
"default": { "en": "default", "de": "Standard", "ru": "по умолчанию", "pt": "padrão", "nl": "standaard", "fr": "défaut", "it": "predefinito", "es": "defecto", "pl": "domyślna"},
"high-priority": { "en": "high-priority", "de": "hohe Priorität", "ru": "высокий приоритет", "pt": "prioridade máxima", "nl": "hoge prioriteit", "fr": "haute priorité", "it": "priorità alta", "es": "alta prioridad", "pl": "wysoki priorytet"},
"Enable first the adapter to test notification.": {"en": "Enable first the adapter to test notification.", "de": "Aktivieren Sie zuerst den Adapter, um die Benachrichtigung zu testen.", "ru": "Включите сначала адаптер для проверки уведомления.", "pt": "Ative primeiro o adaptador para testar a notificação.", "nl": "Schakel eerst de adapter in om de melding te testen.", "fr": "Activez d'abord l'adaptateur pour tester la notification.", "it": "Abilitare prima l'adattatore per verificare la notifica.", "es": "Primero habilite el adaptador para probar la notificación.", "pl": "Najpierw włącz adapter, aby przetestować powiadomienie."},
"First save the settings": { "en": "First save the settings", "de": "Speichern Sie zuerst die Einstellungen", "ru": "Сначала сохраните настройки", "pt": "Primeiro salve as configurações", "nl": "Sla eerst de instellingen op", "fr": "Commencez par enregistrer les paramètres", "it": "Prima salva le impostazioni", "es": "Primero guarde la configuración", "pl": "Najpierw zapisz ustawienia"},
"Check the log or your pushover app!": { "en": "Check the log or your pushover app!", "de": "Überprüfen Sie das Protokoll oder Ihre Pushover-App!", "ru": "Проверьте журнал или приложение pushover!", "pt": "Verifique o log ou o seu aplicativo pushover!", "nl": "Controleer het logboek of je pushover-app!", "fr": "Consultez le journal ou votre application pushover!", "it": "Controlla il registro o la tua app pushover!", "es": "¡Comprueba el registro o tu aplicación de pushover!", "pl": "Sprawdź dziennik lub swoją aplikację pushover!"}
};

@ -0,0 +1,25 @@
version: 'test-{build}'
environment:
matrix:
- nodejs_version: '4'
- nodejs_version: '6'
- nodejs_version: '8'
- nodejs_version: '10'
platform:
- x86
- x64
clone_folder: 'c:\projects\%APPVEYOR_PROJECT_NAME%'
install:
- ps: 'Install-Product node $env:nodejs_version $env:platform'
- ps: '$NpmVersion = (npm -v).Substring(0,1)'
- ps: 'if($NpmVersion -eq 5) { npm install -g npm@5 }'
- ps: npm --version
- npm install
- npm install winston@2.3.1
- 'npm install https://github.com/ioBroker/ioBroker.js-controller/tarball/master --production'
test_script:
- echo %cd%
- node --version
- npm --version
- npm test
build: 'off'

@ -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']);

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

@ -0,0 +1,90 @@
{
"common": {
"name": "pushover",
"version": "1.1.0",
"news": {
"1.1.0": {
"en": "Admin3 is supported now",
"de": "Admin3 wird jetzt unterstützt",
"ru": "Admin3 теперь поддерживается",
"pt": "Admin3 é suportado agora",
"nl": "Admin3 wordt nu ondersteund",
"fr": "Admin3 est pris en charge maintenant",
"it": "Admin3 è supportato ora",
"es": "Admin3 es compatible ahora",
"pl": "Admin3 jest teraz obsługiwany"
},
"1.0.4": {
"en": "callback is now possible (to receive the receipt from pushover if you use priority = 2)",
"de": "Callback ist jetzt möglich (um den Beleg von pushover zu erhalten, wenn Sie Priorität = 2 verwenden)",
"ru": "теперь возможен обратный вызов (чтобы получить результат от pushover, если вы используете приоритет = 2)"
},
"1.0.3": {
"en": "Change priorities in blockly",
"de": "Ändere Prioritäten in Blockly",
"ru": "Изменены значения приоритетов в blockly"
},
"1.0.2": {
"en": "support of blockly",
"de": "Blockly-Unterstützung",
"ru": "Поддержка blockly"
},
"1.0.1": {
"en": "filter out double messages",
"de": "Ausfiltern doppelte Meldungen",
"ru": "Отфильтровывать двойные сообщения"
},
"1.0.0": {
"en": "fix timestamp\nupdate grunt packages",
"de": "fix timestamp\nupdate grunt packages",
"ru": "fix timestamp\nupdate grunt packages"
}
},
"title": "Pushover",
"desc": {
"en": "This adapter allows to send pushover notifications from ioBroker",
"de": "Dieser Adapter ermöglicht das Senden von Pushover-Benachrichtigungen von ioBroker",
"ru": "Этот адаптер позволяет отправлять pushover-уведомления от ioBroker",
"pt": "Este adaptador permite enviar notificações pushover do ioBroker",
"nl": "Met deze adapter kunt u pushover-meldingen van ioBroker verzenden",
"fr": "Cet adaptateur permet d'envoyer des notifications de transfert depuis ioBroker",
"it": "Questo adattatore consente di inviare notifiche pushover da ioBroker",
"es": "Este adaptador permite enviar notificaciones fáciles de ioBroker",
"pl": "Ten adapter umożliwia wysyłanie powiadomień push z programu ioBroker"
},
"authors": [
"bluefox <dogafox@gmail.com>"
],
"license": "MIT",
"platform": "Javascript/Node.js",
"mode": "daemon",
"availableModes": [
"daemon",
"subscribe"
],
"loglevel": "info",
"messagebox": true,
"materialize": true,
"subscribe": "messagebox",
"readme": "https://github.com/ioBroker/ioBroker.pushover/blob/master/README.md",
"wakeup": true,
"enabled": true,
"icon": "pushover.png",
"keywords": [
"notification",
"pushover",
"message"
],
"extIcon": "https://raw.githubusercontent.com/ioBroker/ioBroker.pushover/master/admin/pushover.png",
"type": "messaging",
"blockly": true
},
"native": {
"user": "xxxxx",
"token": "xxxxx",
"title": "ioBroker",
"sound": "",
"priority": 0
},
"objects": []
}

@ -0,0 +1,83 @@
'use strict';
const fs = require('fs');
const path = require('path');
let controllerDir;
let appName;
/**
* returns application name
*
* The name of the application can be different and this function finds it out.
*
* @returns {string}
*/
function getAppName() {
const parts = __dirname.replace(/\\/g, '/').split('/');
return parts[parts.length - 2].split('.')[0];
}
/**
* looks for js-controller home folder
*
* @param {boolean} isInstall
* @returns {string}
*/
function getControllerDir(isInstall) {
// Find the js-controller location
const possibilities = [
'iobroker.js-controller',
'ioBroker.js-controller',
];
/** @type {string} */
let controllerPath;
for (const pkg of possibilities) {
try {
const possiblePath = require.resolve(pkg);
if (fs.existsSync(possiblePath)) {
controllerPath = possiblePath;
break;
}
} catch (e) { /* not found */ }
}
if (controllerPath == null) {
if (!isInstall) {
console.log('Cannot find js-controller');
process.exit(10);
} else {
process.exit();
}
}
// we found the controller
return path.dirname(controllerPath);
}
/**
* reads controller base settings
*
* @alias getConfig
* @returns {object}
*/
function getConfig() {
let configPath;
if (fs.existsSync(
configPath = path.join(controllerDir, 'conf', appName + '.json')
)) {
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
} else if (fs.existsSync(
configPath = path.join(controllerDir, 'conf', + appName.toLowerCase() + '.json')
)) {
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
} else {
throw new Error('Cannot find ' + controllerDir + '/conf/' + appName + '.json');
}
}
appName = getAppName();
controllerDir = getControllerDir(typeof process !== 'undefined' && process.argv && process.argv.indexOf('--install') !== -1);
const adapter = require(path.join(controllerDir, 'lib/adapter.js'));
exports.controllerDir = controllerDir;
exports.getConfig = getConfig;
exports.Adapter = adapter;
exports.appName = appName;

@ -0,0 +1,139 @@
/**
*
* ioBroker pushover Adapter
*
* (c) 2014-2018 bluefox
*
* MIT License
*
*/
/* jshint -W097 */// jshint strict:false
/*jslint node: true */
'use strict';
const utils = require(__dirname + '/lib/utils'); // Get common adapter utils
const Pushover = require('pushover-notifications');
const adapter = new utils.Adapter('pushover');
adapter.on('message', obj => {
if (obj && obj.command === 'send') {
processMessage(obj);
}
processMessages();
});
adapter.on('ready', main);
let stopTimer = null;
let pushover;
let lastMessageTime = 0;
let lastMessageText = '';
// Terminate adapter after 30 seconds idle
function stop() {
if (stopTimer) {
clearTimeout(stopTimer);
}
// Stop only if subscribe mode
if (adapter.common && adapter.common.mode === 'subscribe') {
stopTimer = setTimeout(() => {
stopTimer = null;
adapter.stop();
}, 30000);
}
}
function processMessage(obj) {
if (!obj || !obj.message) return;
// filter out double messages
const json = JSON.stringify(obj.message);
if (lastMessageTime && lastMessageText === JSON.stringify(obj.message) && new Date().getTime() - lastMessageTime < 1000) {
adapter.log.debug('Filter out double message [first was for ' + (new Date().getTime() - lastMessageTime) + 'ms]: ' + json);
return;
}
lastMessageTime = new Date().getTime();
lastMessageText = json;
if (stopTimer) clearTimeout(stopTimer);
sendNotification(obj.message, (err, response) => {
if (obj.callback) adapter.sendTo(obj.from, 'send', { error: err, response: response}, obj.callback);
});
stop();
}
function processMessages() {
adapter.getMessage((err, obj) => {
if (obj) {
processMessage(obj);
processMessages();
}
});
}
function main() {
// Adapter is started only if some one writes into "system.adapter.pushover.X.messagebox" new value
processMessages();
stop();
}
function sendNotification(message, callback) {
if (!message) message = {};
if (!pushover) {
if (adapter.config.user && adapter.config.token) {
pushover = new Pushover({
user: adapter.config.user,
token: adapter.config.token
});
} else {
adapter.log.error('Cannot send notification while not configured');
}
}
if (!pushover) return;
if (typeof message !== 'object') {
message = {message: message};
}
if (message.hasOwnProperty('token')) {
pushover.token = message.token
} else {
pushover.token = adapter.config.token
}
message.title = message.title || adapter.config.title;
message.sound = message.sound || (adapter.config.sound ? adapter.config.sound : undefined);
message.priority = message.priority || adapter.config.priority;
message.url = message.url || adapter.config.url;
message.url_title = message.url_title || adapter.config.url_title;
message.device = message.device || adapter.config.device;
message.message = message.message || '';
// if timestamp in ms => make seconds // if greater than 2000.01.01 00:00:00
if (message.timestamp && message.timestamp > 946681200000) {
message.timestamp = Math.round(message.timestamp / 1000);
}
// mandatory parameters if priority is high (2)
if (message.priority === 2) {
message.retry = parseInt(message.retry, 10) || 60;
message.expire = parseInt(message.expire, 10) || 3600;
}
adapter.log.info('Send pushover notification: ' + JSON.stringify(message));
pushover.send(message, (err, result) => {
if (err) {
adapter.log.error('Cannot send notification: ' + JSON.stringify(err));
if (callback) callback(err);
return false;
} else {
if (callback) callback(null, result);
return true;
}
});
}

@ -0,0 +1,35 @@
{
"name": "iobroker.pushover",
"description": "This adapter allows to send pushover notifications from ioBroker",
"version": "1.1.0",
"author": "bluefox <dogafox@gmail.com>",
"contributors": [
"bluefox <dogafox@gmail.com>"
],
"homepage": "https://github.com/ioBroker/ioBroker.pushover",
"license": "MIT",
"keywords": [
"ioBroker",
"pushover",
"home automation"
],
"repository": {
"type": "git",
"url": "https://github.com/ioBroker/ioBroker.pushover"
},
"dependencies": {
"pushover-notifications": "^1.0.0"
},
"devDependencies": {
"gulp": "^3.9.1",
"mocha": "^5.2.0",
"chai": "^4.1.2"
},
"bugs": {
"url": "https://github.com/ioBroker/ioBroker.pushover/issues"
},
"main": "main.js",
"scripts": {
"test": "node node_modules/mocha/bin/mocha --exit"
}
}

@ -0,0 +1,728 @@
/* jshint -W097 */// jshint strict:false
/*jslint node: true */
// check if tmp directory exists
var fs = require('fs');
var path = require('path');
var child_process = require('child_process');
var rootDir = path.normalize(__dirname + '/../../');
var pkg = require(rootDir + 'package.json');
var debug = typeof v8debug === 'object';
pkg.main = pkg.main || 'main.js';
var adapterName = path.normalize(rootDir).replace(/\\/g, '/').split('/');
adapterName = adapterName[adapterName.length - 2];
var adapterStarted = false;
function getAppName() {
var parts = __dirname.replace(/\\/g, '/').split('/');
return parts[parts.length - 3].split('.')[0];
}
var appName = getAppName().toLowerCase();
var objects;
var states;
var pid = null;
function copyFileSync(source, target) {
var targetFile = target;
//if target is a directory a new file with the same name will be created
if (fs.existsSync(target)) {
if ( fs.lstatSync( target ).isDirectory() ) {
targetFile = path.join(target, path.basename(source));
}
}
try {
fs.writeFileSync(targetFile, fs.readFileSync(source));
}
catch (err) {
console.log("file copy error: " +source +" -> " + targetFile + " (error ignored)");
}
}
function copyFolderRecursiveSync(source, target, ignore) {
var files = [];
var base = path.basename(source);
if (base === adapterName) {
base = pkg.name;
}
//check if folder needs to be created or integrated
var targetFolder = path.join(target, base);
if (!fs.existsSync(targetFolder)) {
fs.mkdirSync(targetFolder);
}
//copy
if (fs.lstatSync(source).isDirectory()) {
files = fs.readdirSync(source);
files.forEach(function (file) {
if (ignore && ignore.indexOf(file) !== -1) {
return;
}
var curSource = path.join(source, file);
var curTarget = path.join(targetFolder, file);
if (fs.lstatSync(curSource).isDirectory()) {
// ignore grunt files
if (file.indexOf('grunt') !== -1) return;
if (file === 'chai') return;
if (file === 'mocha') return;
copyFolderRecursiveSync(curSource, targetFolder, ignore);
} else {
copyFileSync(curSource, curTarget);
}
});
}
}
if (!fs.existsSync(rootDir + 'tmp')) {
fs.mkdirSync(rootDir + 'tmp');
}
function storeOriginalFiles() {
console.log('Store original files...');
var dataDir = rootDir + 'tmp/' + appName + '-data/';
var f = fs.readFileSync(dataDir + 'objects.json');
var objects = JSON.parse(f.toString());
if (objects['system.adapter.admin.0'] && objects['system.adapter.admin.0'].common) {
objects['system.adapter.admin.0'].common.enabled = false;
}
if (objects['system.adapter.admin.1'] && objects['system.adapter.admin.1'].common) {
objects['system.adapter.admin.1'].common.enabled = false;
}
fs.writeFileSync(dataDir + 'objects.json.original', JSON.stringify(objects));
try {
f = fs.readFileSync(dataDir + 'states.json');
fs.writeFileSync(dataDir + 'states.json.original', f);
}
catch (err) {
console.log('no states.json found - ignore');
}
}
function restoreOriginalFiles() {
console.log('restoreOriginalFiles...');
var dataDir = rootDir + 'tmp/' + appName + '-data/';
var f = fs.readFileSync(dataDir + 'objects.json.original');
fs.writeFileSync(dataDir + 'objects.json', f);
try {
f = fs.readFileSync(dataDir + 'states.json.original');
fs.writeFileSync(dataDir + 'states.json', f);
}
catch (err) {
console.log('no states.json.original found - ignore');
}
}
function checkIsAdapterInstalled(cb, counter, customName) {
customName = customName || pkg.name.split('.').pop();
counter = counter || 0;
var dataDir = rootDir + 'tmp/' + appName + '-data/';
console.log('checkIsAdapterInstalled...');
try {
var f = fs.readFileSync(dataDir + 'objects.json');
var objects = JSON.parse(f.toString());
if (objects['system.adapter.' + customName + '.0']) {
console.log('checkIsAdapterInstalled: ready!');
setTimeout(function () {
if (cb) cb();
}, 100);
return;
} else {
console.warn('checkIsAdapterInstalled: still not ready');
}
} catch (err) {
}
if (counter > 20) {
console.error('checkIsAdapterInstalled: Cannot install!');
if (cb) cb('Cannot install');
} else {
console.log('checkIsAdapterInstalled: wait...');
setTimeout(function() {
checkIsAdapterInstalled(cb, counter + 1);
}, 1000);
}
}
function checkIsControllerInstalled(cb, counter) {
counter = counter || 0;
var dataDir = rootDir + 'tmp/' + appName + '-data/';
console.log('checkIsControllerInstalled...');
try {
var f = fs.readFileSync(dataDir + 'objects.json');
var objects = JSON.parse(f.toString());
if (objects['system.adapter.admin.0']) {
console.log('checkIsControllerInstalled: installed!');
setTimeout(function () {
if (cb) cb();
}, 100);
return;
}
} catch (err) {
}
if (counter > 20) {
console.log('checkIsControllerInstalled: Cannot install!');
if (cb) cb('Cannot install');
} else {
console.log('checkIsControllerInstalled: wait...');
setTimeout(function() {
checkIsControllerInstalled(cb, counter + 1);
}, 1000);
}
}
function installAdapter(customName, cb) {
if (typeof customName === 'function') {
cb = customName;
customName = null;
}
customName = customName || pkg.name.split('.').pop();
console.log('Install adapter...');
var startFile = 'node_modules/' + appName + '.js-controller/' + appName + '.js';
// make first install
if (debug) {
child_process.execSync('node ' + startFile + ' add ' + customName + ' --enabled false', {
cwd: rootDir + 'tmp',
stdio: [0, 1, 2]
});
checkIsAdapterInstalled(function (error) {
if (error) console.error(error);
console.log('Adapter installed.');
if (cb) cb();
});
} else {
// add controller
var _pid = child_process.fork(startFile, ['add', customName, '--enabled', 'false'], {
cwd: rootDir + 'tmp',
stdio: [0, 1, 2, 'ipc']
});
waitForEnd(_pid, function () {
checkIsAdapterInstalled(function (error) {
if (error) console.error(error);
console.log('Adapter installed.');
if (cb) cb();
});
});
}
}
function waitForEnd(_pid, cb) {
if (!_pid) {
cb(-1, -1);
return;
}
_pid.on('exit', function (code, signal) {
if (_pid) {
_pid = null;
cb(code, signal);
}
});
_pid.on('close', function (code, signal) {
if (_pid) {
_pid = null;
cb(code, signal);
}
});
}
function installJsController(cb) {
console.log('installJsController...');
if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller') ||
!fs.existsSync(rootDir + 'tmp/' + appName + '-data')) {
// try to detect appName.js-controller in node_modules/appName.js-controller
// travis CI installs js-controller into node_modules
if (fs.existsSync(rootDir + 'node_modules/' + appName + '.js-controller')) {
console.log('installJsController: no js-controller => copy it from "' + rootDir + 'node_modules/' + appName + '.js-controller"');
// copy all
// stop controller
console.log('Stop controller if running...');
var _pid;
if (debug) {
// start controller
_pid = child_process.exec('node ' + appName + '.js stop', {
cwd: rootDir + 'node_modules/' + appName + '.js-controller',
stdio: [0, 1, 2]
});
} else {
_pid = child_process.fork(appName + '.js', ['stop'], {
cwd: rootDir + 'node_modules/' + appName + '.js-controller',
stdio: [0, 1, 2, 'ipc']
});
}
waitForEnd(_pid, function () {
// copy all files into
if (!fs.existsSync(rootDir + 'tmp')) fs.mkdirSync(rootDir + 'tmp');
if (!fs.existsSync(rootDir + 'tmp/node_modules')) fs.mkdirSync(rootDir + 'tmp/node_modules');
if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller')){
console.log('Copy js-controller...');
copyFolderRecursiveSync(rootDir + 'node_modules/' + appName + '.js-controller', rootDir + 'tmp/node_modules/');
}
console.log('Setup js-controller...');
var __pid;
if (debug) {
// start controller
_pid = child_process.exec('node ' + appName + '.js setup first --console', {
cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller',
stdio: [0, 1, 2]
});
} else {
__pid = child_process.fork(appName + '.js', ['setup', 'first', '--console'], {
cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller',
stdio: [0, 1, 2, 'ipc']
});
}
waitForEnd(__pid, function () {
checkIsControllerInstalled(function () {
// change ports for object and state DBs
var config = require(rootDir + 'tmp/' + appName + '-data/' + appName + '.json');
config.objects.port = 19001;
config.states.port = 19000;
fs.writeFileSync(rootDir + 'tmp/' + appName + '-data/' + appName + '.json', JSON.stringify(config, null, 2));
console.log('Setup finished.');
copyAdapterToController();
installAdapter(function () {
storeOriginalFiles();
if (cb) cb(true);
});
});
});
});
} else {
// check if port 9000 is free, else admin adapter will be added to running instance
var client = new require('net').Socket();
client.connect(9000, '127.0.0.1', function() {
console.error('Cannot initiate fisrt run of test, because one instance of application is running on this PC. Stop it and repeat.');
process.exit(0);
});
setTimeout(function () {
client.destroy();
if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller')) {
console.log('installJsController: no js-controller => install from git');
child_process.execSync('npm install https://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();
}
// read system.config object
var dataDir = rootDir + 'tmp/' + appName + '-data/';
var objs;
try {
objs = fs.readFileSync(dataDir + 'objects.json');
objs = JSON.parse(objs);
}
catch (e) {
console.log('ERROR reading/parsing system configuration. Ignore');
objs = {'system.config': {}};
}
if (!objs || !objs['system.config']) {
objs = {'system.config': {}};
}
if (cb) cb(objs['system.config']);
});
}
function startAdapter(objects, states, callback) {
if (adapterStarted) {
console.log('Adapter already started ...');
if (callback) callback(objects, states);
return;
}
adapterStarted = true;
console.log('startAdapter...');
if (fs.existsSync(rootDir + 'tmp/node_modules/' + pkg.name + '/' + pkg.main)) {
try {
if (debug) {
// start controller
pid = child_process.exec('node node_modules/' + pkg.name + '/' + pkg.main + ' --console silly', {
cwd: rootDir + 'tmp',
stdio: [0, 1, 2]
});
} else {
// start controller
pid = child_process.fork('node_modules/' + pkg.name + '/' + pkg.main, ['--console', 'silly'], {
cwd: rootDir + 'tmp',
stdio: [0, 1, 2, 'ipc']
});
}
} catch (error) {
console.error(JSON.stringify(error));
}
} else {
console.error('Cannot find: ' + rootDir + 'tmp/node_modules/' + pkg.name + '/' + pkg.main);
}
if (callback) callback(objects, states);
}
function startController(isStartAdapter, onObjectChange, onStateChange, callback) {
if (typeof isStartAdapter === 'function') {
callback = onStateChange;
onStateChange = onObjectChange;
onObjectChange = isStartAdapter;
isStartAdapter = true;
}
if (onStateChange === undefined) {
callback = onObjectChange;
onObjectChange = undefined;
}
if (pid) {
console.error('Controller is already started!');
} else {
console.log('startController...');
adapterStarted = false;
var isObjectConnected;
var isStatesConnected;
var Objects = require(rootDir + 'tmp/node_modules/' + appName + '.js-controller/lib/objects/objectsInMemServer');
objects = new Objects({
connection: {
"type" : "file",
"host" : "127.0.0.1",
"port" : 19001,
"user" : "",
"pass" : "",
"noFileCache": false,
"connectTimeout": 2000
},
logger: {
silly: function (msg) {
console.log(msg);
},
debug: function (msg) {
console.log(msg);
},
info: function (msg) {
console.log(msg);
},
warn: function (msg) {
console.warn(msg);
},
error: function (msg) {
console.error(msg);
}
},
connected: function () {
isObjectConnected = true;
if (isStatesConnected) {
console.log('startController: started!');
if (isStartAdapter) {
startAdapter(objects, states, callback);
} else {
if (callback) {
callback(objects, states);
callback = null;
}
}
}
},
change: onObjectChange
});
// Just open in memory DB itself
var States = require(rootDir + 'tmp/node_modules/' + appName + '.js-controller/lib/states/statesInMemServer');
states = new States({
connection: {
type: 'file',
host: '127.0.0.1',
port: 19000,
options: {
auth_pass: null,
retry_max_delay: 15000
}
},
logger: {
silly: function (msg) {
console.log(msg);
},
debug: function (msg) {
console.log(msg);
},
info: function (msg) {
console.log(msg);
},
warn: function (msg) {
console.log(msg);
},
error: function (msg) {
console.log(msg);
}
},
connected: function () {
isStatesConnected = true;
if (isObjectConnected) {
console.log('startController: started!!');
if (isStartAdapter) {
startAdapter(objects, states, callback);
} else {
if (callback) {
callback(objects, states);
callback = null;
}
}
}
},
change: onStateChange
});
}
}
function stopAdapter(cb) {
if (!pid) {
console.error('Controller is not running!');
if (cb) {
setTimeout(function () {
cb(false);
}, 0);
}
} else {
adapterStarted = false;
pid.on('exit', function (code, signal) {
if (pid) {
console.log('child process terminated due to receipt of signal ' + signal);
if (cb) cb();
pid = null;
}
});
pid.on('close', function (code, signal) {
if (pid) {
if (cb) cb();
pid = null;
}
});
pid.kill('SIGTERM');
}
}
function _stopController() {
if (objects) {
objects.destroy();
objects = null;
}
if (states) {
states.destroy();
states = null;
}
}
function stopController(cb) {
var timeout;
if (objects) {
console.log('Set system.adapter.' + pkg.name + '.0');
objects.setObject('system.adapter.' + pkg.name + '.0', {
common:{
enabled: false
}
});
}
stopAdapter(function () {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
_stopController();
if (cb) {
cb(true);
cb = null;
}
});
timeout = setTimeout(function () {
timeout = null;
console.log('child process NOT terminated');
_stopController();
if (cb) {
cb(false);
cb = null;
}
pid = null;
}, 5000);
}
// Setup the adapter
function setAdapterConfig(common, native, instance) {
var objects = JSON.parse(fs.readFileSync(rootDir + 'tmp/' + appName + '-data/objects.json').toString());
var id = 'system.adapter.' + adapterName.split('.').pop() + '.' + (instance || 0);
if (common) objects[id].common = common;
if (native) objects[id].native = native;
fs.writeFileSync(rootDir + 'tmp/' + appName + '-data/objects.json', JSON.stringify(objects));
}
// Read config of the adapter
function getAdapterConfig(instance) {
var objects = JSON.parse(fs.readFileSync(rootDir + 'tmp/' + appName + '-data/objects.json').toString());
var id = 'system.adapter.' + adapterName.split('.').pop() + '.' + (instance || 0);
return objects[id];
}
if (typeof module !== undefined && module.parent) {
module.exports.getAdapterConfig = getAdapterConfig;
module.exports.setAdapterConfig = setAdapterConfig;
module.exports.startController = startController;
module.exports.stopController = stopController;
module.exports.setupController = setupController;
module.exports.stopAdapter = stopAdapter;
module.exports.startAdapter = startAdapter;
module.exports.installAdapter = installAdapter;
module.exports.appName = appName;
module.exports.adapterName = adapterName;
module.exports.adapterStarted = adapterStarted;
}

@ -0,0 +1,140 @@
/* jshint -W097 */// jshint strict:false
/*jslint node: true */
var expect = require('chai').expect;
var setup = require(__dirname + '/lib/setup');
var objects = null;
var states = null;
var onStateChanged = null;
var onObjectChanged = null;
var sendToID = 1;
var adapterShortName = setup.adapterName.substring(setup.adapterName.indexOf('.')+1);
function checkConnectionOfAdapter(cb, counter) {
counter = counter || 0;
console.log('Try check #' + counter);
if (counter > 30) {
if (cb) cb('Cannot check connection');
return;
}
states.getState('system.adapter.' + adapterShortName + '.0.alive', function (err, state) {
if (err) console.error(err);
if (state && state.val) {
if (cb) cb();
} else {
setTimeout(function () {
checkConnectionOfAdapter(cb, counter + 1);
}, 1000);
}
});
}
function checkValueOfState(id, value, cb, counter) {
counter = counter || 0;
if (counter > 20) {
if (cb) cb('Cannot check value Of State ' + id);
return;
}
states.getState(id, function (err, state) {
if (err) console.error(err);
if (value === null && !state) {
if (cb) cb();
} else
if (state && (value === undefined || state.val === value)) {
if (cb) cb();
} else {
setTimeout(function () {
checkValueOfState(id, value, cb, counter + 1);
}, 500);
}
});
}
function sendTo(target, command, message, callback) {
onStateChanged = function (id, state) {
if (id === 'messagebox.system.adapter.test.0') {
callback(state.message);
}
};
states.pushMessage('system.adapter.' + target, {
command: command,
message: message,
from: 'system.adapter.test.0',
callback: {
message: message,
id: sendToID++,
ack: false,
time: (new Date()).getTime()
}
});
}
describe('Test ' + adapterShortName + ' adapter', function() {
before('Test ' + adapterShortName + ' adapter: Start js-controller', function (_done) {
this.timeout(600000); // because of first install from npm
setup.setupController(function () {
var config = setup.getAdapterConfig();
// enable adapter
config.common.enabled = true;
config.common.loglevel = 'debug';
//config.native.dbtype = 'sqlite';
setup.setAdapterConfig(config.common, config.native);
setup.startController(true, function(id, obj) {}, function (id, state) {
if (onStateChanged) onStateChanged(id, state);
},
function (_objects, _states) {
objects = _objects;
states = _states;
_done();
});
});
});
/*
ENABLE THIS WHEN ADAPTER RUNS IN DEAMON MODE TO CHECK THAT IT HAS STARTED SUCCESSFULLY
*/
it('Test ' + adapterShortName + ' adapter: Check if adapter started', function (done) {
this.timeout(60000);
checkConnectionOfAdapter(function (res) {
if (res) console.log(res);
expect(res).not.to.be.equal('Cannot check connection');
objects.setObject('system.adapter.test.0', {
common: {
},
type: 'instance'
},
function () {
states.subscribeMessage('system.adapter.test.0');
done();
});
});
});
/**/
/*
PUT YOUR OWN TESTS HERE USING
it('Testname', function ( done) {
...
});
You can also use "sendTo" method to send messages to the started adapter
*/
after('Test ' + adapterShortName + ' adapter: Stop js-controller', function (done) {
this.timeout(10000);
setup.stopController(function (normalTerminated) {
console.log('Adapter normal terminated: ' + normalTerminated);
done();
});
});
});

@ -0,0 +1,91 @@
/* jshint -W097 */
/* jshint strict:false */
/* jslint node: true */
/* jshint expr: true */
var expect = require('chai').expect;
var fs = require('fs');
describe('Test package.json and io-package.json', function() {
it('Test package files', function (done) {
console.log();
var fileContentIOPackage = fs.readFileSync(__dirname + '/../io-package.json', 'utf8');
var ioPackage = JSON.parse(fileContentIOPackage);
var fileContentNPMPackage = fs.readFileSync(__dirname + '/../package.json', 'utf8');
var npmPackage = JSON.parse(fileContentNPMPackage);
expect(ioPackage).to.be.an('object');
expect(npmPackage).to.be.an('object');
expect(ioPackage.common.version, 'ERROR: Version number in io-package.json needs to exist').to.exist;
expect(npmPackage.version, 'ERROR: Version number in package.json needs to exist').to.exist;
expect(ioPackage.common.version, 'ERROR: Version numbers in package.json and io-package.json needs to match').to.be.equal(npmPackage.version);
if (!ioPackage.common.news || !ioPackage.common.news[ioPackage.common.version]) {
console.log('WARNING: No news entry for current version exists in io-package.json, no rollback in Admin possible!');
console.log();
}
expect(npmPackage.author, 'ERROR: Author in package.json needs to exist').to.exist;
expect(ioPackage.common.authors, 'ERROR: Authors in io-package.json needs to exist').to.exist;
if (ioPackage.common.name.indexOf('template') !== 0) {
if (Array.isArray(ioPackage.common.authors)) {
expect(ioPackage.common.authors.length, 'ERROR: Author in io-package.json needs to be set').to.not.be.equal(0);
if (ioPackage.common.authors.length === 1) {
expect(ioPackage.common.authors[0], 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name <my@email.com>');
}
}
else {
expect(ioPackage.common.authors, 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name <my@email.com>');
}
}
else {
console.log('WARNING: Testing for set authors field in io-package skipped because template adapter');
console.log();
}
expect(fs.existsSync(__dirname + '/../README.md'), 'ERROR: README.md needs to exist! Please create one with description, detail information and changelog. English is mandatory.').to.be.true;
if (!ioPackage.common.titleLang || typeof ioPackage.common.titleLang !== 'object') {
console.log('WARNING: titleLang is not existing in io-package.json. Please add');
console.log();
}
if (
ioPackage.common.title.indexOf('iobroker') !== -1 ||
ioPackage.common.title.indexOf('ioBroker') !== -1 ||
ioPackage.common.title.indexOf('adapter') !== -1 ||
ioPackage.common.title.indexOf('Adapter') !== -1
) {
console.log('WARNING: title contains Adapter or ioBroker. It is clear anyway, that it is adapter for ioBroker.');
console.log();
}
if (ioPackage.common.name.indexOf('vis-') !== 0) {
if (!ioPackage.common.materialize || !fs.existsSync(__dirname + '/../admin/index_m.html') || !fs.existsSync(__dirname + '/../gulpfile.js')) {
console.log('WARNING: Admin3 support is missing! Please add it');
console.log();
}
if (ioPackage.common.materialize) {
expect(fs.existsSync(__dirname + '/../admin/index_m.html'), 'Admin3 support is enabled in io-package.json, but index_m.html is missing!').to.be.true;
}
}
var licenseFileExists = fs.existsSync(__dirname + '/../LICENSE');
var fileContentReadme = fs.readFileSync(__dirname + '/../README.md', 'utf8');
if (fileContentReadme.indexOf('## Changelog') === -1) {
console.log('Warning: The README.md should have a section ## Changelog');
console.log();
}
expect((licenseFileExists || fileContentReadme.indexOf('## License') !== -1), 'A LICENSE must exist as LICENSE file or as part of the README.md').to.be.true;
if (!licenseFileExists) {
console.log('Warning: The License should also exist as LICENSE file');
console.log();
}
if (fileContentReadme.indexOf('## License') === -1) {
console.log('Warning: The README.md should also have a section ## License to be shown in Admin3');
console.log();
}
done();
});
});
Loading…
Cancel
Save