I'm working with cordova's BLE (bluetooth low energy)
After I subscribe to notifications of BLE (which returns Observable), I want to send some message to the ble device, what is the best way to perform this, basically I need to run a function once after the subscription is made so that once device responds back to me, the code in the subscription is run.
ble.startNotification(deviceId, uuid1, uuid2).subscribe(bufferData=> {
//do something with bufferData
})
now after this, I want to run something like a callback,
.then(()=> {
//send message to device (only once), after the message is sent, the device will respond back and the `do something with bufferData` code will be run
})
I could easily do a setTimeout and send a message to the device after few seconds, and of course it works, but I want to do it cleanly, after I'm sure the subscription happened (subscription to the Observable of course)
You can wrap existing method using create operator and add custom code that will be executed on every new subscription.
See the example:
// emulate cordova with "dummy" bluetooth interface
const BLE = {
startNotification: () => Rx.Observable.interval(1000)
}
const wrappedBLE = (...params) =>
Rx.Observable.create(observer => {
// constructor fn will be executed on every new subscribtion
const disposable = BLE.startNotification(...params).subscribe(observer);
// place code to send notification here, instead of console log
console.log('New subscriber for BLE with params: ', params);
return disposable;
});
wrappedBLE("param1", "param2", "param3")
.subscribe(e => console.log("received notification: ", e));
<script src="https://unpkg.com/rxjs#5.4.3/bundles/Rx.min.js"></script>
Related
I'm building a PWA using Web Bluetooth API. Connection is OK, I set an eventlistener for getting data and when my Arduino with BLE module HM10 send data, I get them.
But my PWA has more than one page. So on first one I have a "Connection" button, and after connection I "listen".
When I go to a second page, I use navigator.bluetooth.getDevices() to get previsouly connected device.
But I get an empty list.
I though my getDevices() function was bugged but I discoverd a strange thing:
my PWA run a browser tab on Chrome. I connect to the device. Then, on another tab I open this url with a Google example (https://googlechrome.github.io/samples/web-bluetooth/get-devices.html) and call Get Bluetooth Devices to get list of connected device, I see my device in this example page.
Without closing this second tab, I use my PWA: all page are able to get the device (using navigator.bluetooth.getDevices()), set the even and get data from the Arduino. Great!
If I close this tab, connection is lost...
So I think my "connection" is not perfect and when I close my PWA first page, I loose it... But as example avaible on internet are about "one page", I'm a bit lost (like the device connection in fact...).
Here is the code I use:
function reconnect_ble()
{
console.log('Search previously connected devices...');
navigator.bluetooth.getDevices()
.then(devices => {
console.log('> Got ' + devices.length + ' Bluetooth devices.');
if (devices.length == 0)
{
alert("No module");
return false;
}
var flag_module = false;
for (const device of devices)
{
var nom = device.name;
// Check if it's our modle
if (nom == pwa_hm10)
{
flag_module = connectToBluetoothDevice(device);
}
}
if (flag_module == false)
{
alert("Nothing for us");
return false;
}
else
{
return true;
}
})
.catch(error => {
console.log('Erreur:' + error);
});
}
//===================================================================================
// First Connection
function connect_ble()
{
return (deviceCache ? Promise.resolve(deviceCache) :
requestBluetoothDevice()).
then(device => connectToBluetoothDevice(device)).
catch(error => display_info(error));
}
//-------------------------------------------------------------------------------------
function connectToBluetoothDevice(device)
{
const abortController = new AbortController();
// Evenement sur ce device
device.addEventListener('advertisementreceived', (event) =>
{
console.log('connectToBluetoothDevice- Advertisements from "' + device.name + '"...');
// Stop watching advertisements to conserve battery life.
abortController.abort();
// Connexion au serveur GATT
device.gatt.connect()
.then(() =>
{
console.log('connectToBluetoothDevice - Server GATT from "' + device.name + '"...');
// Set our event
connectDeviceAndCacheCharacteristic(device).
then(characteristic => startNotifications(characteristic)).
catch(error => display_info(error));
})
.catch(error =>
{
// Erreur pas de connexion
console.log(error);
});
}, { once: true });
console.log('connectToBluetoothDevice - Watching advertisements from "' + device.name + '"...');
device.watchAdvertisements({ signal: abortController.signal })
.catch(error =>
{
console.log(error);
});
}
//----------------------------------------------------------------------------------
// Form to choose device
function requestBluetoothDevice()
{
return navigator.bluetooth.requestDevice(
{
acceptAllDevices: true,
optionalServices: [0xFFE0]
}).
then(device => {
display_info('Selected: ' + device.name);
deviceCache = device;
deviceCache.addEventListener('gattserverdisconnected',handleDisconnection);
return deviceCache;
});
}
//-----------------------------------------------------------------------------------
function connectDeviceAndCacheCharacteristic(device)
{
if (device.gatt.connected && characteristicCache)
{
return Promise.resolve(characteristicCache);
}
return device.gatt.connect().
then(server => {
return server.getPrimaryService(0xFFE0);
}).
then(service => {
return service.getCharacteristic(0xFFE1);
}).
then(characteristic => {
display_info('characteristic founded');
characteristicCache = characteristic;
return characteristicCache;
});
}
//------------------------------------------------------------------------------------
function startNotifications(characteristic)
{
return characteristic.startNotifications().
then(() => {
characteristic.addEventListener('characteristicvaluechanged',handleCharacteristicValueChanged);
});
}
//--------------------------------------------------------------------------------------
function handleCharacteristicValueChanged(event)
{
// Decoding received data
var decodage = new TextDecoder().decode(event.target.value);
traitement_data_ble(decodage);
}
Edit
If you connect to the bluetooth on Page 1, and go to Page 2 on same tab, connection is lost.
If you connect to the bluetooth on Page 1, set an event listenner to get data then open Page 2 on another tab, Page 2 will "see" the bluetooth module, but even if Page 2 set an event listener, this is even from Page 1 that will receive the data.
If you connect to the bluetooth on Page 1, don't set event listenner, open Page 2 in other tab and set event listenner on Page 2, Page 2 will receive the data.
So case 3 seems to be the only way. Maybe using iFrame for the connection page??
Accessing the same device from multiple pages is not supported and the fact that it seems to partially work is more of a bug than a feature. A device can't effectively distinguish between multiple pages on the same host trying to communicate with it so you need to have a single piece of code which manages the connection to the device.
The correct way to do this today is to create a single-page application so that the connection held by that page can remain own even if the user navigates to different "pages" of your application. If you need to create separate windows these can communicate with the device by sending commands back through the single page that owns the connection using postMessage or Broadcast Channel.
The ideal solution, which is not currently supported, is to use a Shared Worker for this. Shared Workers are a type of web worker which is owned by all the currently open pages of your app at once. It can thus hold shared state (such as the Bluetooth connection) in behalf of your application and won't exit unless every window of your app is closed.
whit this code i can get gps data and retrive address all works fine in background, for some minuts, after 20 minuts about,the app semms stop do what i aspect.
i tryed battery concession and other possible things, ive tryed backgroud service.
But nothing seems to wor for get position in background, after lomg time.
Any suggestion.
this.backgroundGeolocation.configure(config).then(() => {
console.log('backgroundGeolocation configconfigconfigconfigconfigconfig');
this.backgroundGeolocation.on(BackgroundGeolocationEvents.location).subscribe((location: BackgroundGeolocationResponse) => {
console.log(location);
this.ngZone.run(() => {
this.geolocationService.nativeGeocoder.reverseGeocode(location.latitude,
location.longitude).then((result: NativeGeocoderResult[]) =>{
console.log("JSON.stringify(result[0]",JSON.stringify(result[0]));
this.comunenoreplace = result[0]['locality'];
this.comune = result[0]['locality'].replace(/[^A-Z0-9]+/ig, "-");
this.provincia = result[0]['subAdministrativeArea'].replace(new RegExp('Città Metropolitana di|Provincia di', 'g'), '');
this.globalLatitude = location.latitude;
this.globalLongitude = location.longitude;
}).catch((error: any) => console.log("reverseGeocode", error));
});
// IMPORTANT: You must execute the finish method here to inform the native plugin that you're finished,
// and the background-task may be completed. You must do this regardless if your operations are successful or not.
// IF YOU DON'T, ios will CRASH YOUR APP for spending too much time in the background.
// this.backgroundGeolocation.finish;
});
});
you can use the https://ionicframework.com/docs/native/background-mode
it just modify the sleep mode for apps to make them work in background (without killing the process).
If the process is killed, so the app may stop sending datas.
Then you have "cordova.plugins.backgroundMode.excludeFromTaskList()"; to make it disappear from the tasklist (Android 5.0+)
Hope this help :)
I need to ensure that a certain HTTP request was send successfully. Therefore, I'm wondering if a simple way exists to move such a request into a background service task.
The background of my question is the following:
We're developing a survey application using flutter. Unfortunately, the app is intended to be used in an environment where no mobile internet connection can be guaranteed. Therefore, I’m not able to simply post the result of the survey one time but I have to retry it if it fails due to network problems. My current code looks like the following. The problem with my current solution is that it only works while the app is active all the time. If the user minimizes or closes the app, the data I want to upload is lost.
Therefore, I’m looking for a solution to wrap the upload process in a background service task so that it will be processed even when the user closes the app. I found several posts and plugins (namely https://medium.com/flutter-io/executing-dart-in-the-background-with-flutter-plugins-and-geofencing-2b3e40a1a124 and https://pub.dartlang.org/packages/background_fetch) but they don’t help in my particular use case. The first describes a way how the app could be notified when a certain event (namely the geofence occurred) and the second only works every 15 minutes and focuses a different scenario as well.
Does somebody knows a simple way how I can ensure that a request was processed even when there is a bad internet connection (or even none at the moment) while allowing the users to minimize or even close the app?
Future _processUploadQueue() async {
int retryCounter = 0;
Future.doWhile(() {
if(retryCounter == 10){
print('Abborted after 10 tries');
return false;
}
if (_request.uploaded) {
print('Upload ready');
return false;
}
if(! _request.uploaded) {
_networkService.sendRequest(request: _request.entry)
.then((id){
print(id);
setState(() {
_request.uploaded = true;
});
}).catchError((e) {
retryCounter++;
print(e);
});
}
// e ^ retryCounter, min 0 Sec, max 10 minutes
int waitTime = min(max(0, exp(retryCounter)).round(), 600);
print('Waiting $waitTime seconds till next try');
return new Future.delayed(new Duration(seconds: waitTime), () {
print('waited $waitTime seconds');
return true;
});
})
.then(print)
.catchError(print);
}
You can use the plugin shared_preferences to save each HTTP response to the device until the upload completes successfully. Like this:
requests: [
{
id: 8eh1gc,
request: "..."
},
...
],
Then whenever the app is launched, check if any requests are in the list, retry them, and delete them if they complete. You could also use the background_fetch to do this every 15 minutes.
I'm setting up in app purchases in my Ionic app and I've been having some trouble getting the test purchases working correctly. It would seem that as soon as I execute this particular function, it automatically runs the code as if it was approved, even though the confirmation to get the subscription hasn't occurred yet:
this.platform.ready().then(() => {
// Register the products for consumption
this.products.forEach(product => {
this.store.register({
id: product.id,
alias: product.alias,
type: product.type
});
// When a purchase is approved, see what we get here
this.store.when(product.id).approved((order) => {
// Purchase was successful, setup the appropriate subscription
this._subscriptions.updateSubscription(this.user.id, this.selectedPlan.amount, 'activate').then(() => {
if(this.selectedPlan.amount === 1) {
this.subscriptionGrammar = 'month';
} else if(this.selectedPlan.amount > 1) {
this.subscriptionGrammar = 'months';
}
order.finish();
});
});
});
});
I was under the impression that utilizing the .when().approved() would only fire once the payment "goes through". Since I'm using test transactions, I'm not sure how that would affect it, but I would suspect it should only do that once I hit "Confirm" on the Google dialog that pops up in my app?
Is there something I'm missing here?
I am doing the following in an ionic application:
When a user is logged in, subscribe to their user ID topic
When a push notification arrives, console.log something.
this.authState.subscribe((state) => {
if (state) {
this.fcmPush.subscribeToTopic(state.uid);
this.fcmPush.onNotification().subscribe(notification => {
if (notification.wasTapped) {
console.log('Received in background', notification);
} else {
console.log('Received in foreground', notification);
}
});
}
});
Source: https://github.com/AmitMY/ionic-starter-firebase/blob/master/src/app/app.component.ts#L118
Sending a notification to my own topic arrives (after a few minutes), but I never see anything in console, both from outside the app, and inside the app.
What am I doing wrong?
Sending a notification from firebase messaging does not work.
However, using the sdk I must add:
click_action: "FCM_PLUGIN_ACTIVITY",
Which is in the usage guide but I missed