Writing Characteristics on Bluetooth Device - flutter

For those who have already worked on Bluetooth Low Energy on Flutter:
I am developing a mobile application. Simply, every time the user clicks on the "Read Data" button, I want to discoverServices and receive data:
onTap: () async {
deviceStateSubscription = bluetoothDevice.state.listen((s) {
if (s == BluetoothDeviceState.connected) {
_discoverServices();
} else {
bluetoothDevice.connect();
_discoverServices();
}
});
},
I have realized that connection is sometimes dropping for no reason. That is why I use an 'if/else' to check the connection.
Here is my discoverServices method:
_discoverServices() async {
List<BluetoothService> services = await bluetoothDevice.discoverServices();
if (bluetoothServices != null && bluetoothServices.isNotEmpty) {
bluetoothServices.forEach((service) {
if (service.uuid.toString().toLowerCase() == Configuration.nusUUID) {
service.characteristics.forEach((c) async {
if (c.uuid.toString() == Configuration.txUUID) {
txChar = c;
try {
await txChar!.write(Configuration.config, withoutResponse: true); // Sending the configuration
await txChar!.write(Configuration.rfCmdStart, withoutResponse: true); // Sending the start configuration
await txChar!.write(Configuration.rfCmdStop, withoutResponse: true); // Sending the stop configuration to say that I am done, so send me data now
} catch (e) {
print(e.toString());
}
} else if (c.uuid.toString() == Configuration.rxUUID) {
rxChar = c;
rxChar!.setNotifyValue(true);
rxChar!.value.listen((value) async {
if (value.isEmpty) {
print('No data received');
} else {
print(value);
}
});
}
});
}
});
}
}
The problem is, the button sometimes works and sometimes doesn't... It is not stable at all. Sometimes when I click, I receive data; and sometimes I receive Unhandled Exception: PlatformException(set_notification_error, error when writing the descriptor, null, null). I couldn't find any logical answers for that. To fix it, I am simply clicking on the button a few more times so that it sends me the data.
Any thoughts about how to fix this descriptor error?

writing the txChar characteristic with withoutResponse: true three times in a row is a problem. As the documentation for the write method states:
Writes the value of a characteristic.
CharacteristicWriteType.withoutResponse: the write is not guaranteed
and will return immediately with success.

Related

Need to parallelise async operation using stream in dart

Problem: I am building an application on an RSS feed reader.
In a feed:
There are various items and each item is an article which has a URL.
Each URL has open graph metadata which needs to be fetched and takes time.
As soon as URL open graphs metadata is loaded it needs to be shown in the list on the UI.
Now I want to run 2 and 3 in parallel, am doing this in the code for now:
Stream<News> _getNewsRssFeed(Categories selectedCategory) async* {
try {
final rssUrl = _getRssUrl(selectedCategory);
RssFeed feed = await _getRssFeed(rssUrl);
if (feed.items != null) {
for (int itemIndex = 0; itemIndex < feed.items!.length; itemIndex++) {
final item = feed.items![itemIndex];
try {
Future<News> news = _processRssFeedItem(item, feed);
news.then((value){
yield value; // This is not working
});
} catch (error) {
print("Error while parsing ${item.link} error = $error");
}
}
}
} catch (error) {
print("Error while parsing RssFeed url $error");
}
}
The problematic line I have commented in above code, can you please let me know what is the best way here?
Basically, news.then returns Future and you are not awaiting for the result to be available.
You can change it by changing code to following
try {
final value = await _processRssFeedItem(item, feed);
yield value;
} catch (error) {
print("Error while parsing ${item.link} error = $error");
}

PWA problem with Vue3+service worker+keep-alive

