Initial commit

This commit is contained in:
hl 2019-07-27 17:06:40 +08:00
commit b13c3978ce
23 changed files with 2560 additions and 0 deletions

9
.npmignore Normal file
View File

@ -0,0 +1,9 @@
gulpfile.js
tasks
tmp
test
.travis.yml
appveyor.yml
admin/i18n
iob_npm.done
package-lock.json

23
.travis.yml Normal file
View File

@ -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/yunkong2/yunkong2.js-controller/tarball/master --production'
env:
- CXX=g++-4.8
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8

9
LICENSE Normal file
View File

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

54
README.md Normal file
View File

@ -0,0 +1,54 @@
![Logo](admin/pushover.png)
# yunkong2 pushover Adapter
==============
[![NPM version](http://img.shields.io/npm/v/yunkong2.pushover.svg)](https://www.npmjs.com/package/yunkong2.pushover)
[![Downloads](https://img.shields.io/npm/dm/yunkong2.pushover.svg)](https://www.npmjs.com/package/yunkong2.pushover)
[![NPM](https://nodei.co/npm/yunkong2.pushover.png?downloads=true)](https://nodei.co/npm/yunkong2.pushover/)
Send pushover notifications from yunkong2.
## Usage
To send notification from ScriptEngine just write:
```javascript
//短信发送
sendTo("pushover.0", {
phone: '15273732255',
message: '室内温度高于40度报警并打开电磁阀风机室外水位不高的情况下及冷却循环装置'
});
// 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
});
```

217
admin/blockly.js Normal file
View File

@ -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/yunkong2/yunkong2.pushover/blob/master/README.md', 'de': 'https://github.com/yunkong2/yunkong2.pushover/blob/master/README.md', 'ru': 'https://github.com/yunkong2/yunkong2.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;
};

View File

@ -0,0 +1,19 @@
{
"Pushover adapter settings": "Pushover adapter settings",
"Pushover settings": "Pushover settings",
"Group Key": "Group Key",
"Token": "Token",
"Title": "Title",
"Sound": "Sound",
"Priority": "Priority",
"Test": "Test",
"Notification settings": "Notification settings",
"Group Key of Delivery Group": "Group Key of Delivery Group",
"API Token/Key of Application": "API Token/Key of Application",
"quiet": "quiet",
"default": "default",
"high-priority": "high-priority",
"Enable first the adapter to test notification.": "Enable first the adapter to test notification.",
"First save the settings": "First save the settings",
"Check the log or your pushover app!": "Check the log or your pushover app!"
}

View File

@ -0,0 +1,19 @@
{
"Pushover adapter settings": "Pushover adapter settings",
"Pushover settings": "Pushover settings",
"Group Key": "Group Key",
"Token": "Token",
"Title": "Title",
"Sound": "Sound",
"Priority": "Priority",
"Test": "Test",
"Notification settings": "Notification settings",
"Group Key of Delivery Group": "Group Key of Delivery Group",
"API Token/Key of Application": "API Token/Key of Application",
"quiet": "quiet",
"default": "default",
"high-priority": "high-priority",
"Enable first the adapter to test notification.": "Enable first the adapter to test notification.",
"First save the settings": "First save the settings",
"Check the log or your pushover app!": "Check the log or your pushover app!"
}

168
admin/index.html Normal file
View File

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

211
admin/index_m.html Normal file
View File

@ -0,0 +1,211 @@
<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_name" type="text" size="50" class="value"/>
<label for="user_name" class="translate">账号</label>
<span class="translate">短信发送账号</span>
</div>
</div>
<div class="row">
<div class="input-field col s12 m6 l4 xl3">
<input id="user_passwd" type="text" size="50" class="value"/>
<label for="user_passwd" class="translate">密码</label>
<span class="translate">短信发送密码</span>
</div>
</div>
<div class="row">
<div class="input-field col s12 m6 l4 xl3">
<input id="ec_name" type="text" size="50" class="value"/>
<label for="ec_name" class="translate">单位名称</label>
<span class="translate">注册单位名称</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 class="row">
<div class="input-field col s12 m6 l4 xl3">
<input id="smsLoginUrl" type="text" size="50" class="value"/>
<label for="smsLoginUrl" class="translate">smsLoginUrl:</label>
<span class="translate">短信服务登录地址</span>
</div>
</div>
<div class="row">
<div class="input-field col s12 m6 l4 xl3">
<input id="smsUrl" type="text" size="50" class="value"/>
<label for="smsUrl" class="translate">smsUrl:</label>
<span class="translate">短信服务发送地址</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>

BIN
admin/pushover.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

23
admin/words.js Normal file
View File

@ -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", "cn": "Pushover adapter settings"},
"Pushover settings": { "en": "Pushover settings", "cn": "Pushover settings"},
"Group Key": { "en": "Group Key", "cn": "Group Key"},
"Token": { "en": "Token", "cn": "Token"},
"Title": { "en": "Title", "cn": "Title"},
"Sound": { "en": "Sound", "cn": "Sound"},
"Priority": { "en": "Priority", "cn": "Priority"},
"Test": { "en": "Test", "cn": "Test"},
"Notification settings": { "en": "Notification settings", "cn": "Notification settings"},
"Group Key of Delivery Group": { "en": "Group Key of Delivery Group", "cn": "Group Key of Delivery Group"},
"API Token/Key of Application": { "en": "API Token/Key of Application", "cn": "API Token/Key of Application"},
"quiet": { "en": "quiet", "cn": "quiet"},
"default": { "en": "default", "cn": "default"},
"high-priority": { "en": "high-priority", "cn": "high-priority"},
"Enable first the adapter to test notification.": {"en": "Enable first the adapter to test notification.", "cn": "Enable first the adapter to test notification."},
"First save the settings": { "en": "First save the settings", "cn": "First save the settings"},
"Check the log or your pushover app!": { "en": "Check the log or your pushover app!", "cn": "Check the log or your pushover app!"}
};

25
appveyor.yml Normal file
View File

@ -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/yunkong2/yunkong2.js-controller/tarball/master --production'
test_script:
- echo %cd%
- node --version
- npm --version
- npm test
build: 'off'

394
gulpfile.js Normal file
View File

@ -0,0 +1,394 @@
'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: {},
cn: {}
};
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']);

BIN
img/Screen0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
img/Screen1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

BIN
img/Screen3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

49
io-package.json Normal file
View File

@ -0,0 +1,49 @@
{
"common": {
"name": "pushover",
"version": "0.1.0",
"title": "报警通知",
"desc": {
"en": "This adapter allows to send pushover notifications from yunkong2",
"cn": "云控报警通知"
},
"authors": [
"spacen <zhongjin1971@gmail.com>"
],
"license": "MIT",
"platform": "Node.js",
"mode": "daemon",
"availableModes": [
"daemon",
"subscribe"
],
"loglevel": "info",
"messagebox": true,
"materialize": true,
"subscribe": "messagebox",
"readme": "https://git.spacen.net/yunkong2/yunkong2.pushover/raw/master/README.md",
"wakeup": true,
"enabled": true,
"icon": "pushover.png",
"keywords": [
"notification",
"pushover",
"message"
],
"extIcon": "https://git.spacen.net/yunkong2/yunkong2.pushover/raw/master/admin/pushover.png",
"type": "messaging",
"blockly": true
},
"native": {
"user_name": "admin",
"user_passwd": "bdDm@648",
"ec_name": "华翔翔能(湖南)能源科技有限公司",
"token": "xxxxx",
"title": "yunkong2",
"sound": "",
"priority": 0,
"smsLoginUrl":"http://mas.ecloud.10086.cn/app/http/authorize",
"smsUrl" : "http://mas.ecloud.10086.cn/app/http/sendSms"
},
"objects": []
}

83
lib/utils.js Normal file
View File

@ -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 = [
'yunkong2.js-controller',
'yunkong2.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;

265
main.js Normal file
View File

@ -0,0 +1,265 @@
/**
*
* yunkong2 pushover Adapter
*
* (c) 2014-2018 bluefox
*
* MIT License
*
*/
/* jshint -W097 */// jshint strict:false
/*jslint node: true */
'use strict';
var md5=require("md5")
const utils = require(__dirname + '/lib/utils'); // Get common adapter utils
const Pushover = require('pushover-notifications');
const request = require('request');
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 createObject() {
//adapter.setObject('config.0.voltage.50002', json, function (err) {
// if (err) log('Cannot write object: ' + err);
//});
adapter.setObject('config.0.voltage.50002', {
custom: {
min: 320,
max: 400,
interval: 5,
count: 2
},
common: {
name: "50002",
role: "",
type: "array",
desc: "pushover创建",
read: true,
write: true
}
}, function () {
adapter.log.info('Save ' + userdataDir + 'pushover.json');
});
}
function main() {
// Adapter is started only if some one writes into "system.adapter.pushover.X.messagebox" new value
//createObject();
processMessages();
stop();
}
function sendNotification(message, callback) {
if (!message) message = {};
if (!pushover) {
if (adapter.config.user_name && adapter.config.token) {
pushover = new Pushover({
user: adapter.config.user_name,
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));
adapter.log.info('adapter.config.smsUrl' + adapter.config.smsUrl);
adapter.log.info('adapter.config.smsLoginUrl' + adapter.config.smsLoginUrl);
var mobiles = message.phone;
var content = message.message;
smsLogin(mobiles,content);
/*
request(adapter.config.smsUrl, function (error, response, body) {
adapter.log.info('response.statusCode' + response.statusCode);
adapter.log.info('body' + body);
if (error || response.statusCode !== 200) {
adapter.log.error(error || response.statusCode);
} else {
// try to parse answer
try {
var data = JSON.parse(body);
// do something with data
adapter.log.info(JSON.parse(data));
} catch (e) {
adapter.log.error('Cannot parse answer');
}
}
});
*/
/*
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;
}
});
*/
}
// 短信发送登录
function smsLogin(mobiles,content)
{
var url = "http://mas.ecloud.10086.cn/app/http/authorize?ec_name=%E5%8D%8E%E7%BF%94%E7%BF%94%E8%83%BD%EF%BC%88%E6%B9%96%E5%8D%97%EF%BC%89%E8%83%BD%E6%BA%90%E7%A7%91%E6%8A%80%E6%9C%89%E9%99%90%E5%85%AC%E5%8F%B8&user_name=admin&user_passwd=bdDm@648";
console.log('url:' + url);
request(url, function (error, response, body) {
adapter.log.info('error:' + error);
adapter.log.info('statusCode:' + response && response.statusCode);
adapter.log.info('body' + body);
if (!error && response.statusCode == 200) {
// 请求成功的处理逻辑
try {
var data = JSON.parse(body);
// do something with data
adapter.log.info(data.mas_user_id);
adapter.log.info(data.access_token);
var sign = 'uEbOVoPZ';
smsMess(data.mas_user_id, mobiles, content, sign, '', data.access_token);
} catch (e) {
adapter.log.info(e);
}
}
});
}
//发送短信
function smsMess(mas_user_id,mobiles,content,sign,serial,access_token) {
// API输入参数签名结果签名算法将mas_user_idmobilescontentsignserial,access_token按照顺序拼接然后通过MD5+HEX计算后得出的值
var mac = mas_user_id + mobiles + content + sign + serial + access_token;
adapter.log.info('mac:' + mac);
mac = md5(mac);
adapter.log.info('mac-md5:' + mac);
mac = mac.toUpperCase(); //再把密文中的英文母全部转为大写
adapter.log.info('mac-upper:' + mac);
var form = {
mas_user_id:mas_user_id,
mobiles:mobiles,
content:content,
sign:sign,
serial:serial,
mac:mac
};
var url = 'http://mas.ecloud.10086.cn/app/http/sendSms';
request.post({url:url, form:form}, function(error, response, body) {
if (!error && response.statusCode == 200) {
adapter.log.info(body);
}
})
}

33
package.json Normal file
View File

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

728
test/lib/setup.js Normal file
View File

@ -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/yunkong2-data/sqlite';
var files;
try {
if (fs.existsSync(dirPath)) {
console.log('Clear sqlite DB...');
files = fs.readdirSync(dirPath);
} else {
console.log('Create controller log directory...');
files = [];
fs.mkdirSync(dirPath);
}
} catch(e) {
console.error('Cannot read "' + dirPath + '"');
return;
}
if (files.length > 0) {
try {
for (var i = 0; i < files.length; i++) {
var filePath = dirPath + '/' + files[i];
fs.unlinkSync(filePath);
}
console.log('Clear sqlite DB');
} catch (err) {
console.error('cannot clear DB: ' + err);
}
}
}
function setupController(cb) {
installJsController(function (isInited) {
clearControllerLog();
clearDB();
if (!isInited) {
restoreOriginalFiles();
copyAdapterToController();
}
// 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;
}

140
test/testAdapter.js Normal file
View File

@ -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();
});
});
});

91
test/testPackageFiles.js Normal file
View File

@ -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('yunkong2') !== -1 ||
ioPackage.common.title.indexOf('yunkong2') !== -1 ||
ioPackage.common.title.indexOf('adapter') !== -1 ||
ioPackage.common.title.indexOf('Adapter') !== -1
) {
console.log('WARNING: title contains Adapter or yunkong2. It is clear anyway, that it is adapter for yunkong2.');
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();
});
});