I have been fiddling with the new web bluetooth functionality. I have one of these estimote beacons: http://developer.estimote.com/
I know the uuid for my beacon. Here is the code I am using(it is an angular app, hence $scope, $window):
$scope.runBT = runBT;
function runBT() {
let mobile = getMobileOperatingSystem();
if (mobile === 'Android' || mobile === 'iOS') {
$window.navigator.bluetooth.requestDevice({
acceptAllDevices: true,
optionalServices: ['b9407f30-f5f8-466e-aff9-25556b57fe6d']
})
.then(device => {
console.log('FOUND DEVICE: ', device);
device.watchAdvertisements();
device.addEventListener('advertisementreceived', interpretIBeacon);
})
.catch(error => { console.log(error); });
}
}
function interpretIBeacon(event) {
var rssi = event.rssi;
var appleData = event.manufacturerData.get(0x004C);
if (appleData.byteLength != 23 ||
appleData.getUint16(0, false) !== 0x0215) {
console.log({isBeacon: false});
}
var uuidArray = new Uint8Array(appleData.buffer, 2, 16);
var major = appleData.getUint16(18, false);
var minor = appleData.getUint16(20, false);
var txPowerAt1m = -appleData.getInt8(22);
console.log({
isBeacon: true,
uuidArray,
major,
minor,
pathLossVs1m: txPowerAt1m - rssi});
}
Sadly the watchAdvertisements method is not implemented yet. You may want to check the Implementation Status page at https://github.com/WebBluetoothCG/web-bluetooth/blob/master/implementation-status.md to know when this method will be supported in Chrome and other browsers.
It's unclear what the problem is, but here are a few tips:
Understand that the web bluetooth APIs are a proposed set of standards under active development, and support is limited to certain builds of Google Chrome, and as a shim for the Noble.js bluetooth central module. If you are using Angular, you need to use the latter shim to make it work, which perhaps you already are. You can read more here: https://developers.google.com/web/updates/2015/07/interact-with-ble-devices-on-the-web
If you are getting as far as the interpretIBeacon function, then it's just a matter of parsing the bytes out, which you seem well on your way to doing. You can see more about the byte layout of the beacon in my answer here: https://stackoverflow.com/a/19040616/1461050
You don't want to filter for the beacon UUID as a service, so you need to remove optionalServices: ['b9407f30-f5f8-466e-aff9-25556b57fe6d']. A beacon ProximityUUID is not the same as a GATT ServiceUUID even though they superficially have the same format. A beacon bluetooth advertisement of the type you are looking for a manufacturer advertisement and not a *GATT service** advertisement. The two advertisement types are different, but the APIs shown above should return results from both types.
Related
Using Linking.openURL('mailto:') with expo-linking opens the mail app with a 'compose mail' screen, how can i open the mail app directly on the inbox tab ?
Inspired by this stack overflow post, you can use the message:// url scheme to achieve this on iOS. If no content is provided, it defaults to the email inbox on iOS.
For android, things are a little trickier. You'll need expo-intent-launcher and a few extra params to complete the hand-off. A complete solution may look something like this:
import { startActivityAsync, ActivityAction } from 'expo-intent-launcher';
[...]
if (Platform.OS === "android") {
const activityAction = "android.intent.action.MAIN";
const intentParams: IntentLauncher.IntentLauncherParams = {
category: "android.intent.category.APP_EMAIL",
};
IntentLauncher.startActivityAsync(activityAction, intentParams);
}
else if (Platform.OS === "ios") {
Linking.openURL('message://');
}
Be sure to test on real devices if possible since the iPhone simulator doesn't have a mail client installed.
List of URL schemes on wikipedia
Update: If you don't mind an extra dependency, take a look at react-native-email-link which has the added benefit of allowing users to select from any installed email client. Neat!
I'm hosting a video website and ran into a problem I can't really explain. I host the videos using cloudflare which provides a hls stream url.
On non-apple devices I use the hls.js library to stream these videos, this is done using the (React) code below:
useEffect(() => {
let video = document.getElementById(video_id);
if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = hlsUrl
return (() => {
clearWatchTimeout()
video.src = null
})
} else if (Hls.isSupported()) {
let hls = new Hls({});
hls.attachMedia(video);
hls.on(Hls.Events.MEDIA_ATTACHED, function () {
hls.loadSource(hlsUrl);
hls.on(Hls.Events.MANIFEST_PARSED, function (event, data) {
console.log("manifest loaded, found " + data.levels.length + " quality level");
})
});
return (() => {
clearWatchTimeout()
hls.detachMedia()
hls.destroy()
})
} else {
console.log("This device is not capable of playing hls streams")
}
});
The videoplayer works, but somehow the quality drops with the hls.js implementation. This can most easily be noticed on apple devices by playing the video in safari (quality remains ok) compared to playing the video in chrome (quality deteriorates). An example video can which should have crappy quality can be seen following the link below:
https://www.etudor.nl/chapter/1008/1216/1196/Getal-en-Ruimte-vwo-B-deel-4-(11e-editie)-Hoofdstuk-13
I have turned on debugging mode of the hls library but I haven't found anything weird in the logs.
EDIT:
Just as an extra tidbit of information: If I manually raise the quality to a higher level (say level 4) the video uses that quality and runs perfectly, so bandwidth really does not seem to be the issue.
EDIT 2:
Another bit of information. If I log the bandwidth estimate over time, it seems to decrease every time the quality level has been decreased. If i manually up the quality to level 4, the bandwidth estimate immediately increases.
Assuming that my Codename One web-app is supposed to run on a mobile device, I tried to lock its orientation to portrait.
My problem is that the following build hint doesn't force the use of a web-app in portrait mode:
javascript.manifest.orientation=portrait
Am I missing something?
I also tried the following native interface, without success:
(function (exports) {
var o = {};
o.lockPortraitOrientation_ = function (callback) {
// Screen Orientation API, https://w3c.github.io/screen-orientation/#examples
screen.orientation.lock('portrait');
callback.complete();
};
o.isSupported_ = function (callback) {
callback.complete(true);
};
exports.cool_teammate_apps_frontend_NativeUtilities = o;
})(cn1_get_native_interfaces());
Is the browser you tested on within the compatibility table here?
Assuming it is try this syntax for the native code:
screen.lockOrientationUniversal = screen.lockOrientation || screen.mozLockOrientation || screen.msLockOrientation;
if (screen.lockOrientationUniversal("portrait-primary")) {
// orientation was locked
} else {
// orientation lock failed
}
I'll check whether we can make our Display orientation calls map to these API's.
I am currently working on a hybrid application (Ionic) and have a problem with detecting iBeacons on iOS (currently developing on 9.2). I'm using cordova-plugin-estimote (https://github.com/evothings/phonegap-estimotebeacons) to detect beacons, followed their documentation and everything works fine on Android, but not on iOS. The beacons are simply not detected. There is no error, it just doesn't find anything.
Fun fact: When I downloaded the original estimote app on iPhone, it also did not detect any iBeacons until I logged in. After that, it started detecting them normally. Why is this happening?
Relevant part of my AngularJS code based on plugin documentation:
$scope.init = function() {
bluetoothSerial.isEnabled(scanBeacons, errorBluetooth);
}
function scanBeacons() {
startScanning();
updateList = $interval(updateView, 5000);
}
function startScanning() {
console.log("requesting permissions");
estimote.beacons.requestAlwaysAuthorization(successAuth, errorAuth);
}
function successAuth(){
console.log("success auth, starting scan");
estimote.beacons.startRangingBeaconsInRegion(
{},
onMonitoringSuccess,
onError);
}
function errorAuth(){
console.log("error auth");
popupService.showPopup("Authorization error", "Location services required to perform scanning");
}
function onMonitoringSuccess(regionState) {
console.log("monitoring success: "+JSON.stringify(regionState));
var successHandler = function (response) {
$scope.downloadedlist = response;
$scope.offline = false;
};
var errorHandler = function (response) {
$scope.beaconList = regionState.beacons;
};
eventService.getBeaconList(regionState.beacons, storageService.getEventId())
.then(successHandler)
.catch(errorHandler);
}
function onError(response) {
console.log("monitoring error: "+JSON.stringify(response));
popupService.showPopup('popup.error.title', 'popup.error.server');
}
As you can see, I have some console.log statements (it has to be done this way) and I'm getting "requesting permissions", instantly followed by "success auth, starting scan". It's weird because authorization popup is displayed but the code does not wait for the user input, it just automatically fires success handler (successAuth) function and that's it. No more logs, which means no monitoring success, no error, it just doesn't find any beacons.
Any help appreciated. Thanks.
Finally found the solution. The problem was here:
estimote.beacons.startRangingBeaconsInRegion(
{},
onMonitoringSuccess,
onError);
Turns out Android allows for {} parameter (which means look for all regions) in the following function but iOS doesn't. After specifying a region, my application successfuly finds the beacons.
Example:
function successAuth(){
console.log("success auth, starting scan");
var region = { uuid: 'YOUR_UUID_HERE' }
estimote.beacons.startRangingBeaconsInRegion(
region,
onMonitoringSuccess,
onError);
}
Estimote Developer quote:
iOS only allows scanning for beacons the UUID of which you know. All Estimote Beacons ship with our default UUID, "B9407F30-F5F8-466E-AFF9-25556B57FE6D", and these are the beacons you can see on the radar even when not logged in. If you change this UUID to something else, then you need to stay logged in so that Estimote app can know what the UUIDs of your beacons are, and scan for them in addition to the default UUID.
Source: https://forums.estimote.com/t/beacons-not-detected-using-estimote-ios-app/1580/5
This also explains why Estimote App started detecting iBeacons after logging in.
When an user attempts to log in without Internet connection, I just need to check whether he/she is connected to internet or not.
I tried the following code :
if (status == '404') {
$scope.error="No internet connection";
return false;
}
But this status 404 comes even when my Web Service failed to connect. I need to differentiate both, if is the user's internet connection issue or the Web Service connection issue.
Using navigator.onLine
You can use navigator.onLine and wrap it on a helper variable, like this (Credits to this answer)
myApp.run(function($window, $rootScope) {
$rootScope.online = navigator.onLine;
$window.addEventListener("offline", function () {
$rootScope.$apply(function() {
$rootScope.online = false;
});
}, false);
$window.addEventListener("online", function () {
$rootScope.$apply(function() {
$rootScope.online = true;
});
}, false);
});
and then watch it in the controller:
$scope.$watch('online', function(newStatus) { ... });
But that just serves as a dirty check to know if the PC is actually connected to a network, not meaning that the internet is working.
Using a fake AJAX request
You can mock a service that does a fake request to a browser (warning: non-tested code below)
myApp.service('Internet', function($http){
this.IsOk = function () {
return $http({ method: 'HEAD', url: '/' + window.location.hostname + "/?rand=" + Math.floor((1 + Math.random()) * 0x10000) })
.then(function(response) {
var status = response.status;
return status >= 200 && status < 300 || status === 304;
});
}
});
And then use something in this context:
myApp.controller('TestController', function(Internet){
Internet.IsOn().then(function(isok){
if(isok) {...} else {...}
});
});
Code for request mocking in this link.
Also note that it will not work using localhost, because the server will work even when disconnected to the internet.
Taken from MDN's summary of NavigatorOnLine.onLine.
Browsers implement this property differently.
In Chrome and Safari, if the browser is not able to connect to a local
area network (LAN) or a router, it is offline; all other conditions
return true. So while you can assume that the browser is offline when
it returns a false value, you cannot assume that a true value
necessarily means that the browser can access the internet. You could
be getting false positives, such as in cases where the computer is
running a virtualization software that has virtual ethernet adapters
that are always "connected." Therefore, if you really want to
determine the online status of the browser, you should develop
additional means for checking. To learn more, see the HTML5 Rocks
article, Working Off the Grid.
In Firefox and Internet Explorer, switching the browser to offline
mode sends a false value. All other conditions return a true value.
You can see changes in the network state by listening for the events
on window.onOnline and window.onOffline.
You can access that information via window.navigator.onLine, but as the documentation states, it's very inconsistent cross-browser.
You can also listen for changes in status using window.addEventListener as follows:
window.addEventListener("offline", function(e) {alert("offline");})
window.addEventListener("online", function(e) {alert("online");})
There is a JavaScript property, navigator.onLine, it returns a Boolean value that specifies wether the browser is in online mode or offline mode.
The navigator object contains information about the user's browser.
It is supported in all major browsers, so you sholdn't have any problem at all.