mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2024-12-24 23:12:24 -05:00
Adding Ev3 extension with three test blocks to latest device-connection work, auto-connecting to device for now.
This commit is contained in:
parent
4332725d33
commit
2d8ad05a78
6 changed files with 476 additions and 15 deletions
|
@ -15,6 +15,7 @@ const Scratch3SpeakBlocks = require('../extensions/scratch3_speak');
|
||||||
const Scratch3TranslateBlocks = require('../extensions/scratch3_translate');
|
const Scratch3TranslateBlocks = require('../extensions/scratch3_translate');
|
||||||
const Scratch3VideoSensingBlocks = require('../extensions/scratch3_video_sensing');
|
const Scratch3VideoSensingBlocks = require('../extensions/scratch3_video_sensing');
|
||||||
const Scratch3SpeechBlocks = require('../extensions/scratch3_speech');
|
const Scratch3SpeechBlocks = require('../extensions/scratch3_speech');
|
||||||
|
const Scratch3Ev3Blocks = require('../extensions/scratch3_ev3');
|
||||||
|
|
||||||
const builtinExtensions = {
|
const builtinExtensions = {
|
||||||
pen: Scratch3PenBlocks,
|
pen: Scratch3PenBlocks,
|
||||||
|
@ -24,7 +25,8 @@ const builtinExtensions = {
|
||||||
speak: Scratch3SpeakBlocks,
|
speak: Scratch3SpeakBlocks,
|
||||||
translate: Scratch3TranslateBlocks,
|
translate: Scratch3TranslateBlocks,
|
||||||
videoSensing: Scratch3VideoSensingBlocks,
|
videoSensing: Scratch3VideoSensingBlocks,
|
||||||
speech: Scratch3SpeechBlocks
|
speech: Scratch3SpeechBlocks,
|
||||||
|
ev3: Scratch3Ev3Blocks
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
381
src/extensions/scratch3_ev3/index.js
Normal file
381
src/extensions/scratch3_ev3/index.js
Normal file
|
@ -0,0 +1,381 @@
|
||||||
|
const ArgumentType = require('../../extension-support/argument-type');
|
||||||
|
const BlockType = require('../../extension-support/block-type');
|
||||||
|
const Cast = require('../../util/cast');
|
||||||
|
const log = require('../../util/log');
|
||||||
|
const Base64Util = require('../../util/base64-util');
|
||||||
|
const BTSession = require('../../io/BTSession');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* High-level primitives / constants used by the extension.
|
||||||
|
* @type {object}
|
||||||
|
*/
|
||||||
|
const BTCommand = {
|
||||||
|
LAYER: 0x00,
|
||||||
|
NUM8: 0x81,
|
||||||
|
NUM16: 0x82,
|
||||||
|
NUM32: 0x83,
|
||||||
|
COAST: 0x0,
|
||||||
|
BRAKE: 0x1,
|
||||||
|
LONGRAMP: 50,
|
||||||
|
STEPSPEED: 0xAE,
|
||||||
|
TIMESPEED: 0xAF,
|
||||||
|
OUTPUTSTOP: 0xA3,
|
||||||
|
OUTPUTRESET: 0xA2,
|
||||||
|
STEPSPEEDSYNC: 0xB0,
|
||||||
|
TIMESPEEDSYNC: 0xB1
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of accepted motor ports.
|
||||||
|
* @note These should not be translated as they correspond to labels on
|
||||||
|
* the EV3 hub.
|
||||||
|
* @type {array}
|
||||||
|
*/
|
||||||
|
const MOTOR_PORTS = [
|
||||||
|
{
|
||||||
|
name: 'A',
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'B',
|
||||||
|
value: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'C',
|
||||||
|
value: 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'D',
|
||||||
|
value: 8
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of accepted sensor ports.
|
||||||
|
* @note These should not be translated as they correspond to labels on
|
||||||
|
* the EV3 hub.
|
||||||
|
* @type {array}
|
||||||
|
*/
|
||||||
|
// const SENSOR_PORTS = ['1', '2', '3', '4'];
|
||||||
|
|
||||||
|
class EV3 {
|
||||||
|
|
||||||
|
constructor (runtime, extensionId) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Scratch 3.0 runtime used to trigger the green flag button.
|
||||||
|
* @type {Runtime}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this._runtime = runtime;
|
||||||
|
|
||||||
|
this.connected = false;
|
||||||
|
this.speed = 50;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Bluetooth connection session for reading/writing device data.
|
||||||
|
* @type {BTSession}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this._bt = null;
|
||||||
|
this._runtime.registerExtensionDevice(extensionId, this);
|
||||||
|
|
||||||
|
// TODO: auto-connect temporary - until button is added
|
||||||
|
this.startDeviceScan();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: keep here?
|
||||||
|
/**
|
||||||
|
* Called by the runtime when user wants to scan for a device.
|
||||||
|
*/
|
||||||
|
startDeviceScan () {
|
||||||
|
log.info('making a new BT session');
|
||||||
|
this._bt = new BTSession(this._runtime, {
|
||||||
|
majorDeviceClass: 8,
|
||||||
|
minorDeviceClass: 1
|
||||||
|
}, this._onSessionConnect.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: keep here?
|
||||||
|
/**
|
||||||
|
* Called by the runtime when user wants to connect to a certain device.
|
||||||
|
* @param {number} id - the id of the device to connect to.
|
||||||
|
*/
|
||||||
|
connectDevice (id) {
|
||||||
|
this._bt.connectDevice(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
beep () {
|
||||||
|
if (!this.connected) return;
|
||||||
|
this._bt.sendMessage({
|
||||||
|
message: 'DwAAAIAAAJQBgQKC6AOC6AM=',
|
||||||
|
encoding: 'base64'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
motorTurnClockwise (port, time) {
|
||||||
|
if (!this.connected) return;
|
||||||
|
|
||||||
|
// Build up motor command
|
||||||
|
const cmd = this._applyPrefix(0, this._motorCommand(
|
||||||
|
BTCommand.TIMESPEED,
|
||||||
|
port,
|
||||||
|
time,
|
||||||
|
this.speed,
|
||||||
|
BTCommand.LONGRAMP
|
||||||
|
));
|
||||||
|
|
||||||
|
// Send message
|
||||||
|
this._bt.sendMessage({
|
||||||
|
message: Base64Util.arrayBufferToBase64(cmd),
|
||||||
|
encoding: 'base64'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Yield for time
|
||||||
|
return new Promise(resolve => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve();
|
||||||
|
}, time);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
motorTurnCounterClockwise (port, time) {
|
||||||
|
if (!this.connected) return;
|
||||||
|
|
||||||
|
// Build up motor command
|
||||||
|
const cmd = this._applyPrefix(0, this._motorCommand(
|
||||||
|
BTCommand.TIMESPEED,
|
||||||
|
port,
|
||||||
|
time,
|
||||||
|
this.speed * -1,
|
||||||
|
BTCommand.LONGRAMP
|
||||||
|
));
|
||||||
|
|
||||||
|
// Send message
|
||||||
|
this._bt.sendMessage({
|
||||||
|
message: Base64Util.arrayBufferToBase64(cmd),
|
||||||
|
encoding: 'base64'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Yield for time
|
||||||
|
return new Promise(resolve => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve();
|
||||||
|
}, time);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_applyPrefix (n, cmd) {
|
||||||
|
const len = cmd.length + 5;
|
||||||
|
return [].concat(
|
||||||
|
len & 0xFF,
|
||||||
|
(len >> 8) & 0xFF,
|
||||||
|
0x1,
|
||||||
|
0x0,
|
||||||
|
0x0,
|
||||||
|
n,
|
||||||
|
0x0,
|
||||||
|
cmd
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a motor command in EV3 byte array format (CMD, LAYER, PORT,
|
||||||
|
* SPEED, RAMP UP, RUN, RAMP DOWN, BREAKING TYPE)
|
||||||
|
* @param {string} command Motor command primitive (i.e. "prefix")
|
||||||
|
* @param {string} port Port to address
|
||||||
|
* @param {number} n Value to be passed to motor command
|
||||||
|
* @param {number} speed Speed value
|
||||||
|
* @param {number} ramp Ramp value
|
||||||
|
* @return {array} Byte array
|
||||||
|
*/
|
||||||
|
_motorCommand (command, port, n, speed, ramp) {
|
||||||
|
/**
|
||||||
|
* Generate run values for a given input.
|
||||||
|
* @param {number} run Run input
|
||||||
|
* @return {array} Run values (byte array)
|
||||||
|
*/
|
||||||
|
const getRunValues = function (run) {
|
||||||
|
// If run duration is less than max 16-bit integer
|
||||||
|
if (run < 0x7fff) {
|
||||||
|
return [
|
||||||
|
BTCommand.NUM16,
|
||||||
|
run & 0xff,
|
||||||
|
(run >> 8) & 0xff
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run forever
|
||||||
|
return [
|
||||||
|
BTCommand.NUM32,
|
||||||
|
run & 0xff,
|
||||||
|
(run >> 8) & 0xff,
|
||||||
|
(run >> 16) & 0xff,
|
||||||
|
(run >> 24) & 0xff
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
// If speed is less than zero, make it positive and multiply the input
|
||||||
|
// value by -1
|
||||||
|
if (speed < 0) {
|
||||||
|
speed = -1 * speed;
|
||||||
|
n = -1 * n;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the input value is less than 0
|
||||||
|
const dir = (n < 0) ? 0x100 - speed : speed; // step negative or possitive
|
||||||
|
n = Math.abs(n);
|
||||||
|
|
||||||
|
// Setup motor run duration and ramping behavior
|
||||||
|
let rampup = ramp;
|
||||||
|
let rampdown = ramp;
|
||||||
|
let run = n - ramp * 2;
|
||||||
|
if (run < 0) {
|
||||||
|
rampup = Math.floor(n / 2);
|
||||||
|
run = 0;
|
||||||
|
rampdown = n - rampup;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate motor command
|
||||||
|
const runcmd = getRunValues(run);
|
||||||
|
return [
|
||||||
|
command,
|
||||||
|
BTCommand.LAYER,
|
||||||
|
port,
|
||||||
|
BTCommand.NUM8,
|
||||||
|
dir & 0xff,
|
||||||
|
BTCommand.NUM8,
|
||||||
|
rampup
|
||||||
|
].concat(runcmd.concat([
|
||||||
|
BTCommand.NUM8,
|
||||||
|
rampdown,
|
||||||
|
BTCommand.BRAKE
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
_onSessionConnect () {
|
||||||
|
log.info('bt device connected!');
|
||||||
|
this.connected = true;
|
||||||
|
// start reading data?
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Scratch3Ev3Blocks {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ID of the extension.
|
||||||
|
* @return {string} the id
|
||||||
|
*/
|
||||||
|
static get EXTENSION_ID () {
|
||||||
|
return 'ev3';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of the EV3 extension.
|
||||||
|
* @param {object} runtime VM runtime
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
constructor (runtime) {
|
||||||
|
/**
|
||||||
|
* The Scratch 3.0 runtime.
|
||||||
|
* @type {Runtime}
|
||||||
|
*/
|
||||||
|
this.runtime = runtime;
|
||||||
|
|
||||||
|
// Create a new MicroBit device instance
|
||||||
|
this._device = new EV3(this.runtime, Scratch3Ev3Blocks.EXTENSION_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the EV3 extension.
|
||||||
|
* @return {object} Extension description.
|
||||||
|
*/
|
||||||
|
getInfo () {
|
||||||
|
return {
|
||||||
|
id: Scratch3Ev3Blocks.EXTENSION_ID,
|
||||||
|
name: 'LEGO MINDSTORMS EV3',
|
||||||
|
iconURI: null,
|
||||||
|
blocks: [
|
||||||
|
{
|
||||||
|
opcode: 'motorTurnClockwise',
|
||||||
|
text: '[PORT] turn clockwise [TIME] seconds',
|
||||||
|
blockType: BlockType.COMMAND,
|
||||||
|
arguments: {
|
||||||
|
PORT: {
|
||||||
|
type: ArgumentType.STRING,
|
||||||
|
menu: 'motorPorts',
|
||||||
|
defaultValue: MOTOR_PORTS[0].value
|
||||||
|
},
|
||||||
|
TIME: {
|
||||||
|
type: ArgumentType.NUMBER,
|
||||||
|
defaultValue: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
opcode: 'motorTurnCounterClockwise',
|
||||||
|
text: '[PORT] turn counter [TIME] seconds',
|
||||||
|
blockType: BlockType.COMMAND,
|
||||||
|
arguments: {
|
||||||
|
PORT: {
|
||||||
|
type: ArgumentType.STRING,
|
||||||
|
menu: 'motorPorts',
|
||||||
|
defaultValue: MOTOR_PORTS[0].value
|
||||||
|
},
|
||||||
|
TIME: {
|
||||||
|
type: ArgumentType.NUMBER,
|
||||||
|
defaultValue: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
opcode: 'beep',
|
||||||
|
text: 'beep',
|
||||||
|
blockType: BlockType.COMMAND
|
||||||
|
}
|
||||||
|
],
|
||||||
|
menus: {
|
||||||
|
motorPorts: this._buildMenu(MOTOR_PORTS)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create data for a menu in scratch-blocks format, consisting of an array of objects with text and
|
||||||
|
* value properties. The text is a translated string, and the value is one-indexed.
|
||||||
|
* @param {object[]} info - An array of info objects each having a name property.
|
||||||
|
* @return {array} - An array of objects with text and value properties.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_buildMenu (info) {
|
||||||
|
return info.map((entry, index) => {
|
||||||
|
const obj = {};
|
||||||
|
obj.text = entry.name;
|
||||||
|
obj.value = String(index + 1);
|
||||||
|
return obj;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
motorTurnClockwise (args) {
|
||||||
|
// Validate arguments
|
||||||
|
const port = Cast.toNumber(args.PORT);
|
||||||
|
const time = Cast.toNumber(args.TIME) * 1000;
|
||||||
|
|
||||||
|
this._device.motorTurnClockwise(port, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
motorTurnCounterClockwise (args) {
|
||||||
|
// Validate arguments
|
||||||
|
const port = Cast.toNumber(args.PORT);
|
||||||
|
const time = Cast.toNumber(args.TIME) * 1000;
|
||||||
|
|
||||||
|
this._device.motorTurnCounterClockwise(port, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
beep () {
|
||||||
|
return this._device.beep();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Scratch3Ev3Blocks;
|
|
@ -111,7 +111,7 @@ class MicroBit {
|
||||||
* Called by the runtime when user wants to scan for a device.
|
* Called by the runtime when user wants to scan for a device.
|
||||||
*/
|
*/
|
||||||
startDeviceScan () {
|
startDeviceScan () {
|
||||||
console.log('making a new BLE session');
|
log.info('making a new BLE session');
|
||||||
this._ble = new BLESession(this._runtime, {
|
this._ble = new BLESession(this._runtime, {
|
||||||
filters: [
|
filters: [
|
||||||
{services: [BLEUUID.service]}
|
{services: [BLEUUID.service]}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const JSONRPCWebSocket = require('../util/jsonrpc-web-socket');
|
const JSONRPCWebSocket = require('../util/jsonrpc-web-socket');
|
||||||
|
const log = require('../util/log');
|
||||||
const ScratchLinkWebSocket = 'ws://localhost:20110/scratch/ble';
|
const ScratchLinkWebSocket = 'ws://localhost:20110/scratch/ble';
|
||||||
|
|
||||||
class BLESession extends JSONRPCWebSocket {
|
class BLESession extends JSONRPCWebSocket {
|
||||||
|
@ -34,7 +35,7 @@ class BLESession extends JSONRPCWebSocket {
|
||||||
if (this._ws.readyState === 1) { // is this needed since it's only called on ws.onopen?
|
if (this._ws.readyState === 1) { // is this needed since it's only called on ws.onopen?
|
||||||
// TODO: start a 'discover' timeout
|
// TODO: start a 'discover' timeout
|
||||||
this.sendRemoteRequest('discover', this._deviceOptions)
|
this.sendRemoteRequest('discover', this._deviceOptions)
|
||||||
.catch(e => this._sendError('error on discover')); // never reached?
|
.catch(e => this._sendError(e)); // never reached?
|
||||||
}
|
}
|
||||||
// TODO: else?
|
// TODO: else?
|
||||||
}
|
}
|
||||||
|
@ -47,7 +48,7 @@ class BLESession extends JSONRPCWebSocket {
|
||||||
connectDevice (id) {
|
connectDevice (id) {
|
||||||
this.sendRemoteRequest('connect', {peripheralId: id})
|
this.sendRemoteRequest('connect', {peripheralId: id})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
console.log('should have connected');
|
log.info('should have connected');
|
||||||
this._runtime.emit(this._runtime.constructor.PERIPHERAL_CONNECTED);
|
this._runtime.emit(this._runtime.constructor.PERIPHERAL_CONNECTED);
|
||||||
this._connectCallback();
|
this._connectCallback();
|
||||||
})
|
})
|
||||||
|
@ -118,8 +119,8 @@ class BLESession extends JSONRPCWebSocket {
|
||||||
}
|
}
|
||||||
|
|
||||||
_sendError (e) {
|
_sendError (e) {
|
||||||
console.log(`BLESession error:`);
|
log.error(`BLESession error:`);
|
||||||
console.log(e);
|
log.error(e);
|
||||||
this._runtime.emit(this._runtime.constructor.PERIPHERAL_ERROR);
|
this._runtime.emit(this._runtime.constructor.PERIPHERAL_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +1,84 @@
|
||||||
const JSONRPCWebSocket = require('../util/jsonrpc');
|
const JSONRPCWebSocket = require('../util/jsonrpc-web-socket');
|
||||||
|
const log = require('../util/log');
|
||||||
const ScratchLinkWebSocket = 'ws://localhost:20110/scratch/bt';
|
const ScratchLinkWebSocket = 'ws://localhost:20110/scratch/bt';
|
||||||
|
|
||||||
class BTSession extends JSONRPCWebSocket {
|
class BTSession extends JSONRPCWebSocket {
|
||||||
constructor () {
|
|
||||||
super(new WebSocket(ScratchLinkWebSocket));
|
/**
|
||||||
|
* A BT device session object. It handles connecting, over web sockets, to
|
||||||
|
* BT devices, and reading and writing data to them.
|
||||||
|
* @param {Runtime} runtime - the Runtime for sending/receiving GUI update events.
|
||||||
|
* @param {object} deviceOptions - the list of options for device discovery.
|
||||||
|
* @param {object} connectCallback - a callback for connection.
|
||||||
|
*/
|
||||||
|
constructor (runtime, deviceOptions, connectCallback) {
|
||||||
|
const ws = new WebSocket(ScratchLinkWebSocket);
|
||||||
|
super(ws);
|
||||||
|
|
||||||
|
this._ws = ws;
|
||||||
|
this._ws.onopen = this.requestDevice.bind(this); // only call request device after socket opens
|
||||||
|
this._ws.onerror = this._sendError.bind(this, 'ws onerror');
|
||||||
|
this._ws.onclose = this._sendError.bind(this, 'ws onclose');
|
||||||
|
|
||||||
|
this._availablePeripherals = {};
|
||||||
|
this._connectCallback = connectCallback;
|
||||||
|
this._characteristicDidChangeCallback = null;
|
||||||
|
this._deviceOptions = deviceOptions;
|
||||||
|
this._runtime = runtime;
|
||||||
}
|
}
|
||||||
|
|
||||||
requestDevice (options) {
|
/**
|
||||||
return this.sendRemoteRequest('discover', options);
|
* Request connection to the device.
|
||||||
|
* If the web socket is not yet open, request when the socket promise resolves.
|
||||||
|
*/
|
||||||
|
requestDevice () {
|
||||||
|
if (this._ws.readyState === 1) { // is this needed since it's only called on ws.onopen?
|
||||||
|
// TODO: start a 'discover' timeout
|
||||||
|
this.sendRemoteRequest('discover', this._deviceOptions)
|
||||||
|
.catch(e => this._sendError(e)); // never reached?
|
||||||
|
}
|
||||||
|
// TODO: else?
|
||||||
}
|
}
|
||||||
|
|
||||||
connectDevice (options) {
|
/**
|
||||||
return this.sendRemoteRequest('connect', options);
|
* Try connecting to the input peripheral id, and then call the connect
|
||||||
|
* callback if connection is successful.
|
||||||
|
* @param {number} id - the id of the peripheral to connect to
|
||||||
|
*/
|
||||||
|
connectDevice (id) {
|
||||||
|
this.sendRemoteRequest('connect', {peripheralId: id})
|
||||||
|
.then(() => {
|
||||||
|
log.info('should have connected');
|
||||||
|
this._runtime.emit(this._runtime.constructor.PERIPHERAL_CONNECTED);
|
||||||
|
this._connectCallback();
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
this._sendError(e);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessage (options) {
|
sendMessage (options) {
|
||||||
return this.sendRemoteRequest('send', options);
|
return this.sendRemoteRequest('send', options);
|
||||||
}
|
}
|
||||||
|
|
||||||
didReceiveCall (method /* , params */) {
|
/**
|
||||||
|
* Handle a received call from the socket.
|
||||||
|
* @param {string} method - a received method label.
|
||||||
|
* @param {object} params - a received list of parameters.
|
||||||
|
* @return {object} - optional return value.
|
||||||
|
*/
|
||||||
|
didReceiveCall (method, params) {
|
||||||
// TODO: Add peripheral 'undiscover' handling
|
// TODO: Add peripheral 'undiscover' handling
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case 'didDiscoverPeripheral':
|
case 'didDiscoverPeripheral':
|
||||||
// TODO: do something on peripheral discovered
|
/* this._availablePeripherals[params.peripheralId] = params;
|
||||||
|
this._runtime.emit(
|
||||||
|
this._runtime.constructor.PERIPHERAL_LIST_UPDATE,
|
||||||
|
this._availablePeripherals
|
||||||
|
); */
|
||||||
|
// TODO: auto-connect temporary until button is added
|
||||||
|
this.connectDevice(params.peripheralId);
|
||||||
|
// TODO: cancel a discover timeout if one is active
|
||||||
break;
|
break;
|
||||||
case 'didReceiveMessage':
|
case 'didReceiveMessage':
|
||||||
// TODO: do something on received message
|
// TODO: do something on received message
|
||||||
|
@ -31,6 +87,12 @@ class BTSession extends JSONRPCWebSocket {
|
||||||
return 'nah';
|
return 'nah';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_sendError (e) {
|
||||||
|
log.error(`BLESession error:`);
|
||||||
|
log.error(e);
|
||||||
|
this._runtime.emit(this._runtime.constructor.PERIPHERAL_ERROR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = BTSession;
|
module.exports = BTSession;
|
||||||
|
|
|
@ -28,6 +28,21 @@ class Base64Util {
|
||||||
return base64;
|
return base64;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert an array buffer to a base64 encoded string.
|
||||||
|
* @param {array} buffer - an array buffer to convert.
|
||||||
|
* @return {string} - the base64 encoded string.
|
||||||
|
*/
|
||||||
|
static arrayBufferToBase64 (buffer) {
|
||||||
|
let binary = '';
|
||||||
|
const bytes = new Uint8Array(buffer);
|
||||||
|
const len = bytes.byteLength;
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
binary += String.fromCharCode(bytes[ i ]);
|
||||||
|
}
|
||||||
|
return btoa(binary);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Base64Util;
|
module.exports = Base64Util;
|
||||||
|
|
Loading…
Reference in a new issue