Nodejs lookup of known bluetooth device - javascript

Is it possible to perform a lookup of a Bluetooth device given its address in a Nodejs script?
There are a few packages out there, the main one being Noble. However, they all focus around scanning, and not looking up a known address (as far as i can tell anyway!).
What i want to achieve, is to look up a known address, to see if the device can be found.
Much like PyBluez does for Python:
bluetooth.lookup_name('CC:20:E8:8F:3A:1D', timeout=5)
In Python, this can find the device even if it is undiscoverable, unlike a typical inquiry scan would.

I had this same problem and just found the btwatch lib, but it isn't working for me on the latest raspbian. But the source is just calling l2ping and looking for a string that I'm guessing no longer prints on success, so the modified code below works instead, similar to the lookup_name method, once you have l2ping installed (I think npm bluetooth or pybluez has it)
var Spawn = require('child_process').spawn;
function detectMacAddress(macAddress, callback)
{
//var macAddress = '72:44:56:05:79:A0';
var ls = Spawn('l2ping', ['-c', '1', '-t', '5', macAddress]);
ls.stdout.on('data', function (data) {
console.log("Found device in Range! " + macAddress);
callback(true);
});
ls.on('close', function () {
console.log("Could not find: " + macAddress);
callback(false);
});
}
Or, a synchronous way,
var execSync = require('child_process').execSync;
function detectMacAddressSync(macAddress)
{
var cmd = 'l2ping -c 1 -t 5 ' + macAddress;
try
{
var output = execSync(cmd );
console.log("output : "+ output );
return true;
}
catch(e)
{
console.log("caught: " + e);
return false;
}
}

As far as I have understood the problem you want to connect to the device using address. Then, I would suggest using node-bluetooth-serial-port.
var btSerial = new (require('bluetooth-serialport')).BluetoothSerialPort();
btSerial.on('found', function(address, name) {
btSerial.findSerialPortChannel(address, function(channel) {
btSerial.connect(address, channel, function() {
console.log('connected');
btSerial.write(new Buffer('my data', 'utf-8'), function(err, bytesWritten) {
if (err) console.log(err);
});
btSerial.on('data', function(buffer) {
console.log(buffer.toString('utf-8'));
});
}, function () {
console.log('cannot connect');
});
// close the connection when you're ready
btSerial.close();
}, function() {
console.log('found nothing');
});
});
BluetoothSerialPort.findSerialPortChannel(address, callback[, errorCallback])
Checks if a device has a serial port service running and if it is found it passes the channel id to use for the RFCOMM connection.
callback(channel) - called when finished looking for a serial port on the device.
errorCallback - called the search finished but no serial port channel was found on the device. Connects to a remote bluetooth device.
bluetoothAddress - the address of the remote Bluetooth device.
channel - the channel to connect to.
[successCallback] - called when a connection has been established.
[errorCallback(err)] - called when the connection attempt results in an error. The parameter is an Error object.

Related

Web bluetooth Notification only few response

I am learning use web bluetooth api to write web app and use chrome/chromium run it .
But the notification only response few times, I don't know why and how to debug it(to see what happened).
The bluetooth peripheral is an oximeter, use BLE to send real-time spo2, heart rate, etc. And my browser use Chromium 60.0.3112.78 built on Debian 9.1, running on Debian 9.1 (64 bit) .
Below is my javascript:
var serviceUuid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
characteristicUuid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" ;
// Sorry, I hide the UUID.
document.querySelector('#button').addEventListener('click', function(event) {
onStartButtonClick();
}});
async function onStartButtonClick(){
let options = {};
options.acceptAllDevices = true;
options.optionalServices = [serviceUuid];
try{
const device = await navigator.bluetooth.requestDevice(options);
device.addEventListener('gattserverdisconnected', onDisconnected);
console.log('Got device:', device.name);
console.log('id:', device.id);
console.log('Connecting to GATT Server...');
const server = await device.gatt.connect();
console.log('Getting Service...');
const service = await server.getPrimaryService(serviceUuid);
console.log('Getting Characteristic...');
myCharacteristic = await service.getCharacteristic(characteristicUuid);
myCharacteristic.addEventListener('characteristicvaluechanged',
handleNotifications);
await myCharacteristic.startNotifications();
console.log('> Notifications started');
} catch(error) {
console.log('Argh! ' + error);
}
}
async function disconnect(){
await myCharacteristic.stopNotifications();
onDisconnected();
}
function onDisconnected(event) {
// Object event.target is Bluetooth Device getting disconnected.
console.log('> Bluetooth Device disconnected');
}
var tmp_count=0;
async function handleNotifications(event) {
// I will read data by Uint8Array.
// var databuf = new Uint8Array(event.target.value.buffer);
tmp_count++;
console.log(tmp_count);
}
Console of chromium display:
03:41:49.893 (index):192 Connecting to GATT Server...
03:41:50.378 (index):195 Getting Service...
03:41:51.237 (index):198 Getting Characteristic...
03:41:51.359 (index):204 > Notifications started
03:41:51.781 (index):228 1
03:41:51.782 (index):228 2
03:42:22.573 (index):217 > Bluetooth Device disconnected
It's no response after 03:41:51.782 (index):228 2 , so I turn turn off oximeter.
What is the problem ? And what can I do ?
Thanks.
First, note that Chrome supports Android, Chrome OS, macOS at this time.
Try using some of the tips on https://www.chromium.org/developers/how-tos/file-web-bluetooth-bugs such as chrome://bluetooth-internals,
nRF Connect for Android or nRF Connect for iOS
If you can receive continuous notifications in those apps, but not in Chrome, there's a bug. Please file details on how to reproduce it, logs you've collected, there: https://crbug.com/new