I have a problem with Vue3+service worker+keep-alive.
I use keep-live in template
<q-page-container>
<router-view v-slot="{ Component }">
<keep-alive :include="['WorkPage']">
<component :is="Component" :key="$route.fullPath"/>
</keep-alive>
</router-view>
</q-page-container>
create queue
createWorkQueue = new Queue('createWorkQueue', {
onSync: async ( {queue} ) => {
let entry
while (entry = await queue.shiftRequest()) {
try {
await fetch(entry.request);
const channel = new BroadcastChannel('sw-messages-work');
channel.postMessage({msg: 'offline-work-uploaded'});
} catch (error) {
await queue.unshiftRequest(entry);
throw error;
}
}
}
})
addEventListener('fetch'
self.addEventListener('fetch', (event) => {
if (event.request.url.endsWith('/api/ins_new_work')) {
const bgSyncLogic = async () => {
try {
const response = await fetch(event.request.clone())
return response
} catch (error) {
await createWorkQueue.pushRequest({request: event.request})
return error
}
}
event.respondWith(bgSyncLogic())
}
})
when in offline I send form - createWorkQueue.pushRequest hangs to 5 minutes
if I delete from keep-alive - WorkPage - then pushRequest works well
but I need keep-alive page. How can I solve this?
I found!!
I use IndexedDB library and for show offline message I read from DB information
const db = await openDB('workbox-background-sync')
but in first time - table 'requests' don't create
I insert next code
const db = await openDB('workbox-background-sync', undefined, { upgrade(db) { db.createObjectStore('requests') }})
and works well

How to show firebase storage file upload progress

I am trying to show the progress of the file I am uploading to firebase storage in a progress indicator how can I achieve this.?
Note that I am a uploading two files at the same time.
here is my code.
Future<String?> _uploadFileToStorage(BuildContext context,File file, path) async {
try {
var task = _firebaseStorage.ref().child(path);
var status = await task.putFile(file);
print(status.state);
return task.getDownloadURL();
} catch (err) {
print(err.toString());
}
}
first take variable bool load and make initialy load=false when your function start make load=trueand at then end of your function load=false, so on your parent widget load==true?CircularProgressIndicator():Scaffold(),
so here you function
Future<String?> _uploadFileToStorage(BuildContext context,File file, path) async {
try {
setState({
load=true;
});
var task = _firebaseStorage.ref().child(path);
var status = await task.putFile(file);
print(status.state);
return task.getDownloadURL();
setState({
load=false;
})
} catch (err) {
print(err.toString());
setState({
load=false;
})
}
}
You can track the uploading progress by simply using the following code:
status.snapshotEvents.listen((TaskSnapshot snapshot) {
print('Task state: ${snapshot.state}');
print('Progress: ${(snapshot.bytesTransferred / snapshot.totalBytes) * 100} %');
switch (snapshot.state) {
case TaskState.paused:
// TODO: Handle this case.
break;
case TaskState.running:
return CircularProgressIndicator(
value: (snapshot.bytesTransferred > 0 && snapshot.totalBytes > 0) ? snapshot.bytesTransferred / snapshot.totalBytes : null);
break;
case TaskState.success:
// TODO: Handle this case.
break;
case TaskState.canceled:
// TODO: Handle this case.
break;
case TaskState.error:
// TODO: Handle this case.
break;
}
}, onError: (e) {
// The final snapshot is also available on the status via `.snapshot`,
// this can include 2 additional states, `TaskState.error` & `TaskState.canceled`
print(status.snapshot);
});

How to handle application conditional messages/alerts

I have to handle a message coming from application on form filling. if it comes have to close the alert window using (X)button.
problem is I'm not able to close it. directly close (.click()) works fine.
but on condition when i say if alert is present then only close it scenario is not working. Any help is appreciated
try {
var Alert1 = element(by.xpath("//h2[text()='Job Role']/parent::div/following-sibling::div//span"));
Alert1.isPresent().then(async function(res) {
if (res) {
Alert1.click();
}
})
} catch (err) {
console.log(err)
}
})
it('async test case', async () => {
var Alert1 = element(by.xpath("//h2[text()='Job Role']/parent::div/following-sibling::div//span"));
if (await Alert1.isPresent()) {
await Alert1.click();
}
})
simple, readable and robust if you avoid .then

Can a Future internally retry an http request if it fails in Flutter?

I'm using the following code to successfully poll mysite for JSON data and return that data. If the request fails, then it successfully returns an error message as well return Result.error(title:"No connection",msg:"Status code not 200", errorcode:0);.
What I'd like to happen is have the app retry the request 3 times before it returns the error message.
Basically have the future call itself for some number of iterations.
I did try to create an external function that would get called from the outer catch which then would in turn call the getJSONfromTheSite function a second and then a third time, but the problem was that you would have a non-null return from the Future so the app wouldn't accept that approach
Is there another way of doing this?
Future<Result> getJSONfromTheSite(String call) async {
debugPrint('Network Attempt by getJSONfromTheSite');
try {
final response = await http.get(Uri.parse('http://www.thesite.com/'));
if (response.statusCode == 200) {
return Result<AppData>.success(AppData.fromRawJson(response.body));
} else {
//return Result.error("Error","Status code not 200", 1);
return Result.error(title:"Error",msg:"Status code not 200", errorcode:1);
}
} catch (error) {
return Result.error(title:"No connection",msg:"Status code not 200", errorcode:0);
}
}
The following extension method will take a factory for futures, create them and try them until the retry limit is reached:
extension Retry<T> on Future<T> Function() {
Future<T> withRetries(int count) async {
while(true) {
try {
final future = this();
return await future;
}
catch (e) {
if(count > 0) {
count--;
}
else {
rethrow;
}
}
}
}
}
Assuming you have a rather plain dart method:
Future<AppData> getJSONfromTheSite(String call) async {
final response = await http.get(Uri.parse('http://www.thesite.com/'));
if (response.statusCode == 200) {
return AppData.fromRawJson(response.body);
} else {
throw Exception('Error');
}
}
You can now call it like this:
try {
final result = (() => getJSONfromTheSite('call data')).withRetries(3);
// succeeded at some point, "result" being the successful result
}
catch (e) {
// failed 3 times, the last error is in "e"
}
If you don't have a plain method that either succeeds or throws an exception, you will have to adjust the retry method to know when something is an error. Maybe use one of the more functional packages that have an Either type so you can figure out whether a return value is an error.
Inside catch() you can count how many times have you retried, if counter is less than three you return the same getJSONfromTheSite() function, but with the summed counter. And if the connection keeps failing on try{} and the counter is greater than three it will then returns the error.
Future<Result> getJSONfromTheSite(String call, {counter = 0}) async {
debugPrint('Network Attempt by getJSONfromTheSite');
try {
String? body = await tryGet();
if (body != null) {
return Result<AppData>.success(AppData.fromRawJson(response.body));
} else {
//return Result.error("Error","Status code not 200", 1);
return Result.error(title:"Error",msg:"Status code not 200", errorcode:1);
}
} catch (error) {
if(counter < 3) {
counter += 1;
return getJSONfromTheSite(call, counter: counter);
} else {
return Result.error(title:"No connection",msg:"Status code not 200", errorcode:0);
}
}
}