mirror of
https://github.com/node-red/node-red-nodes.git
synced 2023-10-10 13:36:58 +02:00
Merge pull request #95 from dsundberg/pushbullet-enhance
Thanks @dsundberg. Updated pushbullet node with all push types, added new "pushbullet in" t...
This commit is contained in:
commit
4742e19202
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
Copyright 2013 IBM Corp.
|
Copyright 2013,2015 IBM Corp.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -14,94 +14,353 @@
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
<!-- PUSHBULLET CONFIG -->
|
||||||
|
|
||||||
|
<script type="text/x-red" data-template-name="pushbullet-config">
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-config-input-name"><i class="fa fa-tag"></i> Name</label>
|
||||||
|
<input type="text" id="node-config-input-name" placeholder="Name">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-config-input-apikey"><i class="fa fa-lock"></i> API-key</label>
|
||||||
|
<input type="password" id="node-config-input-apikey">
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
RED.nodes.registerType('pushbullet-config', {
|
||||||
|
category: 'config',
|
||||||
|
color: "rgb(218, 196, 180)",
|
||||||
|
defaults: {
|
||||||
|
name: {value: ""}
|
||||||
|
},
|
||||||
|
credentials: {
|
||||||
|
apikey: {type: "password"}
|
||||||
|
},
|
||||||
|
label: function() {
|
||||||
|
return this.name || "Pushbullet config";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- PUSHBULLET OUT -->
|
||||||
|
|
||||||
<script type="text/x-red" data-template-name="pushbullet">
|
<script type="text/x-red" data-template-name="pushbullet">
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label for="node-input-title"><i class="fa fa-flag"></i> Title</label>
|
<label for="node-input-config"><i class="fa fa-user"></i> Config</label>
|
||||||
<input type="text" id="node-input-title" placeholder="Node-RED">
|
<input type="text" id="node-input-config">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-deviceid"><i class="fa fa-mobile"></i> Device ID</label>
|
||||||
|
<select id="node-input-deviceid">
|
||||||
|
<option value="">All</option>
|
||||||
|
<option value="_msg_">- set from msg.deviceid -</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label for="node-input-chan"><i class="fa fa-random"></i> Channel</label>
|
<label for="node-input-chan"><i class="fa fa-random"></i> Channel</label>
|
||||||
<input type="text" id="node-input-chan" placeholder="channel name">
|
<input type="text" id="node-input-chan" placeholder="channel name">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label for="node-config-input-deviceid"><i class="fa fa-user"></i> Device ID</label>
|
<label for="node-input-pushtype"><i class="fa fa-dot-circle-o"></i> Type</label>
|
||||||
<input type="text" id="node-config-input-deviceid">
|
<select id="node-input-pushtype">
|
||||||
|
<optgroup label="Push types">
|
||||||
|
<option value="address">Address</option>
|
||||||
|
<option value="file">File</option>
|
||||||
|
<option value="link">Link</option>
|
||||||
|
<option value="list">List</option>
|
||||||
|
<option value="note">Note</option>
|
||||||
|
<option value="">- set from msg.pushtype -</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="Actions">
|
||||||
|
<option value="delete">Delete push</option>
|
||||||
|
<option value="dismissal">Dismiss push</option>
|
||||||
|
<option value="updatelist">Update list</option>
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label for="node-config-input-pushkey"><i class="fa fa-lock"></i> API Key</label>
|
<label for="node-input-title"><i class="fa fa-random"></i> Title</label>
|
||||||
<input type="password" id="node-config-input-pushkey">
|
<input type="text" id="node-input-title" placeholder="Node-RED">
|
||||||
</div>
|
</div>
|
||||||
<br/>
|
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
||||||
<input type="text" id="node-input-name" placeholder="Name">
|
<input type="text" id="node-input-name" placeholder="Name">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-tips" id="node-tip"><b>Note:</b> Using credentials from global pushkey.js file.</div>
|
<div class="form-tips" id="pushbullet-migration-info" style="display: none;">
|
||||||
|
<p><i class="fa fa-random"></i> Configuration node has been migrated, please click Ok and then re-deploy flow to save settings.</p>
|
||||||
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script type="text/x-red" data-help-name="pushbullet">
|
<script type="text/x-red" data-help-name="pushbullet">
|
||||||
<p>Uses PushBullet to push the <b>msg.payload</b> to a device that has the PushBullet app installed.</p>
|
<p>Uses PushBullet to push <code>msg.payload</code> to a device that has the PushBullet app installed.</p>
|
||||||
<p>Optionally uses <b>msg.topic</b> to set the title, if not already set in the properties.</p>
|
<p>Optionally uses <code>msg.topic</code> to set the title, if not already set in the properties.</p>
|
||||||
<p>You need to configure both your <i>API key</i> and the target <i>device ID</i>.
|
<p>Optionally uses <code>msg.pushtype</code> to set the type of the push, if not already set in the properties.</p>
|
||||||
The device ID may be passed in as <b>msg.deviceid</b>.</p>
|
<p>Optionally uses <code>msg.deviceid</code> to set the device ID, if not already set in the properties.</p>
|
||||||
<p>You can also push to any channels that you own either configured or via <b>msg.channel</b>.</p>
|
<p>You can also push to any channels that you own either configured or via <code>msg.channel</code>.</p>
|
||||||
The old method of storing your credentials in the pushkey.js file in the directory <b>above</b> /node-red is deprecated.</p>
|
<p>The node can also <i>dismiss</i> and <i>delete</i> and <i>update</i> items in a pushed list. In this case <code>msg.data.iden</code> must be set to a valid push id, if <code>msg</code> originates from the Pushbullet input node this value is already set.</p>
|
||||||
<p>The deviceid can be found by clicking on your required device on the <a href="https://www.pushbullet.com/">PushBullet website</a>, once you have logged in.</p>
|
<p>The old method of storing your credentials in the pushkey.js file in the directory above /node-red is deprecated.</p>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
RED.nodes.registerType('pushbullet',{
|
RED.nodes.registerType('pushbullet',{
|
||||||
category: 'output',
|
category: 'social-output',
|
||||||
defaults: {
|
defaults: {
|
||||||
|
config: {type: "pushbullet-config", required: true},
|
||||||
|
pushtype: {value:"note"},
|
||||||
title: {value:""},
|
title: {value:""},
|
||||||
chan: {value:"" },
|
chan: {value:"" },
|
||||||
name: {value:""}
|
name: {value:""}
|
||||||
},
|
},
|
||||||
|
credentials: {
|
||||||
|
deviceid: {value: ""},
|
||||||
|
pushkey: {value: ""}
|
||||||
|
},
|
||||||
color:"#a7c9a0",
|
color:"#a7c9a0",
|
||||||
inputs:1,
|
inputs:1,
|
||||||
outputs:0,
|
outputs:0,
|
||||||
icon: "bullet.png",
|
icon: "bullet.png",
|
||||||
align: "right",
|
align: "right",
|
||||||
label: function() {
|
label: function() {
|
||||||
return this.name||this.title||"pushbullet";
|
var self = this;
|
||||||
|
function getName(deviceid) {
|
||||||
|
if(!self.devicename && deviceid && self.config) {
|
||||||
|
$.getJSON('pushbullet/'+self.config+'/devices', function(data) {
|
||||||
|
for(var i=0; i<data.length; i++) {
|
||||||
|
if(data[i].iden === deviceid) {
|
||||||
|
self.devicename = data[i].nickname;
|
||||||
|
self.dirty = true;
|
||||||
|
RED.view.redraw();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
getName(this.credentials.deviceid);
|
||||||
|
}
|
||||||
|
catch(err) {
|
||||||
|
$.getJSON('credentials/pushbullet/'+this.id, function(data) {
|
||||||
|
getName(data.deviceid);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.name||this.devicename||this.title||"pushbullet";
|
||||||
|
},
|
||||||
|
labelStyle: function() {
|
||||||
|
return this.name?"node_label_italic":"";
|
||||||
|
},
|
||||||
|
oneditsave: function() {
|
||||||
|
this.devicename = undefined;
|
||||||
|
if(this.migrationData) {
|
||||||
|
$.ajax('pushbullet/'+this.id+'/migrate?save=true');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
oneditprepare: function() {
|
||||||
|
var node = this, ddConfig = $('#node-input-config'), ddDevice = $('#node-input-deviceid'), ddPushtype = $('#node-input-pushtype');
|
||||||
|
|
||||||
|
function updateDeviceList() {
|
||||||
|
if(!ddConfig.val()) {
|
||||||
|
ddDevice.children().remove();
|
||||||
|
ddDevice.append('<option value="">All</option>')
|
||||||
|
ddDevice.append('<option value="_msg_">- set from msg.deviceid -</option>')
|
||||||
|
var currentDevice;
|
||||||
|
if(node.credentials) {
|
||||||
|
currentDevice = node.credentials.deviceid;
|
||||||
|
}
|
||||||
|
if(currentDevice && currentDevice !== "_msg_") {
|
||||||
|
ddDevice.append('<option value="'+currentDevice+'">'+currentDevice+'</option>');
|
||||||
|
ddDevice.val(currentDevice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var config = RED.nodes.node(ddConfig.val()),
|
||||||
|
url = 'pushbullet/'+ddConfig.val()+'/devices';
|
||||||
|
if(config && config.credentials && config.credentials.apikey) {
|
||||||
|
url += '?apikey='+config.credentials.apikey;
|
||||||
|
}
|
||||||
|
$.getJSON(url, function(data) {
|
||||||
|
ddDevice.children().remove();
|
||||||
|
ddDevice.append('<option value="">All</option>')
|
||||||
|
ddDevice.append('<option value="_msg_">- set from msg.deviceid -</option>')
|
||||||
|
var currentDevice, addCurrent = true;
|
||||||
|
if(node.credentials) {
|
||||||
|
currentDevice = node.credentials.deviceid;
|
||||||
|
}
|
||||||
|
for(var i=0; i<data.length; i++) {
|
||||||
|
var dev = data[i];
|
||||||
|
ddDevice.append('<option value="'+dev.iden+'">'+dev.nickname+' ('+dev.kind+')</option>');
|
||||||
|
if(dev.iden === currentDevice) {
|
||||||
|
addCurrent = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(currentDevice) {
|
||||||
|
if(addCurrent && currentDevice !== "_msg_") {
|
||||||
|
ddDevice.append('<option value="'+currentDevice+'">'+currentDevice+'</option>');
|
||||||
|
}
|
||||||
|
ddDevice.val(currentDevice);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkMigration(cb) {
|
||||||
|
$.getJSON('pushbullet/'+node.id+'/migrate', function(data) {
|
||||||
|
var showMigration = false;
|
||||||
|
node.migrationData = data.config;
|
||||||
|
if(data.migrated) {
|
||||||
|
if(data.config) {
|
||||||
|
var configId = data.config;
|
||||||
|
var configType = 'pushbullet-config';
|
||||||
|
var configTypeDef = RED.nodes.getType(configType);
|
||||||
|
RED.nodes.add({
|
||||||
|
type: configType,
|
||||||
|
id: configId,
|
||||||
|
name: "Imported",
|
||||||
|
users: [node.id],
|
||||||
|
label: configTypeDef.label,
|
||||||
|
_def: configTypeDef,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(node.credentials) {
|
||||||
|
if(node.credentials.pushkey) {
|
||||||
|
if(ddConfig.find('option[value="'+data.config+'"]').length === 0) {
|
||||||
|
ddConfig.append('<option value="'+data.config+'">Imported</option>');
|
||||||
|
}
|
||||||
|
ddConfig.val(data.config);
|
||||||
|
showMigration = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ddConfig.val("_ADD_");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(node.credentials.deviceid) {
|
||||||
|
ddDevice.append('<option value="'+node.credentials.deviceid+'">Imported</option>');
|
||||||
|
ddDevice.val(node.credentials.deviceid);
|
||||||
|
showMigration = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ddDevice.val("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(showMigration) {
|
||||||
|
ddPushtype.val("note");
|
||||||
|
node.dirty = true;
|
||||||
|
$('#pushbullet-migration-info').show();
|
||||||
|
}
|
||||||
|
if(cb) {
|
||||||
|
cb(showMigration);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
checkMigration(function(migrated) {
|
||||||
|
if(!migrated) {
|
||||||
|
ddConfig.change(function() {
|
||||||
|
if(ddConfig.val() && ddConfig.val() !== "_ADD_") {
|
||||||
|
updateDeviceList();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- PUSHBULLET IN -->
|
||||||
|
|
||||||
|
<script type="text/x-red" data-template-name="pushbullet in">
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-config"><i class="fa fa-user"></i> Config</label>
|
||||||
|
<input type="text" id="node-input-config" placeholder="Node-RED">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-filters"><i class="fa fa-mobile"></i> Device filter</label>
|
||||||
|
<select id="node-input-filters" style="width: 60%;" multiple="multiple"></select>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
||||||
|
<input type="text" id="node-input-name" placeholder="Name">
|
||||||
|
</div>
|
||||||
|
<div class="form-tips">
|
||||||
|
<i class="fa fa-mobile"></i> Device filter list is multi-selectable. No selections means no filtering, i.e. all pushes are emitted.
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/x-red" data-help-name="pushbullet in">
|
||||||
|
<p>Receives Pushbullets from all devices. Messages contain the following data:</p>
|
||||||
|
<p><code>msg.pushtype</code>: type of message</p>
|
||||||
|
<p><code>msg.topic</code>: topic information from the push</p>
|
||||||
|
<p><code>msg.payload</code>: main content of the push</p>
|
||||||
|
<p><code>msg.data</code>: original object from the pushbullet API containing e.g. sender, receiver and message ids.</p>
|
||||||
|
<p>Pushes of type <i>link</i> and <i>file</i> will also have <code>msg.message</code> containing the message associated with the push.</p>
|
||||||
|
<p>For further details of see <a href="https://docs.pushbullet.com/stream/">Pushbullet Stream API</a> and <a href="https://docs.pushbullet.com/v2/pushes/">Pushbullet Push API</a>.</p>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
RED.nodes.registerType('pushbullet in',{
|
||||||
|
category: 'social-input',
|
||||||
|
defaults: {
|
||||||
|
config: {type: "pushbullet-config", required: true},
|
||||||
|
name: {value:""}
|
||||||
|
},
|
||||||
|
credentials: {
|
||||||
|
filters: {value: []}
|
||||||
|
},
|
||||||
|
color:"#a7c9a0",
|
||||||
|
inputs:0,
|
||||||
|
outputs:1,
|
||||||
|
icon: "bullet.png",
|
||||||
|
label: function() {
|
||||||
|
return this.name||"pushbullet";
|
||||||
},
|
},
|
||||||
labelStyle: function() {
|
labelStyle: function() {
|
||||||
return this.name?"node_label_italic":"";
|
return this.name?"node_label_italic":"";
|
||||||
},
|
},
|
||||||
oneditprepare: function() {
|
oneditprepare: function() {
|
||||||
$.getJSON('pushbullet/'+this.id,function(data) {
|
var node = this, ddConfig = $('#node-input-config'), ddDevice = $('#node-input-filters');
|
||||||
if (data.deviceid) {
|
|
||||||
$('#node-config-input-deviceid').val(data.deviceid);
|
function updateDeviceList() {
|
||||||
|
var config = RED.nodes.node(ddConfig.val()),
|
||||||
|
url = 'pushbullet/'+ddConfig.val()+'/devices';
|
||||||
|
if(config && config.credentials && config.credentials.apikey) {
|
||||||
|
url += '?apikey='+config.credentials.apikey;
|
||||||
}
|
}
|
||||||
if (data.hasPassword) {
|
$.getJSON(url, function(data) {
|
||||||
$('#node-config-input-pushkey').val('__PWRD__');
|
var currentDevices = [], addDevices = [];
|
||||||
} else {
|
if(node.credentials && node.credentials.filters) {
|
||||||
$('#node-config-input-pushkey').val('');
|
currentDevices = node.credentials.filters;
|
||||||
|
addDevices = node.credentials.filters.splice();
|
||||||
}
|
}
|
||||||
if (data.global) $('#node-tip').show();
|
|
||||||
else $('#node-tip').hide();
|
var idx;
|
||||||
|
ddDevice.children().remove();
|
||||||
|
for(var i=0; i<data.length; i++) {
|
||||||
|
var dev = data[i];
|
||||||
|
ddDevice.append('<option value="'+dev.iden+'">'+dev.nickname+' ('+dev.kind+')</option>');
|
||||||
|
|
||||||
|
idx = addDevices.indexOf(dev.iden);
|
||||||
|
if (idx > -1) {
|
||||||
|
addDevices.splice(idx, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var j=0;j<addDevices.length;j++) {
|
||||||
|
ddDevice.append('<option value="'+addDevices[j]+'">'+addDevices[j]+'</option>');
|
||||||
|
}
|
||||||
|
ddDevice.val(currentDevices);
|
||||||
});
|
});
|
||||||
},
|
|
||||||
oneditsave: function() {
|
|
||||||
var newUser = $('#node-config-input-deviceid').val();
|
|
||||||
var newPass = $('#node-config-input-pushkey').val();
|
|
||||||
var credentials = {};
|
|
||||||
credentials.deviceid = newUser;
|
|
||||||
if (newPass != '__PWRD__') {
|
|
||||||
credentials.pushkey = newPass;
|
|
||||||
}
|
}
|
||||||
$.ajax({
|
|
||||||
url: 'pushbullet/'+this.id,
|
ddConfig.change(function() {
|
||||||
type: 'POST',
|
if(ddConfig.val() && ddConfig.val() !== "_ADD_") {
|
||||||
data: credentials,
|
updateDeviceList();
|
||||||
success:function(result){}
|
}
|
||||||
});
|
|
||||||
},
|
|
||||||
ondelete: function() {
|
|
||||||
$.ajax({
|
|
||||||
url: 'pushbullet/'+this.id,
|
|
||||||
type: 'DELETE',
|
|
||||||
success: function(result) {}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2013 IBM Corp.
|
* Copyright 2013,2015 IBM Corp.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -17,109 +17,527 @@
|
|||||||
module.exports = function(RED) {
|
module.exports = function(RED) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var PushBullet = require('pushbullet');
|
var PushBullet = require('pushbullet');
|
||||||
|
var fs = require('fs');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
var when = require('when');
|
||||||
|
var nodefn = require('when/node');
|
||||||
|
var EventEmitter = require('events').EventEmitter;
|
||||||
|
|
||||||
// Either create pushkey.js in dir ABOVE node-red, it just needs to be like
|
function onError(err, node) {
|
||||||
// module.exports = {pushbullet:'My-API-KEY', deviceid:'12345'}
|
if(err && node) {
|
||||||
// or set them per node in the edit dialog
|
if(node.emitter) {
|
||||||
|
if(!node.emitter.emit('error', err)) {
|
||||||
try {
|
|
||||||
var pushkeys = RED.settings.pushbullet || require(process.env.NODE_RED_HOME+"/../pushkey.js");
|
|
||||||
}
|
|
||||||
catch(err) {
|
|
||||||
//util.log("[57-pushbullet.js] Warning: Failed to load global PushBullet credentials");
|
|
||||||
}
|
|
||||||
|
|
||||||
function PushbulletNode(n) {
|
|
||||||
RED.nodes.createNode(this,n);
|
|
||||||
this.title = n.title;
|
|
||||||
this.chan = n.chan;
|
|
||||||
var credentials = RED.nodes.getCredentials(n.id);
|
|
||||||
if ((credentials) && (credentials.hasOwnProperty("pushkey"))) { this.pushkey = credentials.pushkey; }
|
|
||||||
else {
|
|
||||||
if (pushkeys) { this.pushkey = pushkeys.pushbullet; }
|
|
||||||
else { this.error("No Pushbullet API key set"); }
|
|
||||||
}
|
|
||||||
if ((credentials) && (credentials.hasOwnProperty("deviceid"))) { this.deviceid = credentials.deviceid; }
|
|
||||||
else {
|
|
||||||
if (pushkeys) { this.deviceid = pushkeys.deviceid; }
|
|
||||||
else { this.warn("No deviceid set"); }
|
|
||||||
}
|
|
||||||
this.pusher = new PushBullet(this.pushkey);
|
|
||||||
var node = this;
|
|
||||||
|
|
||||||
this.on("input",function(msg) {
|
|
||||||
var titl = node.title || msg.topic || "Node-RED";
|
|
||||||
var dev = node.deviceid || msg.deviceid;
|
|
||||||
var channel = node.chan || msg.chan;
|
|
||||||
if (channel != undefined) {
|
|
||||||
dev = { channel_tag : channel };
|
|
||||||
} else {
|
|
||||||
if (!isNaN(dev)) { dev = Number(dev); }
|
|
||||||
}
|
|
||||||
if (typeof(msg.payload) === 'object') {
|
|
||||||
msg.payload = JSON.stringify(msg.payload);
|
|
||||||
}
|
|
||||||
else { msg.payload = msg.payload.toString(); }
|
|
||||||
if (node.pushkey && dev) {
|
|
||||||
try {
|
|
||||||
node.pusher.note(dev, titl, msg.payload, function(err, response) {
|
|
||||||
if (err) { node.error("Pushbullet error"); }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
node.error(err);
|
node.error(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
node.warn("Pushbullet credentials not set/found. See node info.");
|
node.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function PushbulletConfig(n) {
|
||||||
|
RED.nodes.createNode(this, n);
|
||||||
|
this.name = n.name;
|
||||||
|
this._inputNodes = [];
|
||||||
|
this.emitter = new EventEmitter();
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// sort migration from old node
|
||||||
|
var apikey;
|
||||||
|
if(n._migrate) {
|
||||||
|
apikey = n._apikey;
|
||||||
|
this.credentials = {apikey:apikey};
|
||||||
|
}
|
||||||
|
else if(this.credentials) {
|
||||||
|
apikey = this.credentials.apikey;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (apikey) {
|
||||||
|
try {
|
||||||
|
var pusher = new PushBullet(apikey);
|
||||||
|
// get 'me' info
|
||||||
|
this.me = when.promise(function(resolve, reject) {
|
||||||
|
pusher.me(function(err, me) {
|
||||||
|
if(err) {
|
||||||
|
reject(err);
|
||||||
|
return onError(err, self);
|
||||||
|
}
|
||||||
|
resolve(me);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// get latest timestamp
|
||||||
|
this.last = when.promise(function(resolve) {
|
||||||
|
pusher.history({limit:1}, function(err, res) {
|
||||||
|
if(err) {
|
||||||
|
resolve(0);
|
||||||
|
return onError(err, self);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
resolve(res.pushes[0].modified);
|
||||||
|
}
|
||||||
|
catch(ex){
|
||||||
|
self.warn('Unable to get history.');
|
||||||
|
resolve(0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
this.pusher = pusher;
|
||||||
}
|
}
|
||||||
RED.nodes.registerType("pushbullet",PushbulletNode);
|
catch(err) {
|
||||||
|
onError(err, this);
|
||||||
var querystring = require('querystring');
|
|
||||||
|
|
||||||
RED.httpAdmin.get('/pushbullet/:id',function(req,res) {
|
|
||||||
var credentials = RED.nodes.getCredentials(req.params.id);
|
|
||||||
if (credentials) {
|
|
||||||
res.send(JSON.stringify({deviceid:credentials.deviceid,hasPassword:(credentials.pushkey&&credentials.pushkey!=="")}));
|
|
||||||
}
|
}
|
||||||
else if (pushkeys && pushkeys.pushbullet && pushkeys.deviceid) {
|
|
||||||
RED.nodes.addCredentials(req.params.id,{pushkey:pushkeys.pushbullet,deviceid:pushkeys.deviceid,global:true});
|
|
||||||
credentials = RED.nodes.getCredentials(req.params.id);
|
|
||||||
res.send(JSON.stringify({deviceid:credentials.deviceid,global:credentials.global,hasPassword:(credentials.pushkey&&credentials.pushkey!=="")}));
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
res.send(JSON.stringify({}));
|
this.error("No credentials set for pushbullet config.");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.on("close", function() {
|
||||||
|
if(self.stream) {
|
||||||
|
self.stream.close();
|
||||||
|
}
|
||||||
|
self._inputNodes.length = 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
RED.nodes.registerType("pushbullet-config", PushbulletConfig, {
|
||||||
|
credentials: {
|
||||||
|
apikey: {type: "password"}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
RED.httpAdmin.delete('/pushbullet/:id',function(req,res) {
|
PushbulletConfig.prototype.onConfig = function(type, cb) {
|
||||||
RED.nodes.deleteCredentials(req.params.id);
|
this.emitter.on(type, cb);
|
||||||
res.send(200);
|
}
|
||||||
|
|
||||||
|
PushbulletConfig.prototype.setupStream = function() {
|
||||||
|
var self = this;
|
||||||
|
if(this.pusher) {
|
||||||
|
var stream = this.pusher.stream();
|
||||||
|
stream.on('message', function(res) {
|
||||||
|
if(res.type === 'tickle') {
|
||||||
|
self.handleTickle(res);
|
||||||
|
}
|
||||||
|
else if(res.type === 'push') {
|
||||||
|
self.pushMsg(res.push);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
stream.on('connect', function() {
|
||||||
|
self.emitter.emit('stream_connected');
|
||||||
|
});
|
||||||
|
stream.on('close', function() {
|
||||||
|
self.emitter.emit('stream_disconnected');
|
||||||
|
});
|
||||||
|
stream.on('error', function(err) {
|
||||||
|
self.emitter.emit('stream_error', err);
|
||||||
|
});
|
||||||
|
stream.connect();
|
||||||
|
this.stream = stream;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PushbulletConfig.prototype.handleTickle = function(ticklemsg) {
|
||||||
|
var self = this;
|
||||||
|
if(this.pusher && ticklemsg.subtype === "push") {
|
||||||
|
var lastprom = this.last;
|
||||||
|
this.last = when.promise(function(resolve) {
|
||||||
|
when(lastprom).then(function(last) {
|
||||||
|
self.pusher.history({modified_after: last}, function(err, res) {
|
||||||
|
if(err) {
|
||||||
|
resolve(last);
|
||||||
|
return onError(err);
|
||||||
|
}
|
||||||
|
for(var i=0;i<res.pushes.length; i++) {
|
||||||
|
self.pushMsg(res.pushes[i]);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
resolve(res.pushes[0].modified);
|
||||||
|
} catch(ex) {
|
||||||
|
resolve(last);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PushbulletConfig.prototype.pushMsg = function(incoming) {
|
||||||
|
if(this._inputNodes.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var msg = {
|
||||||
|
pushtype: incoming.type,
|
||||||
|
data: incoming
|
||||||
|
}
|
||||||
|
|
||||||
|
if(incoming.dismissed === true) {
|
||||||
|
msg.pushtype = 'dismissal';
|
||||||
|
msg.topic = 'Push dismissed';
|
||||||
|
msg.payload = incoming.iden;
|
||||||
|
}
|
||||||
|
else if(incoming.active === false && incoming.type === undefined) {
|
||||||
|
msg.pushtype = 'delete';
|
||||||
|
msg.topic = 'Push deleted';
|
||||||
|
msg.payload = incoming.iden;
|
||||||
|
}
|
||||||
|
else if(incoming.type === 'clip') {
|
||||||
|
msg.topic = 'Clipboard content';
|
||||||
|
msg.payload = incoming.body;
|
||||||
|
}
|
||||||
|
else if(incoming.type === 'note') {
|
||||||
|
msg.topic = incoming.title;
|
||||||
|
msg.payload = incoming.body;
|
||||||
|
}
|
||||||
|
else if(incoming.type === 'link') {
|
||||||
|
msg.topic = incoming.title;
|
||||||
|
msg.payload = incoming.url;
|
||||||
|
msg.message = incoming.body;
|
||||||
|
}
|
||||||
|
else if(incoming.type === 'address') {
|
||||||
|
msg.topic = incoming.name;
|
||||||
|
msg.payload = incoming.address;
|
||||||
|
}
|
||||||
|
else if(incoming.type === 'list') {
|
||||||
|
msg.topic = incoming.title;
|
||||||
|
msg.payload = incoming.items;
|
||||||
|
}
|
||||||
|
else if(incoming.type === 'file') {
|
||||||
|
msg.topic = incoming.file_name;
|
||||||
|
msg.payload = incoming.file_url;
|
||||||
|
msg.message = incoming.body;
|
||||||
|
}
|
||||||
|
// Android specific, untested
|
||||||
|
else if(incoming.type === 'mirror') {
|
||||||
|
msg.topic = incoming.title;
|
||||||
|
msg.payload = incoming.body;
|
||||||
|
}
|
||||||
|
else if(incoming.type === 'dismissal') {
|
||||||
|
msg.topic = "dismissal";
|
||||||
|
msg.topic = "Push dismissed";
|
||||||
|
msg.payload = incoming.iden;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.error("unknown push type: " + incoming.type + " content: " + JSON.stringify(incoming));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < this._inputNodes.length; i++) {
|
||||||
|
this._inputNodes[i].emitPush(msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PushbulletConfig.prototype.registerInputNode = function(/*Node*/handler) {
|
||||||
|
if(!this.stream) {
|
||||||
|
this.setupStream();
|
||||||
|
}
|
||||||
|
this._inputNodes.push(handler);
|
||||||
|
};
|
||||||
|
|
||||||
|
function migrateOldSettings(n) {
|
||||||
|
if(n.config === undefined) {
|
||||||
|
var newid, config, apikey, deviceid, pushkeys;
|
||||||
|
|
||||||
|
try {
|
||||||
|
pushkeys = RED.settings.pushbullet || require(process.env.NODE_RED_HOME+"/../pushkey.js");
|
||||||
|
}
|
||||||
|
catch(err) {
|
||||||
|
}
|
||||||
|
|
||||||
|
var cred = RED.nodes.getCredentials(n.id);
|
||||||
|
// get old apikey
|
||||||
|
if(cred && cred.hasOwnProperty("pushkey")) {
|
||||||
|
apikey = cred.pushkey;
|
||||||
|
}
|
||||||
|
else if(pushkeys) {
|
||||||
|
apikey = pushkeys.pushbullet;
|
||||||
|
}
|
||||||
|
// get old device
|
||||||
|
if (cred && cred.hasOwnProperty("deviceid")) {
|
||||||
|
deviceid = cred.deviceid;
|
||||||
|
}
|
||||||
|
else if (pushkeys) {
|
||||||
|
deviceid = pushkeys.deviceid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(apikey) {
|
||||||
|
newid = (1+Math.random()*4294967295).toString(16);
|
||||||
|
config = new PushbulletConfig({
|
||||||
|
id: newid,
|
||||||
|
type: 'pushbullet-config',
|
||||||
|
name: n.name,
|
||||||
|
_migrate: true,
|
||||||
|
_apikey: apikey,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!(apikey || deviceid)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// override configuration properties to compatible migrated ones
|
||||||
|
n.pushtype = "note";
|
||||||
|
n.deviceid = deviceid;
|
||||||
|
return {
|
||||||
|
deviceid: deviceid,
|
||||||
|
apikey: apikey,
|
||||||
|
config: config,
|
||||||
|
id: newid
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function PushbulletOut(n) {
|
||||||
|
RED.nodes.createNode(this, n);
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.migrated = migrateOldSettings(n);
|
||||||
|
this.title = n.title;
|
||||||
|
this.chan = n.chan;
|
||||||
|
this.pushtype = n.pushtype;
|
||||||
|
this.pusher = null;
|
||||||
|
|
||||||
|
var configNode;
|
||||||
|
if(this.migrated) {
|
||||||
|
this.warn('Settings migrated from previous version of Pushbullet Node, please edit node to update settings.');
|
||||||
|
this.status({fill: 'yellow', shape: 'ring', text: 'Node migrated'});
|
||||||
|
this.deviceid = this.migrated.deviceid;
|
||||||
|
configNode = this.migrated.config;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.status({});
|
||||||
|
configNode = RED.nodes.getNode(n.config);
|
||||||
|
try {
|
||||||
|
this.deviceid = this.credentials.deviceid;
|
||||||
|
}
|
||||||
|
catch(err){}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(configNode) {
|
||||||
|
this.pusher = configNode.pusher;
|
||||||
|
configNode.onConfig('error', function(err) {
|
||||||
|
self.error(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.on("input", function(msg) {
|
||||||
|
var title = self.title || msg.topic || "Node-RED";
|
||||||
|
var deviceid = (self.deviceid === '_msg_')? (msg.deviceid || ""): (self.deviceid || "");
|
||||||
|
var pushtype = self.pushtype || msg.pushtype || "note";
|
||||||
|
var channel = self.chan || msg.channel;
|
||||||
|
|
||||||
|
if (typeof(msg.payload) === 'object') {
|
||||||
|
msg.payload = JSON.stringify(msg.payload);
|
||||||
|
}
|
||||||
|
else if(msg.payload) {
|
||||||
|
msg.payload = msg.payload.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(['delete', 'dismissal', 'updatelist', '_rawupdate_'].indexOf(pushtype) === -1) {
|
||||||
|
if (channel) {
|
||||||
|
deviceid = { channel_tag : channel };
|
||||||
|
}
|
||||||
|
else if(deviceid === "") {
|
||||||
|
try {
|
||||||
|
when(configNode.me).then(function(me) {
|
||||||
|
deviceid = me.email;
|
||||||
|
self.pushMsg(pushtype, deviceid, title, msg);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch(err) {
|
||||||
|
self.error('Unable to push to "all".');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!isNaN(deviceid)) {
|
||||||
|
deviceid = Number(deviceid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.pushMsg(pushtype, deviceid, title, msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
RED.nodes.registerType("pushbullet", PushbulletOut, {
|
||||||
|
credentials: {
|
||||||
|
deviceid: {value: ""},
|
||||||
|
pushkey: {value: ""}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
RED.httpAdmin.post('/pushbullet/:id',function(req,res) {
|
PushbulletOut.prototype.pushMsg = function(pushtype, deviceid, title, msg) {
|
||||||
var body = "";
|
var self = this;
|
||||||
req.on('data', function(chunk) {
|
if (this.pusher) {
|
||||||
body+=chunk;
|
var handleErr = function(msg){
|
||||||
});
|
return function(err) {
|
||||||
req.on('end', function(){
|
if(err) {
|
||||||
var newCreds = querystring.parse(body);
|
self.error(msg);
|
||||||
var credentials = RED.nodes.getCredentials(req.params.id)||{};
|
onError(err, self);
|
||||||
if (newCreds.deviceid === null || newCreds.deviceid === "") {
|
|
||||||
delete credentials.deviceid;
|
|
||||||
} else {
|
|
||||||
credentials.deviceid = newCreds.deviceid;
|
|
||||||
}
|
}
|
||||||
if (newCreds.pushkey === "") {
|
|
||||||
delete credentials.pushkey;
|
|
||||||
} else {
|
|
||||||
credentials.pushkey = newCreds.pushkey||credentials.pushkey;
|
|
||||||
}
|
}
|
||||||
RED.nodes.addCredentials(req.params.id,credentials);
|
}
|
||||||
res.send(200);
|
|
||||||
|
if(deviceid) {
|
||||||
|
if(pushtype === 'note') {
|
||||||
|
this.pusher.note(deviceid, title, msg.payload, handleErr('Unable to push note'));
|
||||||
|
}
|
||||||
|
else if(pushtype === 'address') {
|
||||||
|
this.pusher.address(deviceid, title, msg.payload, handleErr('Unable to push address'));
|
||||||
|
}
|
||||||
|
else if(pushtype === 'list') {
|
||||||
|
this.pusher.list(deviceid, title, JSON.parse(msg.payload), handleErr('Unable to push list'));
|
||||||
|
}
|
||||||
|
else if(pushtype === 'link') {
|
||||||
|
this.pusher.push(deviceid, {
|
||||||
|
type: 'link',
|
||||||
|
title: title,
|
||||||
|
body: msg.message,
|
||||||
|
url: msg.payload
|
||||||
|
}, handleErr('Unable to push link'));
|
||||||
|
}
|
||||||
|
else if(pushtype === 'file') {
|
||||||
|
// Workaround for Pushbullet dep not handling error on file open
|
||||||
|
if(fs.existsSync(msg.payload)) {
|
||||||
|
this.pusher.file(deviceid, msg.payload, title, handleErr('Unable to push file'));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.error('File does not exist!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(pushtype === '_raw_') {
|
||||||
|
this.pusher.push(deviceid, msg.raw, handleErr('Unable to push raw data'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(msg.data && msg.data.iden) {
|
||||||
|
if(pushtype === 'delete') {
|
||||||
|
this.pusher.deletePush(msg.data.iden, handleErr('Unable to delete push'));
|
||||||
|
}
|
||||||
|
else if(pushtype === 'dismissal') {
|
||||||
|
this.pusher.updatePush(msg.data.iden, {dismissed: true}, handleErr('Unable to dismiss push'));
|
||||||
|
}
|
||||||
|
else if(pushtype === 'updatelist') {
|
||||||
|
try {
|
||||||
|
var data = JSON.parse(msg.payload);
|
||||||
|
if(msg.data.type && msg.data.type !== 'list') {
|
||||||
|
this.warn('Trying to update list items in non list push');
|
||||||
|
}
|
||||||
|
this.pusher.updatePush(msg.data.iden, {items: data}, handleErr('Unable to update list'));
|
||||||
|
}
|
||||||
|
catch(err) {
|
||||||
|
this.warn("Invalid list");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(pushtype === '_rawupdate_') {
|
||||||
|
this.pusher.updatePush(msg.data.iden, msg.raw, handleErr('Unable to update raw data'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.error("Pushbullet credentials not set/found.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
RED.httpAdmin.get('/pushbullet/:id/migrate', function(req, res) {
|
||||||
|
var node = RED.nodes.getNode(req.params.id);
|
||||||
|
if(node && node.migrated) {
|
||||||
|
if(req.query.save) {
|
||||||
|
var promise;
|
||||||
|
if(node.migrated.apikey) {
|
||||||
|
promise = RED.nodes.addCredentials(node.migrated.id, {apikey: node.migrated.apikey});
|
||||||
|
}
|
||||||
|
if(node.migrated.deviceid) {
|
||||||
|
when(promise).then(function() {
|
||||||
|
RED.nodes.addCredentials(req.params.id, {deviceid: node.migrated.deviceid});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.send(JSON.stringify({migrated: true, config: node.migrated.id}));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res.send("{}");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
RED.httpAdmin.get('/pushbullet/:id/devices', function(req, res) {
|
||||||
|
var config = RED.nodes.getNode(req.params.id);
|
||||||
|
var cred = RED.nodes.getCredentials(req.params.id);
|
||||||
|
|
||||||
|
if(config && config.pusher) {
|
||||||
|
config.pusher.devices(function(err, chans) {
|
||||||
|
if(err) {
|
||||||
|
res.send("[]");
|
||||||
|
return onError(err, config);
|
||||||
|
}
|
||||||
|
res.send(JSON.stringify(chans.devices));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if(cred && cred.apikey) {
|
||||||
|
var pb = new PushBullet(cred.apikey);
|
||||||
|
pb.devices(function(err, chans) {
|
||||||
|
if(err) {
|
||||||
|
res.send("[]");
|
||||||
|
return onError(err, config);
|
||||||
|
}
|
||||||
|
res.send(JSON.stringify(chans.devices));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if(req.query.apikey) {
|
||||||
|
var pb = new PushBullet(req.query.apikey);
|
||||||
|
pb.devices(function(err, chans) {
|
||||||
|
if(err) {
|
||||||
|
res.send("[]");
|
||||||
|
return onError(err, config);
|
||||||
|
}
|
||||||
|
res.send(JSON.stringify(chans.devices));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res.send("[]");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function PushbulletIn(n) {
|
||||||
|
RED.nodes.createNode(this, n);
|
||||||
|
var self = this;
|
||||||
|
var config = RED.nodes.getNode(n.config);
|
||||||
|
if(config) {
|
||||||
|
config.registerInputNode(this);
|
||||||
|
config.onConfig('error', function(err) {
|
||||||
|
self.error(err);
|
||||||
|
});
|
||||||
|
config.onConfig('stream_connected', function() {
|
||||||
|
self.status({fill: 'green', shape: 'ring', text: 'connected'});
|
||||||
|
});
|
||||||
|
config.onConfig('stream_disconnected', function(err) {
|
||||||
|
self.status({fill: 'red', shape: 'ring', text: 'disconnected'});
|
||||||
|
});
|
||||||
|
config.onConfig('stream_error', function(err) {
|
||||||
|
self.status({fill: 'red', shape: 'ring', text: 'error, see log'});
|
||||||
|
self.error(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RED.nodes.registerType("pushbullet in", PushbulletIn, {
|
||||||
|
credentials: {
|
||||||
|
filters: {value: []}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
PushbulletIn.prototype.emitPush = function(msg) {
|
||||||
|
try {
|
||||||
|
if(this.credentials.filters.length > 0) {
|
||||||
|
if( (this.credentials.filters.indexOf(msg.data.source_device_iden) > -1) ||
|
||||||
|
(this.credentials.filters.indexOf(msg.data.target_device_iden) > -1) ||
|
||||||
|
(!msg.data.target_device_iden && !msg.data.source_device_iden)) { /* All */
|
||||||
|
this.send(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.send(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(err) {
|
||||||
|
this.send(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,23 @@ Run the following command in the root directory of your Node-RED install
|
|||||||
|
|
||||||
Usage
|
Usage
|
||||||
-----
|
-----
|
||||||
|
### Pushbullet output node
|
||||||
|
Uses PushBullet to push <code>msg.payload</code> to a device that has the PushBullet app installed.
|
||||||
|
* Optionally uses <code>msg.topic</code> to set the title, if not already set in the properties.
|
||||||
|
* Optionally uses <code>msg.pushtype</code> to set the type of the push, if not already set in the properties.
|
||||||
|
* Optionally uses <code>msg.deviceid</code> to set the device ID, if not already set in the properties.
|
||||||
|
|
||||||
Uses PushBullet to push the <b>msg.payload</b> to a device that has the PushBullet app installed.
|
You can also push to any channels that you own either configured or via <code>msg.channel</code>.
|
||||||
|
|
||||||
Optionally uses <b>msg.topic</b> to set the title, if not already set in the properties.
|
The node can also *dismiss* and *delete* any push and *update* items in a pushed list. In this case <code>msg.data.iden</code> must be set to a valid push id, if <code>msg</code> originates from the Pushbullet input node this value is already set.
|
||||||
|
|
||||||
You need to configure both your <i>API key</i> and the target <i>device ID</i>. The device ID may be passed in as <b>msg.deviceid</b>. You can set these per node in the edit dialog.
|
### Pushbullet input node
|
||||||
|
Receives Pushbullets from all devices. Messages contain the following data:
|
||||||
|
* <code>msg.pushtype</code>: type of message
|
||||||
|
* <code>msg.topic</code>: topic information from the push
|
||||||
|
* <code>msg.payload</code>: main content of the push
|
||||||
|
* <code>msg.data</code>: original object from the pushbullet API containing e.g. sender, receiver and message ids.
|
||||||
|
|
||||||
|
Pushes of type <i>link</i> and <i>file</i> will also have <code>msg.message</code> containing the message associated with the push.
|
||||||
|
|
||||||
|
For further details of see <a href="https://docs.pushbullet.com/stream/">Pushbullet Stream API</a> and <a href="https://docs.pushbullet.com/v2/pushes/">Pushbullet Push API</a>.
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
{
|
{
|
||||||
"name" : "node-red-node-pushbullet",
|
"name" : "node-red-node-pushbullet",
|
||||||
"version" : "0.0.2",
|
"version" : "0.0.3",
|
||||||
"description" : "A Node-RED node to send alerts via Pushbullet",
|
"description" : "A Node-RED node to send alerts via Pushbullet",
|
||||||
"dependencies" : {
|
"dependencies" : {
|
||||||
"pushbullet" : "1.4.*"
|
"pushbullet": "1.4.*",
|
||||||
|
"when": "^3.6.4"
|
||||||
},
|
},
|
||||||
"repository" : {
|
"repository" : {
|
||||||
"type":"git",
|
"type":"git",
|
||||||
"url":"https://github.com/node-red/node-red-nodes/tree/master/hardware/pushbullet"
|
"url":"https://github.com/node-red/node-red-nodes/tree/master/social/pushbullet"
|
||||||
},
|
},
|
||||||
"license": "Apache",
|
"license": "Apache",
|
||||||
"keywords": [ "node-red", "pushbullet" ],
|
"keywords": [ "node-red", "pushbullet" ],
|
||||||
|
779
test/social/pushbullet/57-pushbullet_spec.js
Normal file
779
test/social/pushbullet/57-pushbullet_spec.js
Normal file
@ -0,0 +1,779 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2015 IBM Corp.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*jshint expr: true*/
|
||||||
|
|
||||||
|
var sinon = require('sinon');
|
||||||
|
var should = require("should");
|
||||||
|
var proxyquire = require("proxyquire");
|
||||||
|
var EventEmitter = require('events').EventEmitter
|
||||||
|
var data = require("./data");
|
||||||
|
var path = require("path");
|
||||||
|
|
||||||
|
// Mute "Starting/Stopping flows to stdout"
|
||||||
|
var util = require('util');
|
||||||
|
var flows = proxyquire('../../../.node-red/red/nodes/flows.js', {
|
||||||
|
util: {
|
||||||
|
log: function(msg) {
|
||||||
|
if(!/ing flows/.test(msg)) {
|
||||||
|
util.log(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var helper = require('../../../.node-red/test/nodes/helper.js');
|
||||||
|
|
||||||
|
var currentPB = null;
|
||||||
|
var pushbulletStub = function() {
|
||||||
|
var self = this;
|
||||||
|
currentPB = this;
|
||||||
|
|
||||||
|
this.streamEmitter = new EventEmitter();
|
||||||
|
this.streamObj = {
|
||||||
|
on: function(key, cb){ self.streamEmitter.on(key, cb);},
|
||||||
|
close: function(){},
|
||||||
|
connect: function(){}
|
||||||
|
}
|
||||||
|
|
||||||
|
// funcs
|
||||||
|
this.me = function(cb){ cb(null, data.me); };
|
||||||
|
this.history = function(opts, cb){ cb(null, getPushReply('note')); };
|
||||||
|
this.devices = function(){};
|
||||||
|
|
||||||
|
// pushes
|
||||||
|
this.note = function(){};
|
||||||
|
this.file = function(){};
|
||||||
|
this.link = function(){};
|
||||||
|
this.list = function(){};
|
||||||
|
this.address = function(){};
|
||||||
|
this.push = function(){};
|
||||||
|
|
||||||
|
// websocket
|
||||||
|
this.stream = function() {
|
||||||
|
return this.streamObj;
|
||||||
|
};
|
||||||
|
|
||||||
|
// modifiers
|
||||||
|
this.deletePush = function(){};
|
||||||
|
this.updatePush = function(){};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
function getReply() {
|
||||||
|
return JSON.parse(JSON.stringify(data.base));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPushReply(name) {
|
||||||
|
var r = JSON.parse(JSON.stringify(data.base));
|
||||||
|
r.pushes.push(JSON.parse(JSON.stringify(data[name])))
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pushbulletNode = proxyquire("../../../social/pushbullet/57-pushbullet.js", {
|
||||||
|
"pushbullet": function(apikey) {
|
||||||
|
currentPB.apikey = apikey;
|
||||||
|
return currentPB;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('pushbullet node', function() {
|
||||||
|
|
||||||
|
beforeEach(function(done) {
|
||||||
|
currentPB = new pushbulletStub();
|
||||||
|
helper.startServer(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function(done) {
|
||||||
|
helper.unload();
|
||||||
|
helper.stopServer(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('loading and settings', function() {
|
||||||
|
it('loads', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet", config: "n1"}];
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}}, function() {
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gets user info', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet", config: "n1"}];
|
||||||
|
var func = sinon.stub(currentPB, "me");
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}}, function() {
|
||||||
|
func.yield(false, data.me);
|
||||||
|
helper.getNode("n1").me.then(function(me) {
|
||||||
|
me.should.have.property("email", "john.doe@noma.il");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gets user info, fail', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet", config: "n1"},
|
||||||
|
{id:"n3", type:"pushbullet in", config: "n1"}];
|
||||||
|
var func = sinon.spy(currentPB, "me");
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}}, function() {
|
||||||
|
var warn1 = sinon.spy(helper.getNode("n2"), "error");
|
||||||
|
var warn2 = sinon.spy(helper.getNode("n3"), "error");
|
||||||
|
func.yield(true, null);
|
||||||
|
func.callCount.should.be.above(0);
|
||||||
|
warn1.callCount.should.be.above(0);
|
||||||
|
warn2.callCount.should.be.above(0);
|
||||||
|
//helper.getNode("n1").me.should.have.property("email", "john.doe@noma.il");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('list devices', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet", config: "n1"}];
|
||||||
|
var func = sinon.stub(currentPB, "devices");
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}}, function() {
|
||||||
|
helper.request().get("/pushbullet/n1/devices").end(function(err, res) {
|
||||||
|
var devs = JSON.parse(res.text);
|
||||||
|
devs.should.have.length(2);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
var ret = getReply();
|
||||||
|
ret.devices = data.devices;
|
||||||
|
func.yields(false, ret);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gets last modified', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet", config: "n1"}];
|
||||||
|
var func = sinon.stub(currentPB, "history");
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}}, function() {
|
||||||
|
func.yield(false, getPushReply('modified'));
|
||||||
|
helper.getNode("n1").last.then(function(last) {
|
||||||
|
last.should.equal(1234);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gets last modified with no history', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet", config: "n1"}];
|
||||||
|
var func = sinon.stub(currentPB, "history");
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}}, function() {
|
||||||
|
func.yield(false, getReply());
|
||||||
|
helper.getNode("n1").last.then(function(last) {
|
||||||
|
last.should.equal(0);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('backward compatiblity', function() {
|
||||||
|
it('should be valid', function(done) {
|
||||||
|
var flow = [{"id": "n1", "type": "pushbullet", "chan": "", "title": "topic"},
|
||||||
|
{id:"n3", type:"helper", wires: [["n1"]]}];
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{pushkey:"key", deviceid: "dev"}}, function() {
|
||||||
|
var func = sinon.spy(currentPB, "note");
|
||||||
|
helper.getNode("n3").send({
|
||||||
|
payload: "my note",
|
||||||
|
});
|
||||||
|
sinon.assert.calledWith(func, "dev", "topic", "my note");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('defaults and msg optionals', function() {
|
||||||
|
it('defaults', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet", config: "n1"},
|
||||||
|
{id:"n3", type:"helper", wires: [["n2"]]}];
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}}, function() {
|
||||||
|
var func = sinon.spy(currentPB, "note");
|
||||||
|
helper.getNode("n3").send({
|
||||||
|
payload: "my note",
|
||||||
|
});
|
||||||
|
helper.getNode("n1").me.then(function(){
|
||||||
|
sinon.assert.calledWith(func, "john.doe@noma.il", "Node-RED", "my note");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('no overrides', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet", config: "n1", pushtype: "note", title: "title"},
|
||||||
|
{id:"n3", type:"helper", wires: [["n2"]]}];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}, n2: {deviceid: "id"}}, function() {
|
||||||
|
var func = sinon.spy(currentPB, "note");
|
||||||
|
helper.getNode("n3").send({
|
||||||
|
payload: "my note",
|
||||||
|
});
|
||||||
|
sinon.assert.calledWith(func, "id", "title", "my note");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('channel', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet", config: "n1", pushtype: "note", title: "title", chan: "mychannel"},
|
||||||
|
{id:"n3", type:"helper", wires: [["n2"]]}];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}, n2: {deviceid: "id"}}, function() {
|
||||||
|
var func = sinon.spy(currentPB, "note");
|
||||||
|
helper.getNode("n3").send({
|
||||||
|
payload: "my note",
|
||||||
|
});
|
||||||
|
sinon.assert.calledWith(func, {channel_tag: "mychannel"}, "title", "my note");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('channel from msg', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet", config: "n1", pushtype: "note", title: "title"},
|
||||||
|
{id:"n3", type:"helper", wires: [["n2"]]}];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}, n2: {deviceid: "id"}}, function() {
|
||||||
|
var func = sinon.spy(currentPB, "note");
|
||||||
|
helper.getNode("n3").send({
|
||||||
|
payload: "my note",
|
||||||
|
channel: "mychannel"
|
||||||
|
});
|
||||||
|
sinon.assert.calledWith(func, {channel_tag: "mychannel"}, "title", "my note");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('all optionals but defined in node', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet", config: "n1", pushtype: "link", title: "title"},
|
||||||
|
{id:"n3", type:"helper", wires: [["n2"]]}];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}, n2: {deviceid: "id"}}, function() {
|
||||||
|
var func = sinon.spy(currentPB, "push");
|
||||||
|
helper.getNode("n3").send({
|
||||||
|
deviceid: "over1",
|
||||||
|
topic: "over2",
|
||||||
|
pushtype: "note",
|
||||||
|
payload: "payload"
|
||||||
|
});
|
||||||
|
sinon.assert.calledWith(func, "id", {type: "link", body: undefined, title:"title", url: "payload"});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('all optionals', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet", config: "n1", pushtype: "", title: ""},
|
||||||
|
{id:"n3", type:"helper", wires: [["n2"]]}];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}, n2: {deviceid: "_msg_"}}, function() {
|
||||||
|
var func = sinon.spy(currentPB, "push");
|
||||||
|
helper.getNode("n3").send({
|
||||||
|
deviceid: "over1",
|
||||||
|
topic: "over2",
|
||||||
|
payload: "over3",
|
||||||
|
pushtype: "link"
|
||||||
|
});
|
||||||
|
sinon.assert.calledWith(func, "over1", {type: "link", body: undefined, title: "over2", url: "over3"});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('output', function() {
|
||||||
|
it('note', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet", config: "n1", pushtype: "note", title: "title"},
|
||||||
|
{id:"n3", type:"helper", wires: [["n2"]]}
|
||||||
|
];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}, n2: {deviceid: "id"}}, function() {
|
||||||
|
var func = sinon.spy(currentPB, "note");
|
||||||
|
helper.getNode("n3").send({
|
||||||
|
payload: "my note",
|
||||||
|
});
|
||||||
|
sinon.assert.calledWith(func, "id", "title", "my note");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('link', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet", config: "n1", pushtype: "link", title: "title"},
|
||||||
|
{id:"n3", type:"helper", wires: [["n2"]]}
|
||||||
|
];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}, n2: {deviceid: "id"}}, function() {
|
||||||
|
var func = sinon.spy(currentPB, "push");
|
||||||
|
helper.getNode("n3").send({
|
||||||
|
payload: "http://link",
|
||||||
|
message: "message"
|
||||||
|
});
|
||||||
|
sinon.assert.calledWith(func, "id", {type: "link", title: "title", url: "http://link", body: "message"});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('list', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet", config: "n1", pushtype: "list", title: "title"},
|
||||||
|
{id:"n3", type:"helper", wires: [["n2"]]}
|
||||||
|
];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}, n2: {deviceid: "id"}}, function() {
|
||||||
|
var func = sinon.spy(currentPB, "list");
|
||||||
|
helper.getNode("n3").send({
|
||||||
|
payload: ["a", "b", "c"],
|
||||||
|
});
|
||||||
|
sinon.assert.calledWith(func, "id", "title");
|
||||||
|
func.getCall(0).args[2].should.have.length(3);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('list string', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet", config: "n1", pushtype: "list", title: "title"},
|
||||||
|
{id:"n3", type:"helper", wires: [["n2"]]}
|
||||||
|
];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}, n2: {deviceid: "id"}}, function() {
|
||||||
|
var func = sinon.spy(currentPB, "list");
|
||||||
|
helper.getNode("n3").send({
|
||||||
|
payload: '["a", "b", "c"]',
|
||||||
|
});
|
||||||
|
sinon.assert.calledWith(func, "id", "title");
|
||||||
|
func.getCall(0).args[2].should.have.length(3);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('address', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet", config: "n1", pushtype: "address", title: "title"},
|
||||||
|
{id:"n3", type:"helper", wires: [["n2"]]}
|
||||||
|
];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}, n2: {deviceid: "id"}}, function() {
|
||||||
|
var func = sinon.spy(currentPB, "address");
|
||||||
|
helper.getNode("n3").send({
|
||||||
|
payload: "My Street 4",
|
||||||
|
});
|
||||||
|
sinon.assert.calledWith(func, "id", "title", "My Street 4");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('file', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet", config: "n1", pushtype: "file", title: "title"},
|
||||||
|
{id:"n3", type:"helper", wires: [["n2"]]}
|
||||||
|
];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}, n2: {deviceid: "id"}}, function() {
|
||||||
|
var func = sinon.spy(currentPB, "file");
|
||||||
|
var fn = path.join(__dirname, "data.js")
|
||||||
|
helper.getNode("n3").send({
|
||||||
|
payload: fn,
|
||||||
|
});
|
||||||
|
sinon.assert.calledWith(func, "id", fn, "title");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail if file don\'t exist', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet", config: "n1", pushtype: "file", title: "title"},
|
||||||
|
{id:"n3", type:"helper", wires: [["n2"]]}
|
||||||
|
];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}, n2: {deviceid: "id"}}, function() {
|
||||||
|
var func = sinon.spy(currentPB, "file");
|
||||||
|
var errfn = sinon.spy(helper.getNode("n2"), "error");
|
||||||
|
helper.getNode("n3").send({
|
||||||
|
payload: "hello",
|
||||||
|
});
|
||||||
|
func.called.should.fail;
|
||||||
|
errfn.callCount.should.be.above(0);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('raw', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet", config: "n1"},
|
||||||
|
{id:"n3", type:"helper", wires: [["n2"]]}
|
||||||
|
];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}, n2: {deviceid: "id"}}, function() {
|
||||||
|
var func = sinon.spy(currentPB, "push");
|
||||||
|
helper.getNode("n3").send({
|
||||||
|
pushtype: '_raw_',
|
||||||
|
raw: {type: 'note', title: 'title', body: 'this is the note'}
|
||||||
|
});
|
||||||
|
sinon.assert.calledWith(func, "id", {type: 'note', title: 'title', body: 'this is the note'});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('raw update', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet", config: "n1"},
|
||||||
|
{id:"n3", type:"helper", wires: [["n2"]]}
|
||||||
|
];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}, n2: {deviceid: "id"}}, function() {
|
||||||
|
var func = sinon.spy(currentPB, "updatePush");
|
||||||
|
helper.getNode("n3").send({
|
||||||
|
pushtype: '_rawupdate_',
|
||||||
|
raw: {items: [{checked:false, text: 'a'}]},
|
||||||
|
data: {iden: 'id'}
|
||||||
|
});
|
||||||
|
sinon.assert.calledWith(func, "id", {items: [{checked:false, text: 'a'}]});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('actions', function() {
|
||||||
|
it('delete', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet", config: "n1", pushtype: "delete"},
|
||||||
|
{id:"n3", type:"helper", wires: [["n2"]]}
|
||||||
|
];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}}, function() {
|
||||||
|
var func = sinon.spy(currentPB, "deletePush");
|
||||||
|
helper.getNode("n3").send({
|
||||||
|
payload: "my note",
|
||||||
|
data: { iden: "delid"}
|
||||||
|
});
|
||||||
|
sinon.assert.calledWith(func, "delid");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('dismiss', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet", config: "n1", pushtype: "dismissal"},
|
||||||
|
{id:"n3", type:"helper", wires: [["n2"]]}
|
||||||
|
];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}}, function() {
|
||||||
|
var func = sinon.spy(currentPB, "updatePush");
|
||||||
|
helper.getNode("n3").send({
|
||||||
|
payload: "my note",
|
||||||
|
data: { iden: "delid", dismissed: false }
|
||||||
|
});
|
||||||
|
sinon.assert.calledWith(func, "delid");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('update list', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet", config: "n1", pushtype: "updatelist"},
|
||||||
|
{id:"n3", type:"helper", wires: [["n2"]]}
|
||||||
|
];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}}, function() {
|
||||||
|
var func = sinon.spy(currentPB, "updatePush");
|
||||||
|
helper.getNode("n3").send({
|
||||||
|
payload: [{}, {}, {}],
|
||||||
|
data: { iden: "delid", type: "list" }
|
||||||
|
});
|
||||||
|
sinon.assert.calledWith(func, "delid");
|
||||||
|
func.getCall(0).args[1].items.should.have.length(3);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('input', function() {
|
||||||
|
it('handles stream push', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet in", config: "n1", wires: [["n3"]]},
|
||||||
|
{id:"n3", type:"helper"}];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}}, function() {
|
||||||
|
helper.getNode("n3").on("input", function(msg) {
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
currentPB.streamEmitter.emit("message", {type: "push", push: {type: "clip", body: "clipboard"}})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles status', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet in", config: "n1", wires: [["n3"]]},
|
||||||
|
{id:"n3", type:"helper"}];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}}, function() {
|
||||||
|
var func = sinon.spy(helper.getNode("n2"), 'status');
|
||||||
|
currentPB.streamEmitter.emit("connect");
|
||||||
|
currentPB.streamEmitter.emit("close");
|
||||||
|
currentPB.streamEmitter.emit("error");
|
||||||
|
func.callCount.should.equal(3);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clipboard', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet in", config: "n1", wires: [["n3"]]},
|
||||||
|
{id:"n3", type:"helper"}];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}}, function() {
|
||||||
|
helper.getNode("n3").on("input", function(msg) {
|
||||||
|
msg.should.have.property("pushtype", "clip");
|
||||||
|
msg.should.have.property("payload", "hello");
|
||||||
|
msg.should.have.property("data");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
currentPB.streamEmitter.emit("message", {type: "push", push: {type: "clip", body: "hello"}});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('mirror', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet in", config: "n1", wires: [["n3"]]},
|
||||||
|
{id:"n3", type:"helper"}];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}}, function() {
|
||||||
|
helper.getNode("n3").on("input", function(msg) {
|
||||||
|
msg.should.have.property("pushtype", "mirror");
|
||||||
|
msg.should.have.property("payload", "If you see this on your computer, mirroring is working!\n");
|
||||||
|
msg.should.have.property("data");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
currentPB.streamEmitter.emit("message", {type: "push", push: data.mirror});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('dismissal', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet in", config: "n1", wires: [["n3"]]},
|
||||||
|
{id:"n3", type:"helper"}];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}}, function() {
|
||||||
|
helper.getNode("n3").on("input", function(msg) {
|
||||||
|
msg.should.have.property("pushtype", "dismissal");
|
||||||
|
msg.should.have.property("payload", "pjgzwwocCCy");
|
||||||
|
msg.should.have.property("data");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
currentPB.streamEmitter.emit("message", {type: "push", push: data.dismissal});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('unknown type', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet in", config: "n1", wires: [["n3"]]},
|
||||||
|
{id:"n3", type:"helper"}];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}}, function() {
|
||||||
|
var err = sinon.spy(helper.getNode("n1"), "error");
|
||||||
|
currentPB.streamEmitter.emit("message", {type: "push", push: {type: "push", push: {type: "unknown", data: "test"}}});
|
||||||
|
err.called.should.be.ok;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('tickle', function() {
|
||||||
|
it('note', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet in", config: "n1", wires: [["n3"]]},
|
||||||
|
{id:"n3", type:"helper"}];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}}, function() {
|
||||||
|
helper.getNode("n3").on("input", function(msg) {
|
||||||
|
msg.should.have.property("pushtype", "note");
|
||||||
|
msg.should.have.property("payload", "body");
|
||||||
|
msg.should.have.property("topic", "title");
|
||||||
|
msg.should.have.property("data");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
var func = sinon.stub(currentPB, "history");
|
||||||
|
currentPB.streamEmitter.emit("message", {type: "tickle", subtype: "push"});
|
||||||
|
func.yields(false, getPushReply('note'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('link', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet in", config: "n1", wires: [["n3"]]},
|
||||||
|
{id:"n3", type:"helper"}];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}}, function() {
|
||||||
|
helper.getNode("n3").on("input", function(msg) {
|
||||||
|
msg.should.have.property("pushtype", "link");
|
||||||
|
msg.should.have.property("payload", "url");
|
||||||
|
msg.should.have.property("topic", "title");
|
||||||
|
msg.should.have.property("data");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
var func = sinon.stub(currentPB, "history");
|
||||||
|
currentPB.streamEmitter.emit("message", {type: "tickle", subtype: "push"});
|
||||||
|
func.yields(false, getPushReply('link'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('address', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet in", config: "n1", wires: [["n3"]]},
|
||||||
|
{id:"n3", type:"helper"}];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}}, function() {
|
||||||
|
helper.getNode("n3").on("input", function(msg) {
|
||||||
|
msg.should.have.property("pushtype", "address");
|
||||||
|
msg.should.have.property("payload", "address");
|
||||||
|
msg.should.have.property("topic", "title");
|
||||||
|
msg.should.have.property("data");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
var func = sinon.stub(currentPB, "history");
|
||||||
|
currentPB.streamEmitter.emit("message", {type: "tickle", subtype: "push"});
|
||||||
|
func.yields(false, getPushReply('address'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('file', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet in", config: "n1", wires: [["n3"]]},
|
||||||
|
{id:"n3", type:"helper"}];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}}, function() {
|
||||||
|
helper.getNode("n3").on("input", function(msg) {
|
||||||
|
msg.should.have.property("pushtype", "file");
|
||||||
|
msg.should.have.property("payload", "fileurl");
|
||||||
|
msg.should.have.property("topic", "filename");
|
||||||
|
msg.should.have.property("data");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
var func = sinon.stub(currentPB, "history");
|
||||||
|
currentPB.streamEmitter.emit("message", {type: "tickle", subtype: "push"});
|
||||||
|
func.yields(false, getPushReply('file'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('list', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet in", config: "n1", wires: [["n3"]]},
|
||||||
|
{id:"n3", type:"helper"}];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}}, function() {
|
||||||
|
helper.getNode("n3").on("input", function(msg) {
|
||||||
|
msg.should.have.property("pushtype", "list");
|
||||||
|
msg.should.have.property("topic", "title");
|
||||||
|
msg.should.have.property("payload").with.length(3);
|
||||||
|
msg.should.have.property("data");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
var func = sinon.stub(currentPB, "history");
|
||||||
|
currentPB.streamEmitter.emit("message", {type: "tickle", subtype: "push"});
|
||||||
|
func.yields(false, getPushReply('list'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('delete', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet in", config: "n1", wires: [["n3"]]},
|
||||||
|
{id:"n3", type:"helper"}];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}}, function() {
|
||||||
|
helper.getNode("n3").on("input", function(msg) {
|
||||||
|
msg.should.have.property("pushtype", "delete");
|
||||||
|
msg.should.have.property("payload", "pjgzwwocCCy");
|
||||||
|
msg.should.have.property("data");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
var func = sinon.stub(currentPB, "history");
|
||||||
|
currentPB.streamEmitter.emit("message", {type: "tickle", subtype: "push"});
|
||||||
|
func.yields(false, getPushReply('delete'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('dismissed', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet in", config: "n1", wires: [["n3"]]},
|
||||||
|
{id:"n3", type:"helper"}];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}}, function() {
|
||||||
|
helper.getNode("n3").on("input", function(msg) {
|
||||||
|
msg.should.have.property("pushtype", "dismissal");
|
||||||
|
msg.should.have.property("payload", "xXxXxXxXxXxsjArqXRsaZM");
|
||||||
|
msg.should.have.property("data");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
var func = sinon.stub(currentPB, "history");
|
||||||
|
currentPB.streamEmitter.emit("message", {type: "tickle", subtype: "push"});
|
||||||
|
var rep = getPushReply('note');
|
||||||
|
rep.pushes[0].dismissed = true;
|
||||||
|
func.yields(false, rep);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('filter', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"pushbullet-config"},
|
||||||
|
{id:"n2", type:"pushbullet in", config: "n1", wires: [["n3"]]},
|
||||||
|
{id:"n3", type:"helper"}];
|
||||||
|
|
||||||
|
helper.load(pushbulletNode, flow, {n1:{apikey:"invalid"}, n2:{filters:['a', 'b']}}, function() {
|
||||||
|
var counter = sinon.spy();
|
||||||
|
helper.getNode("n3").on("input", function(msg) {
|
||||||
|
counter();
|
||||||
|
});
|
||||||
|
|
||||||
|
var func = sinon.stub(currentPB, "history");
|
||||||
|
|
||||||
|
currentPB.streamEmitter.emit("message", {type: "tickle", subtype: "push"});
|
||||||
|
var msg0 = getPushReply('link'); msg0.pushes[0].source_device_iden = 'a';
|
||||||
|
func.onCall(0).yields(false, msg0);
|
||||||
|
|
||||||
|
currentPB.streamEmitter.emit("message", {type: "tickle", subtype: "push"});
|
||||||
|
var msg1 = getPushReply('link'); msg1.pushes[0].source_device_iden = 'b';
|
||||||
|
func.onCall(1).yields(false, msg1);
|
||||||
|
|
||||||
|
currentPB.streamEmitter.emit("message", {type: "tickle", subtype: "push"});
|
||||||
|
var msg2 = getPushReply('link'); msg2.pushes[0].source_device_iden = 'c';
|
||||||
|
func.onCall(2).yields(false, msg2);
|
||||||
|
|
||||||
|
currentPB.streamEmitter.emit("message", {type: "tickle", subtype: "push"});
|
||||||
|
var msg3 = getPushReply('link');
|
||||||
|
delete msg3.pushes[0].source_device_iden;
|
||||||
|
delete msg3.pushes[0].target_device_iden;
|
||||||
|
func.onCall(3).yields(false, msg3);
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
counter.callCount.should.equal(3);
|
||||||
|
done();
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
221
test/social/pushbullet/data.js
Normal file
221
test/social/pushbullet/data.js
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
module.exports = {
|
||||||
|
base: {
|
||||||
|
"accounts": [],
|
||||||
|
"aliases": [],
|
||||||
|
"channels": [],
|
||||||
|
"clients": [],
|
||||||
|
"contacts": [],
|
||||||
|
"devices": [],
|
||||||
|
"grants": [],
|
||||||
|
"pushes": [],
|
||||||
|
"subscriptions": []
|
||||||
|
},
|
||||||
|
modified: {
|
||||||
|
"active": true,
|
||||||
|
"iden": "xXxXxXxXxXxsjArqXRsaZM",
|
||||||
|
"created": 1421834169.9435499,
|
||||||
|
"modified": 1234,
|
||||||
|
"type": "note",
|
||||||
|
"dismissed": false,
|
||||||
|
"sender_iden": "xXxXxXxXxXx",
|
||||||
|
"sender_email": "john.doe@noma.il",
|
||||||
|
"sender_email_normalized": "johndoe@noma.il",
|
||||||
|
"sender_name": "John Doe",
|
||||||
|
"receiver_iden": "xXxXxXxXxXx",
|
||||||
|
"receiver_email": "john.doe@noma.il",
|
||||||
|
"receiver_email_normalized": "johndoe@noma.il",
|
||||||
|
"title": "title",
|
||||||
|
"body": "body"
|
||||||
|
},
|
||||||
|
devices: [
|
||||||
|
{
|
||||||
|
"active": true,
|
||||||
|
"iden": "xXxXxXxXxXxsjAnBdJA0yG",
|
||||||
|
"created": 1418045447.298193,
|
||||||
|
"modified": 1420698259.7963278,
|
||||||
|
"type": "chrome",
|
||||||
|
"kind": "chrome",
|
||||||
|
"nickname": "Chrome",
|
||||||
|
"manufacturer": "Google",
|
||||||
|
"model": "Chrome",
|
||||||
|
"app_version": 157,
|
||||||
|
"pushable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"active": true,
|
||||||
|
"iden": "xXxXxXxXxXxsjAiVsKnSTs",
|
||||||
|
"created": 1418045076.665757,
|
||||||
|
"modified": 1418047786.564935,
|
||||||
|
"type": "ios",
|
||||||
|
"kind": "ios",
|
||||||
|
"nickname": "myphone",
|
||||||
|
"manufacturer": "Apple",
|
||||||
|
"model": "iPhone 5s (Global)",
|
||||||
|
"app_version": 3671,
|
||||||
|
"push_token": "production",
|
||||||
|
"pushable": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
me: {
|
||||||
|
"iden": "xXxXxXxXxXx",
|
||||||
|
"created": 1418045075.2908669,
|
||||||
|
"modified": 1418504323.166974,
|
||||||
|
"email": "john.doe@noma.il",
|
||||||
|
"email_normalized": "johndoe@noma.il",
|
||||||
|
"name": "John Doe",
|
||||||
|
"image_url": "https://photo.jpg",
|
||||||
|
"google_userinfo": {
|
||||||
|
"name": "John Doe"
|
||||||
|
},
|
||||||
|
"preferences": {
|
||||||
|
"onboarding": {
|
||||||
|
"app": false,
|
||||||
|
"extension": false,
|
||||||
|
"friends": true
|
||||||
|
},
|
||||||
|
"social": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
"active": true,
|
||||||
|
"iden": "xXxXxXxXxXxsjArqXRsaZM",
|
||||||
|
"created": 1421834169.9435499,
|
||||||
|
"modified": 1421850263.303394,
|
||||||
|
"type": "note",
|
||||||
|
"dismissed": false,
|
||||||
|
"sender_iden": "xXxXxXxXxXx",
|
||||||
|
"sender_email": "john.doe@noma.il",
|
||||||
|
"sender_email_normalized": "johndoe@noma.il",
|
||||||
|
"sender_name": "John Doe",
|
||||||
|
"receiver_iden": "xXxXxXxXxXx",
|
||||||
|
"receiver_email": "john.doe@noma.il",
|
||||||
|
"receiver_email_normalized": "johndoe@noma.il",
|
||||||
|
"title": "title",
|
||||||
|
"body": "body"
|
||||||
|
},
|
||||||
|
link: {
|
||||||
|
"active": true,
|
||||||
|
"iden": "xXxXxXxXxXxsjxQymYT0qy",
|
||||||
|
"created": 1421834198.374012,
|
||||||
|
"modified": 1421850263.181358,
|
||||||
|
"type": "link",
|
||||||
|
"dismissed": false,
|
||||||
|
"sender_iden": "xXxXxXxXxXx",
|
||||||
|
"sender_email": "john.doe@noma.il",
|
||||||
|
"sender_email_normalized": "johndoe@noma.il",
|
||||||
|
"sender_name": "John Doe",
|
||||||
|
"receiver_iden": "xXxXxXxXxXx",
|
||||||
|
"receiver_email": "john.doe@noma.il",
|
||||||
|
"receiver_email_normalized": "johndoe@noma.il",
|
||||||
|
"title": "title",
|
||||||
|
"url": "url"
|
||||||
|
},
|
||||||
|
file: {
|
||||||
|
"active": true,
|
||||||
|
"iden": "xXxXxXxXxXxsjAjKAwqc5k",
|
||||||
|
"created": 1421842821.5911932,
|
||||||
|
"modified": 1421850262.446063,
|
||||||
|
"type": "file",
|
||||||
|
"dismissed": false,
|
||||||
|
"sender_iden": "xXxXxXxXxXx",
|
||||||
|
"sender_email": "john.doe@noma.il",
|
||||||
|
"sender_email_normalized": "johndoe@noma.il",
|
||||||
|
"sender_name": "John Doe",
|
||||||
|
"receiver_iden": "xXxXxXxXxXx",
|
||||||
|
"receiver_email": "john.doe@noma.il",
|
||||||
|
"receiver_email_normalized": "johndoe@noma.il",
|
||||||
|
"body": "title",
|
||||||
|
"file_name": "filename",
|
||||||
|
"file_type": "text/plain",
|
||||||
|
"file_url": "fileurl"
|
||||||
|
},
|
||||||
|
address: {
|
||||||
|
"active": true,
|
||||||
|
"iden": "xXxXxXxXxXxsjAlHSjtjcO",
|
||||||
|
"created": 1421834217.764878,
|
||||||
|
"modified": 1421850261.531033,
|
||||||
|
"type": "address",
|
||||||
|
"dismissed": false,
|
||||||
|
"sender_iden": "xXxXxXxXxXx",
|
||||||
|
"sender_email": "john.doe@noma.il",
|
||||||
|
"sender_email_normalized": "johndoe@noma.il",
|
||||||
|
"sender_name": "John Doe",
|
||||||
|
"receiver_iden": "xXxXxXxXxXx",
|
||||||
|
"receiver_email": "john.doe@noma.il",
|
||||||
|
"receiver_email_normalized": "johndoe@noma.il",
|
||||||
|
"name": "title",
|
||||||
|
"address": "address"
|
||||||
|
},
|
||||||
|
list: {
|
||||||
|
"active": true,
|
||||||
|
"iden": "xXxXxXxXxXxsjAdWmbcF3Y",
|
||||||
|
"created": 1421834433.677966,
|
||||||
|
"modified": 1421834444.256159,
|
||||||
|
"type": "list",
|
||||||
|
"dismissed": false,
|
||||||
|
"sender_iden": "xXxXxXxXxXx",
|
||||||
|
"sender_email": "john.doe@noma.il",
|
||||||
|
"sender_email_normalized": "johndoe@noma.il",
|
||||||
|
"sender_name": "John Doe",
|
||||||
|
"receiver_iden": "xXxXxXxXxXx",
|
||||||
|
"receiver_email": "john.doe@noma.il",
|
||||||
|
"receiver_email_normalized": "johndoe@noma.il",
|
||||||
|
"title": "title",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"checked": true,
|
||||||
|
"text": "a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"checked": true,
|
||||||
|
"text": "b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"checked": false,
|
||||||
|
"text": "c"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
mirror: {
|
||||||
|
"type": "mirror",
|
||||||
|
"iden": "pjgzwwn3YPQ",
|
||||||
|
"source_device_iden": "ubddjAzSDVLgrI",
|
||||||
|
"package_name": "com.pushbullet.android",
|
||||||
|
"application_name": "Pushbullet",
|
||||||
|
"notification_id": "-8",
|
||||||
|
"notification_tag": null,
|
||||||
|
"dismissable": true,
|
||||||
|
"title": "Mirroring test",
|
||||||
|
"body": "If you see this on your computer, mirroring is working!\n",
|
||||||
|
"icon": "iVBORw0KGgoAAAANSUhEBgUzC42AAAADNElEQVRo\ng==\n",
|
||||||
|
"created": 1399350964.1649699,
|
||||||
|
"sender_iden": "ubd",
|
||||||
|
"sender_email": "ryan@pushbullet.com",
|
||||||
|
"sender_email_normalized": "ryan@pushbullet.com",
|
||||||
|
"receiver_iden": "ubd",
|
||||||
|
"receiver_email": "ryan@pushbullet.com",
|
||||||
|
"receiver_email_normalized": "ryan@pushbullet.com"
|
||||||
|
},
|
||||||
|
dismissal: {
|
||||||
|
"type": "dismissal",
|
||||||
|
"iden": "pjgzwwocCCy",
|
||||||
|
"source_device_iden": "ubddjAzSDVLgrI",
|
||||||
|
"package_name": "com.pushbullet.android",
|
||||||
|
"notification_id": "-8",
|
||||||
|
"notification_tag":null,
|
||||||
|
"created":1399350966.22458,
|
||||||
|
"sender_iden": "ubd",
|
||||||
|
"sender_email": "ryan@pushbullet.com",
|
||||||
|
"sender_email_normalized": "ryan@pushbullet.com",
|
||||||
|
"receiver_iden": "ubd",
|
||||||
|
"receiver_email": "ryan@pushbullet.com",
|
||||||
|
"receiver_email_normalized": "ryan@pushbullet.com"
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"active": false,
|
||||||
|
"iden": "pjgzwwocCCy",
|
||||||
|
"created": 1422525494.4456809,
|
||||||
|
"modified": 1422526124.538504,
|
||||||
|
"dismissed": false
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user