chrome.hid.send fails on second use - javascript

Something about my use of chrome.hid.send seems to be leaving the bus in a bad state. I consistently can NOT get my second usage of the API call to work. Sometimes, it will also fail on the first usage. WITH THE EXACT SAME CODE, I can come back and try a while later (maybe 10min) and the first send will work.
The device I'm working with does not return a response to all messages sent to it. The test message for example, is just a dummy message that is ignored by the device. I've tested this both on a mac and a PC. My call stack depth is 2 at this point in my application (literally first one is kicked off by a button click and then a setTimeout calls the same method 5s later).
I've testing sending buffers of length 64Bytes as well as 58Bytes. The properties from the HidDeviceInfo object read "maxInputReportSize":64,"maxOutputReportSize":64
Params on first usage:
Params on second usage:
I really can't identify how I'm using the API incorrectly. When messages do succeed, I can see them on the device side.
// Transmits the given data
//
// #param[in] outData, The data to send as an ArrayBuffer
// #param[in] onTxCompleted, The method called on completion of the outgoing transfer. The return
// code is passed as a string.
// #param[in] onRxCompleted, The method called on completion of the incoming transfer. The return
// code is passed as a string along with the response as an ArrayBuffer.
send: function(outData, onTxCompleted, onRxCompleted) {
if (-1 === connection_) {
console.log("Attempted to send data with no device connected.");
return;
}
if (0 == outData.byteLength) {
console.log("Attempted to send nothing.");
return;
}
if (COMMS.receiving) {
console.log("Waiting for a response to a previous message. Aborting.");
return;
}
if (COMMS.transmitting) {
console.log("Waiting for a previous message to finish sending. Aborting.");
return;
}
COMMS.transmitting = true;
var dummyUint8Array = new Uint8Array(outData);
chrome.hid.send(connection_, REPORT_ID, outData, function() {
COMMS.transmitting = false;
if (onTxCompleted) {
onTxCompleted(chrome.runtime.lastError ? chrome.runtime.lastError.message : '');
}
if (chrome.runtime.lastError) {
console.log('Error in COMMS.send: ' + chrome.runtime.lastError.message);
return;
}
// Register a response handler if one is expected
if (onRxCompleted) {
COMMS.receiving = true;
chrome.hid.receive(connection_, function(reportId, inData) {
COMMS.receiving = false;
onRxCompleted(chrome.runtime.lastError ? chrome.runtime.lastError.message : '', inData);
});
}
});
}
// Example usage
var testMessage = new Uint8Array(58);
var testTransmission = function() {
message[0] = 123;
COMMS.send(message.buffer, null, null);
setTimeout(testTransmission, 5000);
};
testTranmission();

The issue is that Windows requires buffers to be the full report size expected by the device. I have filed a bug against Chromium to track adding a workaround or at least a better error message to pinpoint the problem.
In general you can get more detailed error messages from the chrome.hid API by enabling verbose logging with the --enable-logging --v=1 command line options. Full documentation of Chrome logging is here.

Related

Can we reset streamwriter data for inbound integration?

Just wondering if we are able to re-write data that we was set via the setWriteString() method while responding to an inbound api call. For example, let's say the scripted rest resource code is as follows:
(function process(/*RESTAPIRequest*/ request, /*RESTAPIResponse*/ response) {
var body = request.body.data;
/* do something with request data
..
..
..
then start preparing the response
*/
var writer = response.getStreamWriter();
try
{
response.setContentType('application/json');
response.setStatus(200);
writer.writeString("{\"results\":[");
var inc = new GlideRecord('incident');
inc.query();
while(inc.next()){
var obj = {};
obj.id = inc.getValue('number');
obj.sys_id = inc.getUniqueValue();
writer.writeString(global.JSON.stringify(obj));
if (inc.hasNext()) {
writer.writeString(",");
}
}
writer.writeString("]}");
}
catch (ex)
{
// let's say exception was thrown on the 3rd iteration while gliding the incident table
// oh no...an exception..so we need to write something else to the stream
// is it possible to erase/remove everything that was added to the stream up until the exception occured?
// so that the response will contain details only about the error?
// something like below:
response.setContentType('application/json');
response.setStatus(500);
writer.writeString("{\"error\":\"Something went wrong\"}"); // this isn't working btw...the stream contained both the content generated in "try" block as well as the "catch" block
// this should not contain anything related to whatever was written from the earlier iterations....
}
})(request, response);
For the errors you can use the Scripted REST API Error objects.
Which should reset the output stream.
https://developer.servicenow.com/dev.do#!/learn/courses/paris/app_store_learnv2_rest_paris_rest_integrations/app_store_learnv2_rest_paris_scripted_rest_apis/app_store_learnv2_rest_paris_scripted_rest_api_error_objects
(function run(request, response) {
try {
...
} catch (e) {
var myError = new sn_ws_err.ServiceError();
myError.setStatus(500);
myError.setMessage('Something went wrong');
myError.setDetail('Error while retrieving your data: ' + e);
return myError;
}
})(request,response);
It might also be useful to get the error message from the GlideRecord
gr.getLastErrorMessage();
// something like aborted by businessrule or acl, etc...
For the details of your error message.