Parse SDK JavaScript with Node.js ENOTFOUND

So I want to fetch a large amount of data on Parse, a good solution I found is to make a recursive function: when the data are successfully found, launch another request. The way I'm doing it is pretty simple:
var containedLimit = 1000, // Also tried with 500, 300, and 100
parseUsersWaiting = {
// A lot of Users
},
parseUsers = {}, // Recipt for Users
getPlayers = function (containedIn) {
var count = 0;
if (containedIn == undefined) {
containedIn = [];
for (var i in parseUsersWaiting) {
count++;
if (count > containedLimit) {
break;
}
containedIn.push(parseUsersWaiting[i].id);
delete parseUsersWaiting[i];
}
}
var UserObject = Parse.Object.extend('User'),
UserQuery = new Parse.Query(UserObject);
UserQuery.limit(containedLimit);
UserQuery.containedIn('objectId', containedIn);
UserQuery.find({
success: function (results) {
if (results) {
for (var i in results) {
if (parseUsers[results[i].id] == undefined) {
parseUsers[results[i].id] = results[i];
}
// Other stuff
if (results.length < containedLimit) {
// End of process
} else {
getPlayers();
}
}
} else {
// Looks like an end of process too
}
}, error: function (error) {
console.log(error.message);
getPlayers(containedIn);
}
});
};
Now, here is what I would like to call the "issue": it happen, very frequently, that the "error" callback is launched with this:
Received an error with invalid JSON from Parse: Error: getaddrinfo ENOTFOUND api.parse.com
at errnoException (dns.js:44:10)
at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:94:26)
(With code 107, of course) So I searched on the Parse Documentation, and it says the exact same thing: "Received an error with invalid JSON". Yeah.
I'm using the Parse SDK provided by Parse.com (npm install parse)
I also tried to modify a bit the Parse code with replacing the host key by 'hostname' on line 361 of the file parse/node_modules/xmlhttprequest/lib/XMLHttpRequest.js (Parse package version: 1.5.0), and it didn't worked, so I removed my changes.
(By the way, I found a solution talking about using ulimit to change memory usage limit that could solve the problem, but actually, I haven't the necessary rights to execute this command)
This error occurs when it can not connect to the API (technically when it cannot lookup the IP address of the API server). Perhaps your internet connection was lost for a brief moment or their SDK server were unavailable (or maybe the server is denying your request due to rate limits).
Either way it is good to code some resilience into your application. I see your on error function retries the API call, but perhaps it would be worth adding a timeout before you do that to give the problem a chance to recover?
error: function (error) {
console.log(error.message);
setTimeout(getPlayers, 10000, containedIn);
}
If the sever is rate limiting your requests, this will also help.

Cordova: Sending Data via TCP

I'm trying to send data between an Android application created with Cordova and an application running on a Windows machine; the application on the Windows machine requires TCP protocol. I've worked with Cordova before, but I haven't used many plugins for Cordova--and therefore I've had difficulty setting up plugins--and socket programming isn't familiar ground for me.
Anywho, I read that I could use chrome.socket with Cordova somewhere, so I've tried doing that, but I'm only receiving errors indicating that "chrome" is undefined.
I first installed the plugin via command line: cordova plugin add org.chromium.socket
It then appeared in the plugins directory of my Cordova application.
Then, I included the following code in my application:
var dataString = "Message to send";
var data = new Uint8Array(dataString.length);
for (var i = 0; i < data.length; i++) {
data[i] = dataString.charCodeAt(i);
}
try {
chrome.socket.create("tcp", function(createInfo) {
var socketId = createInfo.socketId;
try {
chrome.socket.connect(socketId, hostname, port, function(result) {
if (result === 0) {
chrome.socket.write(socketId, data, function(writeInfo) {
chrome.socket.read(socketId, 1000, function(readInfo) {
});
});
}
else
{
alert("CONNECT FAILED");
}
});
}
catch (err)
{
alert("ERROR! " + err);
}
});
}
catch (err)
{
alert("ERROR! " + err);
}
I get the error alert every time, because chrome.socket is undefined; this makes me think that I did not set this up correctly. At any rate, any advice would be greatly appreciated.

Simulating linux terminal in browser

I have read about Fabrice Bellard's linux simulation in browser.
How does Linux emulator in Javascript by Fabrice Bellard work?
Today I stumbled upon this site, where they are simulating full linux terminal in browser, I am able to run python, perl etc. I know they are running their site on node.js, but I couldn't figure out how they exactly simulating the terminal.
http://runnable.com/UWRl3KlLuONCAACG/read-files-from-filesystem-in-python
The full linux is http://docker.io, the rest is https://github.com/Runnable/dockworker
We're not simulating the terminal but as Kyle says, replicating the terminal over websockets (with an ajax fallback).
In the browser we're using https://github.com/chjj/term.js which was derived from Fabrice Bellard's emulator. It handles the output, and also the keystroke capture.
Let me prefix this by saying it is NOT a good idea to do this.
But, You can spawn a shell and use web-sockets or XMLHttpRequests to push keypresses to the spawned server process. Here's a working example of one that runs on windows. Unfortunately, I didn't get around to hooking up / figuring out Ctrl+c. But, you should get the gist of it.
require("underscore");
var Server = {},
express = require("express"),
path = require("path"),
sys = require("sys"),
application_root = __dirname;
global.Server = Server;
Server.root = application_root;
global.app = express();
Server.setup = require("./lib/setup.js").setup({
//redis: require("./lib/redis-client").createClient(),
app: app,
//mongoose : require("mongoose"),
io : require("socket.io"),
express : express,
port: 1773,
paths : {
views : path.join(application_root,"app","views"),
root : path.join(application_root,"public"),
controllers : path.join(application_root,"app","controllers"),
models : path.join(application_root,"app","models")
}
});
var proc = require('child_process'),
cmd;
app.socket.on('connection', function(socket) {
if (!cmd) {
//console.log('spawning cmd');
cmd = proc.spawn('cmd');
//console.log(cmd?'CMD started':'CMD not started');
if (cmd.stdout) {
//console.log('stdout present');
cmd.stdout.on('data',function(data) {
if (data) {
//console.log("data: "+data);
socket.emit('cmd', ""+data);
}
});
}
if (cmd.stderr) {
cmd.stderr.on('data', function(data) {
//console.log('stderr present');
if (data) {
socket.emit('cmd', ""+data);
}
});
}
cmd.on('exit', function() {
//console.log('cmd exited');
socket.emit('cmd', '[CMD Shutdown]');
if (cmd) {
cmd.kill();
cmd = null;
}
});
}
socket.on('sendCmd', function(data) {
if (data && data.buffer) {
var kB = data.buffer.replace("\r","\n");
if (cmd && cmd.stdin) {
cmd.stdin.write(kB);
}
}
});
socket.on('disconnect', function() {
console.log('connection closed');
if (cmd) {
cmd.stdin.end(); //.kill();
if (cmd) {
cmd.kill();
cmd = null;
}
}
});
});
Edit: Actually, this is a portion of a working example. It's missing the client side where you capture and send the keystrokes to the server. But, it should give you the general idea.

Chrome packaged app UDP sockets not working

I'm trying to get UDP sockets working for a packaged app using Chrome Canary (currently version 25). I am pretty confused by the fact the UDP example here conflicts with the reference documentation here.
The official example uses this line:
chrome.socket.create('udp', '127.0.0.1', 1337, { onEvent: handleDataEvent }, ...
In Canary using this line results in the error:
Uncaught Error: Invocation of form socket.create(string, string,
integer, object, function) doesn't match definition
socket.create(string type, optional object options, function callback)
Not surprising since that matches the documented form of the function. (I guess the example is out of date?) OK, so I try this...
chrome.socket.create('udp', { onEvent: handleDataEvent }, ...
Canary complains:
Uncaught Error: Invalid value for argument 2. Property 'onEvent':
Unexpected property.
Now I'm confused, especially since this parameter is undocumented in the reference. So I just go with this:
chrome.socket.create('udp', {}, ...
Now it creates OK, but the following call to connect...
chrome.socket.connect(socketId, function(result) ...
...fails with this:
Uncaught Error: Invocation of form socket.connect(integer, function)
doesn't match definition socket.connect(integer socketId, string
hostname, integer port, function callback)
...which is not surprising, since now my code doesn't mention a host or port anywhere, so I guess it needs to be in connect. So I change it to the form:
chrome.socket.connect(socketId, address, port, function (result) ...
At last I can connect and write to the socket OK. But this doesn't cover reading.
Can someone show me a working example based on UDP that can send & receive, so I can work from that?
How do I receive data since the example's onEvent handler does not work? How do I ensure I receive any data on-demand as soon as it arrives without blocking?
The Network Communications doc is not up-to-date. See the latest API doc: https://developer.chrome.com/trunk/apps/socket.html. But the doc doesn't state everything clearly.
I looked into Chromium source code and found some useful comments here: https://code.google.com/searchframe#OAMlx_jo-ck/src/net/udp/udp_socket.h&q=file:(%5E%7C/)net/udp/udp_socket%5C.h$&exact_package=chromium
// Client form:
// In this case, we're connecting to a specific server, so the client will
// usually use:
// Connect(address) // Connect to a UDP server
// Read/Write // Reads/Writes all go to a single destination
//
// Server form:
// In this case, we want to read/write to many clients which are connecting
// to this server. First the server 'binds' to an addres, then we read from
// clients and write responses to them.
// Example:
// Bind(address/port) // Binds to port for reading from clients
// RecvFrom/SendTo // Each read can come from a different client
// // Writes need to be directed to a specific
// // address.
For the server UDP socket, call chrome.socket.bind and chrome.socket.recvFrom/chrome.socket.sendTo to interact with clients. For the client UDP socket, call chrome.socket.connect and chrome.socket.read/chrome.socket.write to interact with the server.
Here's an example:
var serverSocket;
var clientSocket;
// From https://developer.chrome.com/trunk/apps/app_hardware.html
var str2ab=function(str) {
var buf=new ArrayBuffer(str.length);
var bufView=new Uint8Array(buf);
for (var i=0; i<str.length; i++) {
bufView[i]=str.charCodeAt(i);
}
return buf;
}
// From https://developer.chrome.com/trunk/apps/app_hardware.html
var ab2str=function(buf) {
return String.fromCharCode.apply(null, new Uint8Array(buf));
};
// Server
chrome.socket.create('udp', null, function(createInfo){
serverSocket = createInfo.socketId;
chrome.socket.bind(serverSocket, '127.0.0.1', 1345, function(result){
console.log('chrome.socket.bind: result = ' + result.toString());
});
function read()
{
chrome.socket.recvFrom(serverSocket, 1024, function(recvFromInfo){
console.log('Server: recvFromInfo: ', recvFromInfo, 'Message: ',
ab2str(recvFromInfo.data));
if(recvFromInfo.resultCode >= 0)
{
chrome.socket.sendTo(serverSocket,
str2ab('Received message from client ' + recvFromInfo.address +
':' + recvFromInfo.port.toString() + ': ' +
ab2str(recvFromInfo.data)),
recvFromInfo.address, recvFromInfo.port, function(){});
read();
}
else
console.error('Server read error!');
});
}
read();
});
// A client
chrome.socket.create('udp', null, function(createInfo){
clientSocket = createInfo.socketId;
chrome.socket.connect(clientSocket, '127.0.0.1', 1345, function(result){
console.log('chrome.socket.connect: result = ' + result.toString());
});
chrome.socket.write(clientSocket, str2ab('Hello server!'), function(writeInfo){
console.log('writeInfo: ' + writeInfo.bytesWritten +
'byte(s) written.');
});
chrome.socket.read(clientSocket, 1024, function(readInfo){
console.log('Client: received response: ' + ab2str(readInfo.data), readInfo);
});
});

Categories