UNPKG

@robotical/sensors-dashboard

Version:

A dashboard tool for depicting Marty data by Robotical

196 lines (195 loc) 8.68 kB
"use strict"; require("core-js/modules/esnext.iterator.filter.js"); Object.defineProperty(exports, "__esModule", { value: true }); exports.isMicroBitWebBluetoothSupported = exports.isMicroBitDevice = exports.default = void 0; require("core-js/modules/web.dom-collections.iterator.js"); require("core-js/modules/es.promise.js"); require("core-js/modules/es.array-buffer.slice.js"); require("core-js/modules/es.typed-array.uint8-array.js"); require("core-js/modules/es.typed-array.fill.js"); require("core-js/modules/es.typed-array.set.js"); require("core-js/modules/es.typed-array.sort.js"); require("core-js/modules/es.typed-array.to-locale-string.js"); require("core-js/modules/esnext.iterator.constructor.js"); require("core-js/modules/esnext.iterator.for-each.js"); require("core-js/modules/es.symbol.description.js"); function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } const MICROBIT_SERVICE = 0xf005; const MICROBIT_SENSOR_CHARACTERISTIC = "5261da01-fa7e-42ab-850b-7c80220097cc"; const SENSOR_DATA_TIMEOUT_MS = 4500; const getWebBluetooth = () => navigator.bluetooth; const signed16 = (high, low) => { let value = low | high << 8; if (value >= 1 << 15) { value -= 1 << 16; } return value; }; const copySensors = sensors => _objectSpread(_objectSpread({}, sensors), {}, { touchPins: [...sensors.touchPins] }); const isMicroBitWebBluetoothSupported = () => { var _getWebBluetooth; return typeof navigator !== "undefined" && typeof ((_getWebBluetooth = getWebBluetooth()) === null || _getWebBluetooth === void 0 ? void 0 : _getWebBluetooth.requestDevice) === "function"; }; exports.isMicroBitWebBluetoothSupported = isMicroBitWebBluetoothSupported; const isMicroBitDevice = device => Boolean(device && typeof device === "object" && device.isMicroBitWebBluetooth); exports.isMicroBitDevice = isMicroBitDevice; class MicroBitWebBluetooth { constructor() { _defineProperty(this, "id", ""); _defineProperty(this, "name", "micro:bit"); _defineProperty(this, "deviceType", "micro:bit"); _defineProperty(this, "isMicroBitWebBluetooth", true); _defineProperty(this, "sensors", MicroBitWebBluetooth.defaultSensors()); _defineProperty(this, "device", null); _defineProperty(this, "server", null); _defineProperty(this, "sensorCharacteristic", null); _defineProperty(this, "sensorListeners", new Set()); _defineProperty(this, "disconnectListeners", new Set()); _defineProperty(this, "receiveTimeoutId", null); _defineProperty(this, "didEmitDisconnect", false); _defineProperty(this, "handleCharacteristicValueChanged", event => { var _event$target; this.handleSensorData((_event$target = event.target) === null || _event$target === void 0 ? void 0 : _event$target.value); }); _defineProperty(this, "handleGattServerDisconnected", () => { this.clearReceiveTimeout(); this.releaseConnection(); this.emitDisconnect(); }); } static defaultSensors() { return { tiltX: 0, tiltY: 0, buttonA: 0, buttonB: 0, touchPins: [0, 0, 0], gestureState: 0 }; } async connect() { const bluetooth = getWebBluetooth(); if (!bluetooth) { throw new Error("Web Bluetooth is not available in this browser."); } const device = await bluetooth.requestDevice({ filters: [{ services: [MICROBIT_SERVICE] }] }); if (!device.gatt) { throw new Error("The selected micro:bit does not expose a Bluetooth connection."); } this.device = device; this.id = device.id || "microbit-".concat(Date.now()); this.name = device.name || "micro:bit"; this.didEmitDisconnect = false; device.addEventListener("gattserverdisconnected", this.handleGattServerDisconnected); try { this.server = await device.gatt.connect(); const service = await this.server.getPrimaryService(MICROBIT_SERVICE); this.sensorCharacteristic = await service.getCharacteristic(MICROBIT_SENSOR_CHARACTERISTIC); this.sensorCharacteristic.addEventListener("characteristicvaluechanged", this.handleCharacteristicValueChanged); await this.sensorCharacteristic.startNotifications(); try { this.handleSensorData(await this.sensorCharacteristic.readValue()); } catch (_unused) { // Some firmware begins notifying before direct reads are available. } this.resetReceiveTimeout(); return this; } catch (error) { this.releaseConnection(); throw error; } } disconnect() { this.clearReceiveTimeout(); this.releaseConnection(); this.emitDisconnect(); } isConnected() { var _this$server; return Boolean((_this$server = this.server) === null || _this$server === void 0 ? void 0 : _this$server.connected); } getFriendlyName() { return this.name || "micro:bit"; } addSensorListener(callback) { this.sensorListeners.add(callback); return () => { this.sensorListeners.delete(callback); }; } addDisconnectListener(callback) { this.disconnectListeners.add(callback); return () => { this.disconnectListeners.delete(callback); }; } handleSensorData(value) { if (!value || value.byteLength < 10) { return; } const data = new Uint8Array(value.buffer, value.byteOffset, value.byteLength); const previous = copySensors(this.sensors); this.sensors = { tiltX: signed16(data[0], data[1]), tiltY: signed16(data[2], data[3]), buttonA: data[4], buttonB: data[5], touchPins: [data[6], data[7], data[8]], gestureState: data[9] }; this.sensorListeners.forEach(callback => callback(this.sensors, previous)); this.resetReceiveTimeout(); } releaseConnection() { var _this$device, _this$server2; if (this.sensorCharacteristic) { this.sensorCharacteristic.removeEventListener("characteristicvaluechanged", this.handleCharacteristicValueChanged); try { var _this$sensorCharacter, _this$sensorCharacter2; (_this$sensorCharacter = (_this$sensorCharacter2 = this.sensorCharacteristic).stopNotifications) === null || _this$sensorCharacter === void 0 ? void 0 : _this$sensorCharacter.call(_this$sensorCharacter2).catch(() => undefined); } catch (_unused2) { // Ignore cleanup errors from a device that is already disconnected. } } (_this$device = this.device) === null || _this$device === void 0 ? void 0 : _this$device.removeEventListener("gattserverdisconnected", this.handleGattServerDisconnected); if ((_this$server2 = this.server) !== null && _this$server2 !== void 0 && _this$server2.connected) { this.server.disconnect(); } this.server = null; this.sensorCharacteristic = null; this.device = null; } resetReceiveTimeout() { this.clearReceiveTimeout(); this.receiveTimeoutId = window.setTimeout(() => this.disconnect(), SENSOR_DATA_TIMEOUT_MS); } clearReceiveTimeout() { if (this.receiveTimeoutId !== null) { window.clearTimeout(this.receiveTimeoutId); this.receiveTimeoutId = null; } } emitDisconnect() { if (this.didEmitDisconnect) { return; } this.didEmitDisconnect = true; const listeners = Array.from(this.disconnectListeners); listeners.forEach(callback => callback(this)); this.sensorListeners.clear(); this.disconnectListeners.clear(); } } exports.default = MicroBitWebBluetooth;