diff --git a/examples/web_bluetooth.css b/examples/web_bluetooth.css
index 72440983..634693c4 100644
--- a/examples/web_bluetooth.css
+++ b/examples/web_bluetooth.css
@@ -7,19 +7,10 @@ div {
margin-bottom: 10px;
}
-div#current_color {
- position: absolute;
- top: 61px;
- left: 120px;
-}
-
div#color {
border: 1px solid #666666;
width: 20px;
height: 20px;
- position: absolute;
- top: -4px;
- left: 92px;
}
table td {
diff --git a/examples/web_bluetooth.html b/examples/web_bluetooth.html
index ce68b232..329c3ad7 100644
--- a/examples/web_bluetooth.html
+++ b/examples/web_bluetooth.html
@@ -57,8 +57,8 @@
const hubs = poweredUP.getHubs(); // Get an array of all connected hubs
document.getElementById("color").style.backgroundColor = PoweredUP.Consts.ColorNames[color];
hubs.forEach(async (hub) => {
- const led = await hub.waitForDeviceByType(PoweredUP.Consts.DeviceType.HUB_LED);
- led.setColor(color); // Set the color
+ const led = await hub.waitForDeviceByType(PoweredUP.Consts.DeviceType.HUB_LED);
+ led.setColor(color); // Set the color
})
color++;
if (color > 10) {
@@ -82,7 +82,23 @@
const scan = function () {
if (PoweredUP.isWebBluetooth) {
- poweredUP.scan(); // Start scanning for hubs
+ poweredUP.scan(); // Start scanning for known hubs
+ } else {
+ alert("Your browser does not support the Web Bluetooth specification.");
+ }
+}
+
+const stop = function () {
+ if (PoweredUP.isWebBluetooth) {
+ poweredUP.stop(); // Stop scanning for known hubs
+ } else {
+ alert("Your browser does not support the Web Bluetooth specification.");
+ }
+}
+
+const requestDevice = function () {
+ if (PoweredUP.isWebBluetooth) {
+ poweredUP.requestDevice(); // Start scanning for new hubs
} else {
alert("Your browser does not support the Web Bluetooth specification.");
}
@@ -99,9 +115,11 @@
Web Bluetooth node-poweredup Example
-
+
diff --git a/src/poweredup-browser.ts b/src/poweredup-browser.ts
index 779f7acc..24e42afc 100644
--- a/src/poweredup-browser.ts
+++ b/src/poweredup-browser.ts
@@ -25,6 +25,8 @@ export class PoweredUP extends EventEmitter {
private _connectedHubs: {[uuid: string]: BaseHub} = {};
+ private _scan: boolean = false;
+ private _pendingDevices: BluetoothDevice[] = [];
constructor () {
@@ -35,9 +37,9 @@ export class PoweredUP extends EventEmitter {
/**
* Begin scanning for Powered UP Hub devices.
- * @method PoweredUP#scan
+ * @method PoweredUP#requestDevice
*/
- public async scan () {
+ public async requestDevice () {
try {
@@ -61,6 +63,10 @@ export class PoweredUP extends EventEmitter {
]
});
+ if (this._scan) {
+ this._watchAdvertisements(device);
+ }
+
// @ts-ignore
const server = await device.gatt.connect();
this._discoveryEventHandler.call(this, server);
@@ -73,6 +79,48 @@ export class PoweredUP extends EventEmitter {
}
+ /**
+ * Begin scanning for Powered UP Hub devices that were connected before.
+ * @method PoweredUP#scan
+ */
+ public async scan () {
+ try {
+ if (this._scan) {
+ return true;
+ }
+
+ this._scan = true;
+ const devices = await navigator.bluetooth.getDevices();
+
+ debug("Start watching advertisements");
+
+ for (const device of devices) {
+ this._watchAdvertisements(device);
+ }
+
+ return true;
+
+ } catch (err) {
+ debug(err);
+ return false;
+ }
+ }
+
+
+ /**
+ * Stop scanning for Powered UP Hub devices.
+ * @method PoweredUP#stop
+ */
+ public async stop () {
+ if (!this.scan) {
+ return true;
+ }
+
+ debug("Stop watching advertisements");
+ this._scan = false;
+ return true;
+ }
+
/**
* Retrieve a list of Powered UP Hubs.
* @method PoweredUP#getHubs
@@ -127,6 +175,45 @@ export class PoweredUP extends EventEmitter {
}
+ private _watchAdvertisements (device: BluetoothDevice) {
+ if (device.watchingAdvertisements) {
+ return;
+ }
+
+ device.addEventListener('advertisementreceived', async (event) => {
+ // Chrome 87 does not support unwatchAdvertisements yet.
+ if (!this._scan) {
+ return;
+ }
+
+ // If the connection to the hub was lost, it is not possible
+ // to re-connect as long as 'connected' is still true.
+ if (device.gatt === undefined || device.gatt.connected || this._pendingDevices.includes(device)) {
+ debug('Ignored advertisement from ' + device.id);
+ return;
+ }
+
+ // Ignore further reconnects for one second.
+ this._pendingDevices.push(device);
+
+ try {
+ const server = await device.gatt.connect();
+ debug(device.id + ' connected');
+ this._discoveryEventHandler.call(this, server);
+ } catch (err) {
+ debug(err);
+ } finally {
+ setTimeout(() => this._pendingDevices.splice(this._pendingDevices.indexOf(device), 1), 1000);
+ }
+ });
+
+ device.addEventListener('gattserverdisconnected', async (event) => debug(device.id + ' disconnected'));
+ device.watchAdvertisements();
+
+ debug('Watching advertisements of ' + device.id);
+ }
+
+
private _determineLPF2HubType (device: IBLEAbstraction): Promise {
return new Promise((resolve, reject) => {
let buf: Buffer = Buffer.alloc(0);
@@ -177,14 +264,14 @@ export class PoweredUP extends EventEmitter {
try {
await device.discoverCharacteristicsForService(Consts.BLEService.WEDO2_SMART_HUB);
hubType = Consts.HubType.WEDO2_SMART_HUB;
- // tslint:disable-next-line
+ // tslint:disable-next-line
} catch (error) {}
try {
if (hubType !== Consts.HubType.WEDO2_SMART_HUB) {
await device.discoverCharacteristicsForService(Consts.BLEService.LPF2_HUB);
isLPF2Hub = true;
}
- // tslint:disable-next-line
+ // tslint:disable-next-line
} catch (error) {}
if (isLPF2Hub) {