-
Notifications
You must be signed in to change notification settings - Fork 4
/
index.js
183 lines (146 loc) · 6.16 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
'use strict';
var WebSocket = require('ws');
var Config = require('./config/bitpepsi.json') // JSON configuration file for the application
var merge = require('merge');
var btcprice = require('./lib/btcprice'); // realtime xbt market price in CAD
var logger = require('winston'); // file and console loggin
var argv = require('minimist')(process.argv.slice(2));
var async = require('async');
if(!argv.nogpio) var gpio = require('pi-gpio'); // you must run this script on a raspberry pi for this to work
/*
1. establish websocket connection
2. from config data, being monitoring bitcoin wallets
3. if a deposit is detected, check that it is the right amount. if it is, energize the GPIO for 3 seconds.
utility functions:
- energize gpio up/down
- gpio cycle test
- acquire live bitcoin price from bitstamp in USD
*/
// initiate logger
logger.add(logger.transports.File, { filename: './log/bitpepsi.log' });
if(!(Config.debug != 'true' || argv.debug)) logger.remove(logger.transports.Console);
// get xbt market price,keep updating it.
var currentPrice = {};
btcprice.updatePrice(function(x,results) {
currentPrice.val = results.last;
currentPrice.updated = results.timestamp;
});
logger.info('Establising web socket...');
var conn = new WebSocket(Config.websocket);
var x = Config.wallets;
// watch all wallets in the config file
async.each(x, viewWallet, console.error);
/* ============= */
// Start Wallet Check
function viewWallet(wallet, done) {
async.auto({
wallet: function(done){done(null, wallet)}, // draws the wallet config details into async
open: ['wallet', opensocket],
watch: ['open', watchwallet]
}, done)
};
// open connection(s)
function opensocket(done, results) {
// wallet configuration details
var wallet = results.wallet
// define address
var req = {type: "address", address:wallet.pubkey, block_chain: "bitcoin"};
// open, activate the connection
conn.on('open', function () {
logger.info('Websocket opened.');
conn.send(JSON.stringify(req), function (err) {
if (err) {
done(new Error('Unable to connect to Websocket'));
}
else
{
logger.info("Connection successful.");
done();
}
});
});
// connection errors
conn.on('error', done)
// re-connect
// this is NOT TESTED.
conn.on('close',function (wallet) {
logger.info("Connection lost. Reconnecting in 10 seconds....");
setTimeout(opensocket(wallet, done),10000);
});
}
function watchwallet(done, results) {
var wallet = results.wallet; // wallet{} and open{}
logger.info("Watching address "+ wallet.pubkey +" for a deposit value of $" + wallet.itemcost);
// this stays active and waits for a deposit
conn.on('message', function (data, flags) {
var activity = JSON.parse(data); // parse the websocket reponse
//console.log(activity);
if (activity.payload.type == "address" && activity.payload.received != 0) {
results = merge(results,activity);
validateDeposit(done,results);
} else if (activity.payload.type == "heartbeat") {
logger.debug("Tick Tock. Current price: $"+currentPrice.val+" CAD. Last updated: " + currentPrice.updated);
}
});
}
function validateDeposit(done, results) {
var response = {};
var wallet = results.wallet;
var activity = results.payload;
if( wallet.pubkey == activity.address ) {
if( activity.confirmations != 0 ) {
// this transaction is already confirmed
logger.error("Transaction is not new. "+activity.confirmations+" confirmations exist.");
} else {
logger.info('Validating deposit..');
response.usd = currentPrice.val; // API price in CAD
response.received = activity.received/100000000*currentPrice.val; // Amount detected, converted to CAD
response.expected = wallet.itemcost;
logger.info("price: $%s; received: $%s; expected: $%s; tolerance: $%s",response.usd,response.received,response.expected,wallet.pricetolerance);
if((response.expected - response.received) > wallet.pricetolerance) {
logger.error("Value was not the expected amount. Expected: $" + response.expected + " Received: $" + response.received);
} else {
logger.info("#####################");
logger.info("Transaction is VALID.");
logger.info("#####################");
energize(done,wallet);
}
}
}
}
// GPIO controls for the Raspberry Pi Controller
function energize(done, results) {
var pin = results.gpio;
var duration = results.gpiocycletime;
if(!argv.nogpio) {
gpio.open(pin, "output", function(err) { // Open pin output
if(err) {
// likely what's happened here is that port is still open from a previous session. let's close it and recycle.
gpio.close(pin, function(err) {
done(new Error("We're having trouble closing the GPIO.", err))
});
}
gpio.write(pin,1, function high(err) { // 1=high 0=low
logger.info("GPIO "+pin+" triggered for "+duration+" ms");
setTimeout(function delaypin(err) {
gpio.write(pin,0, function low(err) {
if(err) done(err);
gpio.close(pin, function closepin(err) {
if (err) {
done(new Error("There was an error closing the GPIO."));
}
else
{
logger.info("GPIO close successful.");
done();
}
});
}); // how long in ms do we keep the GPIO 'high'
},duration);
});
});
} else {
logger.info('Bypassing GPIO energize due to testing parameters.');
done();
}
}