Initial commit
This commit is contained in:
commit
b13c3978ce
9
.npmignore
Normal file
9
.npmignore
Normal 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
23
.travis.yml
Normal 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
9
LICENSE
Normal 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
54
README.md
Normal 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
217
admin/blockly.js
Normal 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;
|
||||
};
|
19
admin/i18n/cn/translations.json
Normal file
19
admin/i18n/cn/translations.json
Normal 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!"
|
||||
}
|
19
admin/i18n/en/translations.json
Normal file
19
admin/i18n/en/translations.json
Normal 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
168
admin/index.html
Normal 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
211
admin/index_m.html
Normal 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
BIN
admin/pushover.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.7 KiB |
23
admin/words.js
Normal file
23
admin/words.js
Normal 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
25
appveyor.yml
Normal 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
394
gulpfile.js
Normal 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
BIN
img/Screen0.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 63 KiB |
BIN
img/Screen1.png
Normal file
BIN
img/Screen1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 75 KiB |
BIN
img/Screen3.png
Normal file
BIN
img/Screen3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 50 KiB |
49
io-package.json
Normal file
49
io-package.json
Normal 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
83
lib/utils.js
Normal 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
265
main.js
Normal 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_id,mobiles,content,sign,serial,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
33
package.json
Normal 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
728
test/lib/setup.js
Normal 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
140
test/testAdapter.js
Normal 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
91
test/testPackageFiles.js
Normal 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();
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user