Web BLE Characteristic startNotifications sometimes doesn't bind

I'm using web BLE. I have based my code according to the example of the heart rate measurement.
Everything is working fine most of the time. But sometimes, even if the connection is successfully made, when I try to bind to the notification, it doesn't work.
The link is made in this function :
_startNotifications(characteristicUuid) {
let characteristic = this._characteristics.get(characteristicUuid);
console.log(characteristic);
return characteristic.startNotifications().then(() => characteristic);
}
When everything is OK, I can see in the console that BluetoothRemoteGATTCharacteristic has a value : DataView(2) {}
Otherwise, when it's not working it has a value : null
I would like to be able to retry automatically, if I detect that the value is null. But I'm not familiar with Promise (I think this is it) and console.log(characteristic.value) doesn't work here.
How would you approach this ?
What I ended up doing is "bypass" the issue. So it's a more algorithmic resolution than a pure Javascript one.
I didn't change the connection function, so it is still called like this :
device._startNotifications(some_uuid).then(handleHeartRateMeasurement)
I check everything in the handleHeartRateMeasurement function :
var ready = false;
function handleHeartRateMeasurement(heartRateMeasurement) {
console.log("Hold on...");
heartRateMeasurement.addEventListener("characteristicvaluechanged", event => {
// Everytime the value change, this should be triggered
// If it did not, variable "ready" will stay false
ready = true;
var value = device.parseValue(event.target.value);
// Do something with value here
});
var check = function(){
// If we have received data from characteristic, we are ready to go !
if(ready === false){
console.log("Device connected but not receiving data");
// Stop the current notification subscription
device.stopNotificationsHeartRateMeasurement();
// Start a new one
device._startNotifications(some_uuid).then(handleHeartRateMeasurement);
setTimeout(check, 1000); // check again in a 1s
}
else{
console.log("Device connected and receiving data");
}
}
setTimeout(() => {
check();
}, 500);
}

Disable Display of Generic Browser Push Notification

I have implemented the browser push notification functionality and its working fine. I used this guide as the reference https://developers.google.com/web/fundamentals/getting-started/push-notifications/step-01?hl=en
However as payload is still not supported, I decided to query my server to get the notification data for each user which is also working fine.
There is one issue though. For some cases, after getting data from the server, I want to control whether to show the notification or not. I am not able to figure out how to do this. I tried returning false, throwing errors etc. But is always shows the default notification even if I don't call showNotification method. Let me know how to solve this. Following is the relevant code
self.addEventListener('push', function(event) {
event.waitUntil(
fetch('/getPushNotificationData/').then(function(response){
if (response.status !== 200) {
// I don't want to show any notification in this case
console.log('Looks like there was a problem. Status Code: ' + response.status);
throw new Error();
}
return response.json().then(function(data){
var shouldDisplay = data.shouldDisplay;
if (shouldDisplay=='1'){
var title = data.title;
var message = data.message;
var url = data.url;
return self.registration.showNotification(title, {
body: message,
data: url
});
}
else{
// I don't want to show any notification in this case also
return true;
}
});
})
);
});

Can't figure out Parse Config with Cloud Code

Been searching for solutions for hours and getting close to no luck. I just don't see much documentation on the matter of Parse Config. At least with solutions because I tried everything I could find.
So basically I'm trying to set a default picture when someone saves an object with a status as "denied."
It started with me looking at this: Set default profile picture for parse signup
And here's what I got.
//Accepts/denies picture request
Parse.Cloud.afterSave("Requests", function(request) {
var toUserId = request.object.get("to").id;
var fromUserId = request.object.get("from").id;
var toUser = null;
var fromUser = null;
var status = request.object.get("status");
if (status === "accepted") {
.....
} else if (status === "denied") {
Parse.Config.get().then(function(config) {
request.object.set('photo', config.get("denied"));
}).then(function() {
console.log('Success: Denied photo set.');
}, function(error) {
console.log('error: denied photo not set');
});
} else if (status === "waiting") {
....
}
});
I get a success everytime, but I get nothing as the photo file. I'm stuck and not sure what else to do here. The status changes to denied correctly, but I don't get anything to show up as a file in the photo spot, stays as undefined..
I2015-08-24T01:54:09.837Z]v46 after_save triggered for Requests for user oE3FhNfyWW:
Input: {"object":{"createdAt":"2015-08-24T01:54:03.398Z","from":{"__type":"Pointer","className":"_User","objectId":"odv4R9OWso"},"objectId":"InB8Iods8U","status":"denied","to":{"__type":"Pointer","className":"_User","objectId":"oE3FhNfyWW"},"updatedAt":"2015-08-24T01:54:09.834Z"}}
Result: Success
I2015-08-24T01:54:09.973Z]Success: Denied photo set.
I notice the code doesn't say request.object.save(), which might explain why the object isn't changed when you check later on.
But saving seems strange, since this function runs after saving. That's either wasteful or infinitely-loopy. Since the goal is to modify request.object (the object just saved), then do this on beforeSave().
Remember to call response.success() or .error() at the end of beforeSave().

