ssb-mobile-bluetooth-manager
Advanced tools
Comparing version 1.0.0 to 1.0.1
190
index.js
const pull = require('pull-stream'); | ||
const Pushable = require('pull-pushable'); | ||
const WebSocket = require('ws'); | ||
// Available as a built-in module in the nodejs environment | ||
// see https://code.janeasystems.com/nodejs-mobile/getting-started-react-native | ||
const rn_bridge = require('rn-bridge'); | ||
const Abortable = require('pull-abortable'); | ||
/** | ||
* A plugin that communicates with the native thread that is doing the native | ||
* bluetooth management across the react bridge to manage connections, data reads | ||
* and writes. | ||
*/ | ||
function makeManager () { | ||
// A map of remote device mac address to the duplex stream for reading data data | ||
// from (the source) and sending data to (the sink) | ||
const connections = { | ||
function connect(address, cb) { | ||
}; | ||
console.log("Attempting outgoing ws connection"); | ||
// A map of devices we're awaiting an outgoing connection for. Key: device address, | ||
// value: errBack to give the connection. | ||
const awaitingConnection = { | ||
var pushable = Pushable(); | ||
}; | ||
var ws = new WebSocket("ws://localhost:5666"); | ||
// A callback to call to hand over an incoming connection duplex stream | ||
// to multiserver. | ||
let onIncomingConnection = null; | ||
var duplexStream = null; | ||
var abortable = Abortable(); | ||
function onConnect(params) { | ||
console.log("puppet: incoming connection"); | ||
console.log(params); | ||
ws.on('open', function (event) { | ||
const deviceAddress = params.remoteAddress; | ||
// Tell the websocket bridge where to connect | ||
ws.send(address); | ||
// Source: reading from the remote device | ||
// Sink: writing to the remote device | ||
const duplexStream = { | ||
source: Pushable(), | ||
sink: pull.drain( (msg) => { | ||
duplexStream = { | ||
source: pushable, | ||
sink: createWebsocketSink(ws, abortable) | ||
}; | ||
var bridgeMsg = { | ||
type: "write", | ||
params: { | ||
// the data is a byte array so encode as a base64 string to send over bridge | ||
data: msg.toString('base64'), | ||
remoteAddress: deviceAddress | ||
} | ||
} | ||
console.dir(duplexStream); | ||
rn_bridge.channel.send(JSON.stringify(bridgeMsg)); | ||
}) | ||
} | ||
cb(null, duplexStream) | ||
}); | ||
connections[deviceAddress] = duplexStream; | ||
ws.on('message', function(data) { | ||
console.log(Buffer.from(data, 'base64').toString()); | ||
if (onIncomingConnection && params.isIncoming) { | ||
// Pass the duplex stream to multiserv via the callback that was given | ||
// to us in our 'server' function implementation | ||
onIncomingConnection(null, duplexStream); | ||
} else { | ||
const awaiting = awaitingConnection[params.remoteAddress]; | ||
if (!awaiting) { | ||
console.log("Unexpectedly got a connection to a device we were not waiting on."); | ||
} | ||
pushable.push(Buffer.from(data, 'base64')); | ||
}) | ||
connections[deviceAddress] = duplexStream; | ||
awaiting(null, duplexStream); | ||
ws.on('error', function() { | ||
console.log("connection ended with error to: " + address); | ||
duplexStream.source.end(); | ||
abortable.abort(); | ||
}); | ||
delete awaitingConnection[deviceAddress]; | ||
} | ||
} | ||
function onConnectionFailed(params) { | ||
console.log("puppet: failed connection: " + params.remoteAddress); | ||
ws.on('close', function() { | ||
console.log("connection closed to: " + address); | ||
const deviceAddress = params.remoteAddress; | ||
const awaiting = awaitingConnection[deviceAddress]; | ||
duplexStream.source.end(); | ||
abortable.abort(); | ||
}); | ||
awaiting("Could not connect to bluetooth address: " + deviceAddress); | ||
return function () { | ||
console.log("todo") | ||
} | ||
delete awaitingConnection[deviceAddress]; | ||
} | ||
function onConnectionLost(params) { | ||
console.log("puppet: connection lost"); | ||
console.log(params); | ||
const deviceAddress = params.remoteAddress; | ||
function listenForIncomingConnections(onConnection) { | ||
const duplexStream = connections[deviceAddress]; | ||
const wss = new WebSocket.Server({ port: 5667 }); | ||
if (duplexStream) { | ||
// todo: is this enough to signal to multiserv to break the connection? | ||
duplexStream.source.end(); | ||
delete connections[deviceAddress]; | ||
} | ||
} | ||
wss.on('connection', function connection(ws) { | ||
function onDataRead(params) { | ||
const deviceAddress = params.remoteAddress; | ||
const data = params.data; | ||
var abortable = Abortable(); | ||
const duplexStream = connections[deviceAddress]; | ||
var source = Pushable(); | ||
var sink = createWebsocketSink(ws, abortable); | ||
if (duplexStream) { | ||
// Decode data from base64 string to buffer | ||
duplexStream.source.push(Buffer.from(data, 'base64')); | ||
} else { | ||
console.log("Unexpectedly didn't find address in device map.") | ||
} | ||
ws.on('message', function incoming(message) { | ||
source.push(Buffer.from(message, 'base64')); | ||
}); | ||
} | ||
ws.on('close', function() { | ||
source.end(); | ||
abortable.abort(); | ||
}); | ||
onConnection(null, { | ||
source: source, | ||
sink: sink | ||
}) | ||
}); | ||
function listenForIncomingConnections(cb) { | ||
// We use this callback to handle back any duplex streams for incoming | ||
// connections. | ||
onIncomingConnection = cb; | ||
var bridgeMsg = { | ||
@@ -127,40 +95,24 @@ type: "listenIncoming", | ||
rn_bridge.channel.send(JSON.stringify(bridgeMsg)); | ||
} | ||
function connect(address, cb) { | ||
function createWebsocketSink(ws, abortable) { | ||
return pull( | ||
abortable, | ||
pull.map(buf => buf.toString('base64')), | ||
pull.drain(msg => { | ||
// Store that we're awaiting a connection event to come back over the bridge | ||
awaitingConnection[address] = cb; | ||
try { | ||
ws.send(msg) | ||
} catch (error) { | ||
console.log(error); | ||
abortable.abort(); | ||
var bridgeMsg = { | ||
type: "connectTo", | ||
params: { | ||
remoteAddress: address | ||
} | ||
} | ||
rn_bridge.channel.send(JSON.stringify(bridgeMsg)); | ||
// todo: how to abort sink / streams? | ||
} | ||
}) | ||
) | ||
} | ||
function listenForBridgeEvents() { | ||
rn_bridge.channel.on('message', (msg) => { | ||
var message = JSON.parse(msg); | ||
if (message.type === "connectionSuccess") { | ||
onConnect(message.params); | ||
} else if (message.type === "connectionLost") { | ||
onConnectionLost(message.params); | ||
} else if (message.type === "connectionFailed") { | ||
onConnectionFailed(message.params); | ||
} | ||
else if (message.type === "read") { | ||
onDataRead(message.params); | ||
} | ||
}); | ||
} | ||
listenForBridgeEvents(); | ||
return { | ||
@@ -167,0 +119,0 @@ connect, |
{ | ||
"name": "ssb-mobile-bluetooth-manager", | ||
"version": "1.0.0", | ||
"version": "1.0.1", | ||
"description": "A module for managing bluetooth connections over a react native bridge.", | ||
@@ -16,5 +16,13 @@ "main": "index.js", | ||
"dependencies": { | ||
"pull-abortable": "^4.1.1", | ||
"pull-cat": "^1.1.11", | ||
"pull-pushable": "^2.2.0", | ||
"pull-stream": "^3.6.9" | ||
"pull-stream": "^3.6.9", | ||
"pull-ws": "^3.3.1", | ||
"websocket": "^1.0.26" | ||
}, | ||
"react-native": { | ||
"os": "@staltz/react-native-os", | ||
"rn-viewpager": "@staltz/rn-viewpager" | ||
} | ||
} |
4277
6
85
+ Addedpull-abortable@^4.1.1
+ Addedpull-cat@^1.1.11
+ Addedpull-ws@^3.3.1
+ Addedwebsocket@^1.0.26
+ Addedbufferutil@4.0.9(transitive)
+ Addedd@1.0.2(transitive)
+ Addeddebug@2.6.9(transitive)
+ Addedes5-ext@0.10.64(transitive)
+ Addedes6-iterator@2.0.3(transitive)
+ Addedes6-symbol@3.1.4(transitive)
+ Addedesniff@2.0.1(transitive)
+ Addedevent-emitter@0.3.5(transitive)
+ Addedext@1.7.0(transitive)
+ Addedis-typedarray@1.0.0(transitive)
+ Addedms@2.0.0(transitive)
+ Addednext-tick@1.1.0(transitive)
+ Addednode-gyp-build@4.8.4(transitive)
+ Addedoptions@0.0.6(transitive)
+ Addedpull-abortable@4.1.1(transitive)
+ Addedpull-cat@1.1.11(transitive)
+ Addedpull-ws@3.3.2(transitive)
+ Addedrelative-url@1.0.2(transitive)
+ Addedsafe-buffer@5.2.1(transitive)
+ Addedtype@2.7.3(transitive)
+ Addedtypedarray-to-buffer@3.1.5(transitive)
+ Addedultron@1.0.2(transitive)
+ Addedutf-8-validate@5.0.10(transitive)
+ Addedwebsocket@1.0.35(transitive)
+ Addedws@1.1.5(transitive)
+ Addedyaeti@0.0.6(transitive)