Unable to get Notify data using Noble - javascript

Can't receive any notifications sent from the Server peripheral.
I am using ESP32 as Server with the "BLE_notify" code that you can find in the Arduino app (File> Examples ESP32 BLE Arduino > BLE_notify).
With this code the ESP32 starts notifying new messages every second once a Client connects.
The client used is a Raspberry Pi with Noble node library installed on it (https://github.com/abandonware/noble). this is the code I am using.
noble.on('discover', async (peripheral) => {
console.log('found peripheral:', peripheral.advertisement);
await noble.stopScanningAsync();
await peripheral.connectAsync();
console.log("Connected")
try {
const services = await peripheral.discoverServicesAsync([SERVICE_UUID]);
const characteristics = await services[0].discoverCharacteristicsAsync([CHARACTERISTIC_UUID])
const ch = characteristics[0]
ch.on('read', function(data, isNotification) {
console.log(isNotification)
console.log('Temperature Value: ', data.readUInt8(0));
})
ch.on('data', function(data, isNotification) {
console.log(isNotification)
console.log('Temperature Value: ', data.readUInt8(0));
})
ch.notify(true, function(error) {
console.log(error)
console.log('temperature notification on');
})
} catch (e) {
// handle error
console.log("ERROR: ",e)
}
});
SERVICE_UUID and CHARACTERISTIC_UUID are obviously the UUIDs coded in the ESP32.
This code sort of works, it can find Services and Characteristics and it can successfully connect to the peripheral, but it cannot receive messages notifications.
I also tried an Android app that works as client, from that app I can get all the messages notified by the peripheral once connected to it. So there is something missing in the noBLE client side.
I think there is something wrong in the on.read/on.data/notify(true) callback methods. Maybe these are not the methods to receive notifications from Server?
I also tried the subscribe methods but still not working.
The official documentation is not clear. Anyone could get it up and running? Please help.

on.read/on.data/ are event listeners. There is nothing wrong with them. They are invoked when there is a certain event.
For example adding characteristic.read([callback(error, data)]); would have invoked the on.read.
From the source:
Emitted when:
Characteristic read has completed, result of characteristic.read(...)
Characteristic value has been updated by peripheral via notification or indication, after having been enabled with
characteristic.notify(true[, callback(error)])

I resolve using the following two envs NOBLE_MULTI_ROLE=1 and NOBLE_REPORT_ALL_HCI_EVENTS=1 (see the documentation https://github.com/abandonware/noble)

Related

Inserting ACL after creating Google Room Resource frequently throws error

I have a Google Cloud function which first creates a Google Room Resource using resources.calendars.insert method from the Google admin sdk,
and right after I try to insert an ACL using Acl: insert method from the google calendar api.
Similar to the following code:
const AdminService = google.admin({version: 'directory_v1'});
try {
const response = await AdminService.resources.calendars.insert(options); // options omitted
} catch (error) {
console.log(`Google Room Resource FAIL`);
console.error(error.message);
}
await new Promise((r) => setTimeout(r, 10000));
const CalendarService = google.calendar({version: 'v3'});
try {
const res = await CalendarService.acl.insert(option); // options omitted
console.log(res);
} catch (error) {
console.log(error);
throw new Error(error.message);
}
As for the authentication, I am using a service account with the correct scopes which impersionates an admin user with the correct permissions. This is how I generate the required JWT token:
const generateJWT = async (scope:string[])=>{
const jwtClient = new google.auth.JWT(
client_email, // service account
undefined,
private_key,
scope,
subject // admin user
);
return jwtClient;
}
In the options parameter for each api call I directly acquire the token for the auth attribute like this:
const option = {
'calendarId': acl.calendarId,
'auth': await generateJWT('https://www.googleapis.com/auth/calendar'),
'resource': {
'role': acl.role,
'scope': {
'type': acl.scopeType,
'value': acl.scopeValue,
},
},
};
Since I await all api calls, I thought that I will only get the response back when everything is already propagated in Google Workspace but when I do not use the setTimeout in between I always get an Error: Not Found back.
First I had the timeout set to 5 seconds which worked until it didn't so I moved it up to 10 seconds. This worked quite long but now I get again sometimes the Not Found error back...
I don't like the setTimeout hack...and even less if it does not work reliable, so how should I deal with this asynchronous behavior without spinning up any other infrastructure like queues or similar?
Working with Google Workspace Calendar Resource
As a Super Admin on my organization when creating a Calendar Resource, from the API or the Web interface, it could take up to 24 hours to correctly propagate the information of the Calendar for the organizations, which generally affect the time it would take for any application to gather the ID of the newly created calendar, which could explain why you are increasing the time out.
You have already implemented the await option which is one of the best things you can do. You can also review the option to apply exponential back off to your application or similar to Google App Script a Utitlies.sleep().
There are multiple articles and references on how to utilize it for the retry process needed when the Resource itself has not fully propagated correctly.
You can also review the official Calendar API documentation that suggests that the error "Not Found" is a 404 error:
https://developers.google.com/calendar/api/guides/errors#404_not_found
With a suggested action of reviewing the option to set up exponential backoff to the application.
References:
GASRetry - Exponential backoff JavaScript implementation for Google Apps Script
Exponential backoff

Node.js tcp socket shut off trigger

I use node.js with the NET class to connect multiple Rasperry Pi's.
//Server
const net = require('node:net');
const server = net.createServer();
//Client
const tcpConnect = net.createConnection(SERVERINFO);
Thereby I can easily register multiple clients, open/close streams. Everything works without any problems.
But now I noticed that the callbacks.
tcpConnect.on('end', () => { console.log('end triggered') });
tcpConnect.on('close', () => { console.log('close triggered') });
tcpConnect.on('error', () => { console.log('error triggered') });
do not work if the server suddenly has a power failure (e.g. power supply is pulled). The clients don't trigger an error/end or close, so I can't close the connection properly.
As a result, an error is not triggered until the next attempt to write something to an existing stream. But this is not an option for me, especially for "online monitoring".
Does anyone know a way to identify the shut off of the server immediately?
You need to enable TCP keep-alives in order for the client to detect that the server is no longer responding and you can tune how often it checks on an idle connection. (I've written a language generic description at the beginning of this answer.)
In node it appears you can do this: socket.setKeepAlive(true), i.e. for this client to detect the server is gone when the socket is idle:
//Client
const tcpConnect = net.createConnection(SERVERINFO);
tcpConnect.setKeepAlive(true);

Web BLE secure connection with pairing type 'Just Works'

I have a BLE device that I wish to connect to through Web Bluetooth.
The BLE device's Characteristic is set up to only return values when requested over a secure connection, the bluetooth pairing type has to be 'Just Works'.
I am using Blazor WebAssembly and a nuget called Blazm.Bluetooth, but any pure javascript solution to this is also appreciated.
https://github.com/EngstromJimmy/Blazm.Bluetooth
I am able to get the requestDevice modal to show up in the browser, successfully pair to the device, and set up a notification handler that listens to changes in the characteristics value.
When I try to read a value from the device I get a response code telling me that I am not authorized to access data, since the device is setup to only allow access over a secure connection.
This leads me to the conclusion that a secure connection has not been established.
I am able to communicate with the device (in a different project) using the Windows.Devices.Bluetooth library, so I am certain that the device is working as expected once a secure connection has been established.
code for request:
export async function requestDevice(query)
{
var objquery = JSON.parse(query);
console.log(query);
var device = await navigator.bluetooth.requestDevice(objquery);
await device.gatt.connect();
device.addEventListener('gattserverdisconnected', onDisconnected);
PairedBluetoothDevices.push(device);
return { "Name": device.name, "Id": device.id };
}
function connect(bluetoothDevice) {
exponentialBackoff(3 /* max retries */, 1 /* seconds delay */,
function toTry() {
time('Connecting to Bluetooth Device... ');
return bluetoothDevice.gatt.connect();
},
function success() {
console.log('> Bluetooth Device connected. Try disconnect it now.');
},
function fail() {
time('Failed to reconnect.');
});
}
code for writing to the characteristic:
export async function writeValue(deviceId, serviceId, characteristicId, value)
{
var device = getDevice(deviceId);
console.log(device);
if (device.gatt.connected) {
var service = await device.gatt.getPrimaryService(serviceId);
var characteristic = await service.getCharacteristic(characteristicId);
var b = Uint8Array.from(value);
await characteristic.writeValueWithoutResponse(b);
}
else
{
await sleep(1000);
await writeValue(deviceId, serviceId, characteristicId, value);
}
}
I have used BTP BTVS with WireShark Bluetooth Sniffer to confirm that all request/response packages are identical byte for byte, to the working windows library based project, from the initial connection up to the point where the write response gives me the 'no access' error code.
My biggest question is if it is actually possible to establish a secure connection over the Web BLE API?
I am also interested in code suggestions I could try.
update:
I have tried using the 'add a device' functionality in windows Devices and Printer:
Adding the device in windows before connecting through Web BLE results in:
Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: GATT operation failed for unknown reason.
undefined
Microsoft.JSInterop.JSException: GATT operation failed for unknown reason.
undefined
at Microsoft.JSInterop.JSRuntime.<InvokeAsync>d__16`1[[Microsoft.JSInterop.Infrastructure.IJSVoidResult, Microsoft.JSInterop, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].MoveNext()
at Microsoft.JSInterop.JSObjectReferenceExtensions.InvokeVoidAsync(IJSObjectReference jsObjectReference, String identifier, Object[] args)
at Blazm.Bluetooth.BluetoothNavigator.SetupNotifyAsync(Device device, String serviceId, String characteristicId)
at BlazorWebAssemblySample.Pages.Counter.Connect() in C:\project.page.razor:line 82
at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)
And the same thing but without running the notify setup code results in not being able to pair through Web BLE at all.
Pairing through Web BLE first and then trying to 'add a device' in windows results in windows not being able to find the device.
A "funny" observation is that if I pair through Web BLE first, meaning that the characteristics value change event handler is set up and listening, and I then connect through my Windows.Devices.Bluetooth based project, then all the values read by this project will also be sent to/caught by the Web BLE listener and seen in the browser.
The issue seems a bit similar to what we have seen on https://bugs.chromium.org/p/chromium/issues/detail?id=1271239. As a trial, could you try to pair the device ahead of time using Windows system pairing? Then write to the characteristic and see if it works.

Browser connecting with multiple sockets using JS socket.io

I'm using node.js with a React front end. I'm building a GPS based MMO type of game. I recently decided to drop most HTTP requests and go with sockets, so I can emit data whenever I want and the front end only has to worry about what to do with it when it receives it.
I've built sites with sockets before, but never ran into this issue.
Basically, every time my browser opens a socket connection with node, it opens 2-3 connections at once(?). When it disconnects, I get the console.log stating that 3 socket connectionss have been closed. It'll look like this:
User disconnected
User disconnected
A user has connected to the system with id: nUMbkgX6gleq-JZQAAAD
A user has connected to the system with id: CzFtR2K5NJ1SoiHLAAAE
A user has connected to the system with id: tgGYhpXuOONmL0rMAAAF
For now, it wouldn't be an issue, however I'm only getting the FIRST 'inventory' emit to work. Later when I call the function to emit inventory again, the browser doesn't seem to get it. But the console logs in the node function will trigger correctly.
I have a feeling that this has something to do with multiple sockets opening.
Here is the React Event:
this.socket.on("inventory", charData => {
console.log('heres the data', charData)
props.setCharStats(charData[0])
})
Here's the node emitter:
const getCharInventory = (charId, userId) => {
dbInstance
.getCharInventory(charId)
.then(response => {
console.log( // this console.log happens just fine
"emmited inventory, userId is: ", userId, " with this response: ", response)
socket.emit("inventory", response)
})
.catch(err => console.error(err))
}
I'm such a dork. I was starting multiple connections within multiple connections.
In more than one component, I had this...
this.socket = socketIOClient(`${process.env.REACT_APP_proxy}`)
Sooo..... yeah. Just an FYI to others, it's better to connect in 1 file and import it into others.

Start conversation with Wit.ai chat bot in node.js

I have created a story on wit.ai using the quickstart guide.
Now I want to make a conversation with my chat bot using node-wit in node.js.
I guess I should use https://github.com/wit-ai/node-wit#runactions to run the messages, but I'm not sure how to start a conversation that never ends. I need to send a message and then get the response from the chat bot until I break the conversation.
I have looked through the wit.ai examples, but I cannot find any example of how to start a simple conversation in node.js.
I use socket.io to transmit the messages between client and server, and I have tried to solve my problem with
let sessions = {};
const sessionId = new Date().toISOString();
sessions[sessionId] = { context: {} };
io.on('connection', function (socket) {
socket.on('new message', function (message) {
client.runActions(
sessionId,
message,
sessions[sessionId].context
).then((context) => {
console.log(context);
sessions[sessionId].context = context;
}).catch((err) => {
console.error('Error: ', err.stack || err);
});
});
});
and it seems to almost work. I can chat with my bot, but it messes up the stories by sometimes answering multiple times from different stories. I guess I should probably end the stories somehow?
You should try with this link
https://github.com/wit-ai/node-wit/blob/master/examples/quickstart.js
Just clone/download the whole node-wit module from git or npm-install.
Then just run the command node quickstart.js "wit-token".
wit-token == wit-app-token
it will work .
Have you checked this Facebook Messenger integration example. The quickstart.js includes an interactive mode this is why it may be confusing.
Look at the messenger.js example on how to use runActions and send messages back to Messenger.
I was successful in doing this, although I'm still working on stories.

Categories