UNPKG

node-aurora

Version:

Provides an interface to the Aurora Dreamband.

970 lines (709 loc) 40.2 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _regenerator = require('babel-runtime/regenerator'); var _regenerator2 = _interopRequireDefault(_regenerator); var _slicedToArray2 = require('babel-runtime/helpers/slicedToArray'); var _slicedToArray3 = _interopRequireDefault(_slicedToArray2); var _promise = require('babel-runtime/core-js/promise'); var _promise2 = _interopRequireDefault(_promise); var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator'); var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2); var _values = require('babel-runtime/core-js/object/values'); var _values2 = _interopRequireDefault(_values); var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); var _createClass2 = require('babel-runtime/helpers/createClass'); var _createClass3 = _interopRequireDefault(_createClass2); var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); var _inherits2 = require('babel-runtime/helpers/inherits'); var _inherits3 = _interopRequireDefault(_inherits2); var _events = require('events'); var _events2 = _interopRequireDefault(_events); var _keyBy = require('lodash/keyBy'); var _keyBy2 = _interopRequireDefault(_keyBy); var _util = require('./util'); var _AuroraBluetoothParser = require('./AuroraBluetoothParser'); var _AuroraBluetoothParser2 = _interopRequireDefault(_AuroraBluetoothParser); var _AuroraConstants = require('./AuroraConstants'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } //nasty nasty nasty, see https://github.com/sandeepmistry/noble/issues/570 var noble = void 0; try { noble = require('noble'); } catch (err) { noble = new _events2.default(); } var INIT_DELAY_MS = 5000; var DISCONNECT_RETRY_DELAY_MS = 3000; var AuroraBluetooth = function (_EventEmitter) { (0, _inherits3.default)(AuroraBluetooth, _EventEmitter); function AuroraBluetooth() { (0, _classCallCheck3.default)(this, AuroraBluetooth); var _this = (0, _possibleConstructorReturn3.default)(this, (AuroraBluetooth.__proto__ || (0, _getPrototypeOf2.default)(AuroraBluetooth)).call(this)); _this._onAdapterStateChange = function (state) { if (state == 'poweredOn') { if (_this._connectionState == AuroraBluetooth.ConnectionStates.INIT) { //don't fire event here, just set connection state directly _this._connectionState = AuroraBluetooth.ConnectionStates.DISCONNECTED; } } else if (state == 'poweredOff') { _this._connectionState = AuroraBluetooth.ConnectionStates.INIT; } }; _this._onPeripheralDisconnect = function () { _this._setConnectionState(AuroraBluetooth.ConnectionStates.DISCONNECTED); }; _this._onPeripheralFound = function (peripheral) { peripheral.connect(function (error) { if (error) { return; } peripheral.discoverSomeServicesAndCharacteristics([_AuroraConstants.BleAuroraService], (0, _values2.default)(_AuroraConstants.BleAuroraChars), function (error, services, characteristics) { if (error) { return; } if (!_this._connectPromise) { throw new Error('Peripheral found event fired without valid connection promise.'); } _this._connectPromise.resolve([peripheral, services[0], characteristics]); _this._connectPromise = null; noble.stopScanning(); }); }); }; _this._onPeripheralScanStop = function () { clearTimeout(_this._connectTimer); noble.removeListener('discover', _this._onPeripheralFound); if (_this._connectPromise) { _this._setConnectionState(AuroraBluetooth.ConnectionStates.DISCONNECTED); _this._connectPromise.reject('Connection cancelled.'); _this._connectPromise = null; } }; _this._onParseCmdResponseRead = function (bytesToRead, cbAfterRead) { _this._charRead(_this._cmdDataChar, bytesToRead).then(cbAfterRead); }; _this._onParseCmdResponseWrite = function (buffer, cbAfterWrite) { _this._charWrite(_this._cmdDataChar, buffer).then(function () { _this._charWrite(_this._cmdStatusChar, Buffer.from([_AuroraConstants.BleCmdStates.IDLE])).then(cbAfterWrite); }); }; _this._onParseCmdInputRequested = function () { _this.emit('cmdInputRequested'); }; _this._onParseCmdOutputReady = function (output) { _this.emit('cmdOutputReady', output); }; _this._onParseAuroraEvent = function (auroraEvent) { auroraEvent.origin = 'bluetooth'; _this.emit('auroraEvent', auroraEvent); }; _this._onParseStreamData = function (streamData) { streamData.origin = 'bluetooth'; _this.emit('streamData', streamData); }; _this._onParseError = function (error) { _this.emit('bluetoothError', 'Parse Error: ' + error); }; _this._initializing = false; _this._connectionState = AuroraBluetooth.ConnectionStates.INIT; _this._disconnectPending = false; _this._bluetoothParser = new _AuroraBluetoothParser2.default(); _this._bluetoothParser.on('parseError', _this._onParseError); _this._bluetoothParser.on('cmdResponseRead', _this._onParseCmdResponseRead); _this._bluetoothParser.on('cmdResponseWrite', _this._onParseCmdResponseWrite); _this._bluetoothParser.on('cmdInputRequested', _this._onParseCmdInputRequested); _this._bluetoothParser.on('cmdOutputReady', _this._onParseCmdOutputReady); _this._bluetoothParser.on('auroraEvent', _this._onParseAuroraEvent); _this._bluetoothParser.on('streamData', _this._onParseStreamData); _this._watchBluetoothAdapter(); return _this; } (0, _createClass3.default)(AuroraBluetooth, [{ key: 'isConnected', value: function isConnected() { return this._connectionState == AuroraBluetooth.ConnectionStates.CONNECTED_IDLE || this._connectionState == AuroraBluetooth.ConnectionStates.CONNECTED_BUSY; } }, { key: 'isConnecting', value: function isConnecting() { return this._connectionState == AuroraBluetooth.ConnectionStates.CONNECTING; } }, { key: 'connect', value: function () { var _ref = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee() { var _this2 = this; var timeoutMs = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 30000; var _ref2, _ref3, peripheral, service, characteristics; return _regenerator2.default.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: if (!this._initializing) { _context.next = 2; break; } return _context.abrupt('return'); case 2: if (!(this._connectionState == AuroraBluetooth.ConnectionStates.INIT)) { _context.next = 10; break; } this._initializing = true; //sleep for a little bit _context.next = 6; return (0, _util.sleep)(INIT_DELAY_MS); case 6: this._initializing = false; //if the state hasn't changed since we waited for //initialization to complete, something is wrong if (!(this._connectionState == AuroraBluetooth.ConnectionStates.INIT)) { _context.next = 9; break; } return _context.abrupt('return', _promise2.default.reject('No bluetooth adapter found. Is bluetooth disabled?')); case 9: return _context.abrupt('return', this.connect(timeoutMs)); case 10: if (!(this._connectionState != AuroraBluetooth.ConnectionStates.DISCONNECTED)) { _context.next = 17; break; } _context.t0 = this._connectionState; _context.next = _context.t0 === AuroraBluetooth.ConnectionStates.CONNECTING ? 14 : _context.t0 === AuroraBluetooth.ConnectionStates.CONNECTED_BUSY ? 15 : _context.t0 === AuroraBluetooth.ConnectionStates.CONNECTED_IDLE ? 15 : 16; break; case 14: return _context.abrupt('return', _promise2.default.reject('Already connecting...')); case 15: return _context.abrupt('return', _promise2.default.reject('Already connected.')); case 16: return _context.abrupt('return', _promise2.default.reject('Unknown Bluetooth connection state.')); case 17: this._setConnectionState(AuroraBluetooth.ConnectionStates.CONNECTING); _context.prev = 18; _context.next = 21; return this._connect(timeoutMs); case 21: _ref2 = _context.sent; _ref3 = (0, _slicedToArray3.default)(_ref2, 3); peripheral = _ref3[0]; service = _ref3[1]; characteristics = _ref3[2]; this._peripheral = peripheral; this._service = service; this._peripheral.once('disconnect', this._onPeripheralDisconnect); this._characteristicsByUUID = (0, _keyBy2.default)(characteristics, 'uuid'); //these get used a lot, so let's store references this._cmdStatusChar = this._characteristicsByUUID[_AuroraConstants.BleAuroraChars.CMD_STATUS]; this._cmdDataChar = this._characteristicsByUUID[_AuroraConstants.BleAuroraChars.CMD_DATA]; this._cmdOutputChar = this._characteristicsByUUID[_AuroraConstants.BleAuroraChars.CMD_OUTPUT_INDICATED]; _context.next = 35; return this._charSubscribe(this._characteristicsByUUID[_AuroraConstants.BleAuroraChars.STREAM_DATA_NOTIFIED], this._onParseStreamData); case 35: _context.next = 37; return this._charSubscribe(this._characteristicsByUUID[_AuroraConstants.BleAuroraChars.AURORA_EVENT_NOTIFIED], function (event) { _this2._bluetoothParser.onAuroraEventCharNotification(event); }); case 37: _context.next = 39; return this._charSubscribe(this._cmdStatusChar, function (status) { _this2._bluetoothParser.onCmdStatusCharNotification(status); }); case 39: _context.next = 41; return this._charSubscribe(this._cmdOutputChar, function (output) { _this2._bluetoothParser.onCmdOutputCharNotification(output); }); case 41: this._setConnectionState(AuroraBluetooth.ConnectionStates.CONNECTED_IDLE); return _context.abrupt('return', this._peripheral); case 45: _context.prev = 45; _context.t1 = _context['catch'](18); this._setConnectionState(AuroraBluetooth.ConnectionStates.DISCONNECTED); return _context.abrupt('return', _promise2.default.reject(_context.t1)); case 49: case 'end': return _context.stop(); } } }, _callee, this, [[18, 45]]); })); function connect() { return _ref.apply(this, arguments); } return connect; }() }, { key: 'disconnect', value: function () { var _ref4 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee2() { var _this3 = this; return _regenerator2.default.wrap(function _callee2$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: if (!(this._connectionState == AuroraBluetooth.ConnectionStates.DISCONNECTED || this._disconnectPending)) { _context2.next = 2; break; } return _context2.abrupt('return'); case 2: this._disconnectPending = true; //check if we are in the process of connecting, or are processing a command if (!(this._connectionState == AuroraBluetooth.ConnectionStates.CONNECTING)) { _context2.next = 11; break; } noble.stopScanning(); //give scanning a little time to stop _context2.next = 7; return (0, _util.sleep)(20); case 7: if (!(this._connectionState != AuroraBluetooth.ConnectionStates.DISCONNECTED)) { _context2.next = 9; break; } return _context2.abrupt('return', _promise2.default.reject('Failed to disconnect. Scanning not stopped.')); case 9: _context2.next = 14; break; case 11: if (!(this._connectionState == AuroraBluetooth.ConnectionStates.CONNECTED_BUSY)) { _context2.next = 14; break; } _context2.next = 14; return (0, _util.sleep)(DISCONNECT_RETRY_DELAY_MS); case 14: if (!(this._connectionState == AuroraBluetooth.ConnectionStates.DISCONNECTED)) { _context2.next = 16; break; } return _context2.abrupt('return'); case 16: return _context2.abrupt('return', (0, _util.promisify)(this._peripheral.disconnect, this._peripheral)().then(function () { //in case disconnected event hasn't fired yet, we fire it here _this3._setConnectionState(AuroraBluetooth.ConnectionStates.DISCONNECTED); })); case 17: case 'end': return _context2.stop(); } } }, _callee2, this); })); function disconnect() { return _ref4.apply(this, arguments); } return disconnect; }() }, { key: 'writeCmd', value: function () { var _ref5 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee4(cmd) { var _this4 = this; return _regenerator2.default.wrap(function _callee4$(_context4) { while (1) { switch (_context4.prev = _context4.next) { case 0: if (!(this._connectionState != AuroraBluetooth.ConnectionStates.CONNECTED_IDLE)) { _context4.next = 7; break; } _context4.t0 = this._connectionState; _context4.next = _context4.t0 === AuroraBluetooth.ConnectionStates.DISCONNECTED ? 4 : _context4.t0 === AuroraBluetooth.ConnectionStates.ADAPTER_FOUND ? 4 : _context4.t0 === AuroraBluetooth.ConnectionStates.DEVICE_FOUND ? 4 : _context4.t0 === AuroraBluetooth.ConnectionStates.CONNECTED_BUSY ? 5 : 6; break; case 4: return _context4.abrupt('return', _promise2.default.reject('No idle serial connection.')); case 5: return _context4.abrupt('return', _promise2.default.reject('Another command is already in progress.')); case 6: return _context4.abrupt('return', _promise2.default.reject('Unknown Bluetooth connection state.')); case 7: if (!this._disconnectPending) { _context4.next = 9; break; } return _context4.abrupt('return', _promise2.default.reject('Bluetooth currently disconnecting.')); case 9: this._setConnectionState(AuroraBluetooth.ConnectionStates.CONNECTED_BUSY); return _context4.abrupt('return', new _promise2.default(function () { var _ref6 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee3(resolve, reject) { return _regenerator2.default.wrap(function _callee3$(_context3) { while (1) { switch (_context3.prev = _context3.next) { case 0: _context3.prev = 0; _this4._bluetoothParser.once('cmdResponse', function (cmdResponse) { _this4._setConnectionState(AuroraBluetooth.ConnectionStates.CONNECTED_IDLE); cmdResponse.origin = 'bluetooth'; resolve(cmdResponse); }); //write the status byte, indicating start of command _context3.next = 4; return _this4._charWrite(_this4._cmdStatusChar, Buffer.from([_AuroraConstants.BleCmdStates.IDLE])); case 4: _context3.next = 6; return _this4._charWrite(_this4._cmdDataChar, Buffer.from(cmd, 'ascii')); case 6: //let the parser know the command too _this4._bluetoothParser.setCmd(cmd); //write the status byte, indicating end of command _context3.next = 9; return _this4._charWrite(_this4._cmdStatusChar, Buffer.from([_AuroraConstants.BleCmdStates.CMD_EXECUTE])); case 9: _context3.next = 17; break; case 11: _context3.prev = 11; _context3.t0 = _context3['catch'](0); _this4._bluetoothParser.reset(); _this4._bluetoothParser.removeAllListeners('cmdResponse'); _this4._setConnectionState(AuroraBluetooth.ConnectionStates.CONNECTED_IDLE); reject(_context3.t0); case 17: case 'end': return _context3.stop(); } } }, _callee3, _this4, [[0, 11]]); })); return function (_x3, _x4) { return _ref6.apply(this, arguments); }; }())); case 11: case 'end': return _context4.stop(); } } }, _callee4, this); })); function writeCmd(_x2) { return _ref5.apply(this, arguments); } return writeCmd; }() }, { key: 'writeCmdInput', value: function () { var _ref7 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee5(data) { return _regenerator2.default.wrap(function _callee5$(_context5) { while (1) { switch (_context5.prev = _context5.next) { case 0: if (!(this._connectionState != AuroraBluetooth.ConnectionStates.CONNECTED_BUSY)) { _context5.next = 7; break; } _context5.t0 = this._connectionState; _context5.next = _context5.t0 === AuroraBluetooth.ConnectionStates.DISCONNECTED ? 4 : _context5.t0 === AuroraBluetooth.ConnectionStates.ADAPTER_FOUND ? 4 : _context5.t0 === AuroraBluetooth.ConnectionStates.DEVICE_FOUND ? 4 : _context5.t0 === AuroraBluetooth.ConnectionStates.CONNECTED_IDLE ? 5 : 6; break; case 4: return _context5.abrupt('return', _promise2.default.reject('No idle serial connection.')); case 5: return _context5.abrupt('return', _promise2.default.reject('Command input can only be written during a command.')); case 6: return _context5.abrupt('return', _promise2.default.reject('Unknown Bluetooth connection state.')); case 7: return _context5.abrupt('return', this._charWrite(this._cmdDataChar, data)); case 8: case 'end': return _context5.stop(); } } }, _callee5, this); })); function writeCmdInput(_x5) { return _ref7.apply(this, arguments); } return writeCmdInput; }() }, { key: '_setConnectionState', value: function _setConnectionState(connectionState) { if (this._connectionState == connectionState) { return; } var previousConnectionState = this._connectionState; this._connectionState = connectionState; if (this._connectionState == AuroraBluetooth.ConnectionStates.DISCONNECTED) { this._disconnectPending = false; } //console.log(`${previousConnectionState} --> ${connectionState}`); this.emit('connectionStateChange', connectionState, previousConnectionState); } }, { key: '_connect', value: function () { var _ref8 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee6(timeoutMs) { var _this5 = this; return _regenerator2.default.wrap(function _callee6$(_context6) { while (1) { switch (_context6.prev = _context6.next) { case 0: if (!this._connectPromise) { _context6.next = 2; break; } throw new Error('Already have a pending connection.'); case 2: return _context6.abrupt('return', new _promise2.default(function (resolve, reject) { _this5._connectPromise = { resolve: resolve, reject: reject }; //remove any existing listeners just in case noble.removeListener('discover', _this5._onPeripheralFound); noble.removeListener('scanStop', _this5._onPeripheralScanStop); noble.on('discover', _this5._onPeripheralFound); noble.once('scanStop', _this5._onPeripheralScanStop); noble.startScanning([_AuroraConstants.BleAuroraService], false); clearTimeout(_this5._connectTimer); if (timeoutMs > 0) { _this5._connectTimer = setTimeout(function () { _this5._connectPromise = null; noble.stopScanning(); noble.removeListener('discover', _this5._onPeripheralFound); reject('Timeout waiting for bluetooth connection.'); }, timeoutMs); } })); case 3: case 'end': return _context6.stop(); } } }, _callee6, this); })); function _connect(_x6) { return _ref8.apply(this, arguments); } return _connect; }() }, { key: '_charWritePacket', value: function () { var _ref9 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee7(char, packet) { return _regenerator2.default.wrap(function _callee7$(_context7) { while (1) { switch (_context7.prev = _context7.next) { case 0: if (Buffer.isBuffer(packet)) { _context7.next = 2; break; } return _context7.abrupt('return', _promise2.default.reject('Packet parameter is not a valid buffer.')); case 2: if (packet.length) { _context7.next = 4; break; } return _context7.abrupt('return', _promise2.default.resolve()); case 4: if (!(packet.length > _AuroraConstants.BLE_CMD_MAX_PACKET_LENGTH)) { _context7.next = 6; break; } return _context7.abrupt('return', _promise2.default.reject('Exceeded max write packet length.')); case 6: return _context7.abrupt('return', new _promise2.default(function (resolve, reject) { //write a packet, the false here means the callback //isn't executed until the other side confirms receipt char.write(packet, false, function (error) { if (error) return reject(error); resolve(); }); })); case 7: case 'end': return _context7.stop(); } } }, _callee7, this); })); function _charWritePacket(_x7, _x8) { return _ref9.apply(this, arguments); } return _charWritePacket; }() }, { key: '_charReadPacket', value: function () { var _ref10 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee8(char) { return _regenerator2.default.wrap(function _callee8$(_context8) { while (1) { switch (_context8.prev = _context8.next) { case 0: return _context8.abrupt('return', new _promise2.default(function (resolve, reject) { //write a packet, the false here means the callback //isn't executed until the other side confirms receipt char.read(function (error, packet) { if (error) return reject(error); resolve(packet); }); })); case 1: case 'end': return _context8.stop(); } } }, _callee8, this); })); function _charReadPacket(_x9) { return _ref10.apply(this, arguments); } return _charReadPacket; }() }, { key: '_charWrite', value: function () { var _ref11 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee9(char, buffer) { var i, packet; return _regenerator2.default.wrap(function _callee9$(_context9) { while (1) { switch (_context9.prev = _context9.next) { case 0: if (Buffer.isBuffer(buffer)) { _context9.next = 2; break; } throw 'Buffer parameter is not a valid buffer.'; case 2: if (buffer.length) { _context9.next = 4; break; } return _context9.abrupt('return'); case 4: i = 0; case 5: if (!(i < buffer.length + _AuroraConstants.BLE_CMD_MAX_PACKET_LENGTH)) { _context9.next = 14; break; } //create a buffer slice <= 20 bytes //slice handles case where buffer < 20 packet = buffer.slice(i, i + _AuroraConstants.BLE_CMD_MAX_PACKET_LENGTH); //if this slice is empty, nothing to do if (packet.length) { _context9.next = 9; break; } return _context9.abrupt('break', 14); case 9: _context9.next = 11; return this._charWritePacket(char, packet); case 11: i += _AuroraConstants.BLE_CMD_MAX_PACKET_LENGTH; _context9.next = 5; break; case 14: case 'end': return _context9.stop(); } } }, _callee9, this); })); function _charWrite(_x10, _x11) { return _ref11.apply(this, arguments); } return _charWrite; }() }, { key: '_charRead', value: function () { var _ref12 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee10(char, numBytes) { var packets, packetCount, packet; return _regenerator2.default.wrap(function _callee10$(_context10) { while (1) { switch (_context10.prev = _context10.next) { case 0: if (!(numBytes <= 0)) { _context10.next = 2; break; } throw 'Trying to read less than 1 byte.'; case 2: packets = []; packetCount = Math.ceil(numBytes / _AuroraConstants.BLE_CMD_MAX_PACKET_LENGTH); //read packets until we've read all required bytes case 4: if (!packetCount--) { _context10.next = 11; break; } _context10.next = 7; return this._charReadPacket(char); case 7: packet = _context10.sent; packets.push(packet); _context10.next = 4; break; case 11: return _context10.abrupt('return', Buffer.concat(packets, numBytes)); case 12: case 'end': return _context10.stop(); } } }, _callee10, this); })); function _charRead(_x12, _x13) { return _ref12.apply(this, arguments); } return _charRead; }() }, { key: '_charSubscribe', value: function () { var _ref13 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee11(char, onNotification) { return _regenerator2.default.wrap(function _callee11$(_context11) { while (1) { switch (_context11.prev = _context11.next) { case 0: return _context11.abrupt('return', new _promise2.default(function (resolve, reject) { char.subscribe(function (error) { if (error) return reject(error); char.on('data', onNotification); resolve(); }); })); case 1: case 'end': return _context11.stop(); } } }, _callee11, this); })); function _charSubscribe(_x14, _x15) { return _ref13.apply(this, arguments); } return _charSubscribe; }() }, { key: '_watchBluetoothAdapter', value: function _watchBluetoothAdapter() { this._unwatchBluetoothAdapter(); noble.on('stateChange', this._onAdapterStateChange); } }, { key: '_unwatchBluetoothAdapter', value: function _unwatchBluetoothAdapter() { noble.removeListener('stateChange', this._onAdapterStateChange); } }]); return AuroraBluetooth; }(_events2.default); AuroraBluetooth.ConnectionStates = { INIT: 'init', DISCONNECTED: 'disconnected', CONNECTING: 'connecting', CONNECTED_IDLE: 'idle', CONNECTED_BUSY: 'busy' }; exports.default = AuroraBluetooth;