How to handle SoundCloud's JavaScript API 404 error while streaming a track

I read about this SoundCloud's API bug that returns a 404 error while trying to stream a track even if the "streamable" property was set to true.
I found some other questions about the topic (see "Tracks for “The Hives” are not streaming via the api" or "Soundcloud API SC.stream (track not loading)"), but what I would like to know is how to detect the error, and how to workaround it. I tried with some try {} catch() {} but it seems that I can't detect the problem.
Here is some background information:
I have a php returning a JSON array with a list of tracks to play. My script reads the array one-by-one, and then tries to play the current one. Everything works fine, but when a 404 error is returned the script ends, without jumping to the next track. It seems that I am unable to detect that kind of problem.
Here is the JavaScript method that manages the stream:
playTrack = function() {
// console.log('playTrack');
SC.get(
"/tracks/" + playlist[ now_playing ].sndc_id,
function(track, error) {
if (error) {
sendErrorReport(playlist[ now_playing ].id);
loadNext();
} else {
try {
SC.stream(
"/tracks/" + playlist[ now_playing ].sndc_id,
function(sound, error) {
sound_object = sound;
if (error || !sound_object) {
sendErrorReport(playlist[ now_playing ].id);
loadNext();
} else {
sound_object.play(
{
'from': parseInt(current_position),
'onfinish': function() {
current_position = 0;
updateCounter();
$('#radio-waveform-position').css( { 'opacity': '0', 'width' : '0%' } );
loadNext();
},
'onplay': function() {
$('#radio-like').removeClass('liked');
playing = true;
updateInfo();
},
'whileplaying': function() {
current_position = this.position;
$('#radio-waveform-position').css( { 'opacity': '1', 'width' : (100 * current_position / this.duration).toFixed(3) + '%' } );
},
'ondataerror': function() {
sendErrorReport(playlist[ now_playing ].id);
loadNext();
}
}
);
if ($('html').hasClass('touch') || !autoplay) $('#radio-play-pause').click();
}
}
);
} catch (err) {
sendErrorReport(playlist[ now_playing ].id);
loadNext();
}
}
}
);
}
It seems that both the SC.get and SC.stream "error" return parameters are always empty for this kind of "faulty" tracks. As you can see, I tried to wrap everything in a try() {} catch() {} but without success. Also, the sound_object.play() ondataerror method is completely ignored.
The script can be seen in action here: http://foggetabout.it/
I read that someone found a workaround for that, but there was no explanation. Has anyone any idea on how to solve it?
Run into something similar. Not sure if it is connected but it appears some tracks are only streamable with flash so the GET method states they’re streamable but the stream can fail if flash is not available.
One way to get around it is to check when SoundManager has loaded the track using the onload & readyState, which (from docs: http://www.schillmania.com/projects/soundmanager2/doc/) has 4 states:
0 = uninitialised
1 = loading
2 = failed/error
3 = loaded/success
So:
sound_object.play({
// Exisiting…
onload: function() {
if (this.readyState == 2) {
// Handle error
}
});
There are some caveats on when the state changes so if this becomes a problem you could also try using durationEstimate as it returns null for the failed tracks I’m coming across.
For a lot of songs streamable is set to true and its stream_url is defined, but the resource itself is not there.
Using curl, hitting the i1 endpoint with your desired track_id and a registered API_KEY is the first step to checking for the actual resources on a SoundCloud hosted track. (Warning: the i1 endpoint is not officially documented in SoundCloud's HTTP API Reference)
curl https://api.soundcloud.com/i1/tracks/{{track_id}}/streams?client_id={{API_KEY}}
This will return a JSON object that looks something like:
{
"http_mp3_128_url": "{some_url}",
"preview_mp3_128_url": "{some_url}"
}
If the http_mp3_128_url key-value pair exists, then that track has an accessible HTTP resource and would be streamable.
More info here - https://github.com/francismakes/i1-soundcloud
Building off of roj's answer, I used a bit of code like this to first get into handling 404s on the SC track streaming:
sound.play({
onload: function () {
if (this.readyState === 2){
alert('error');
}
},

Categories