mirror of
https://github.com/scratchfoundation/scratchx.git
synced 2024-11-24 16:48:07 -05:00
Update scratch_extensions
for upcoming scratchr2
This corresponds to the upcoming October 20th release
This commit is contained in:
parent
ed239eb0f0
commit
47d9063829
4 changed files with 300 additions and 127 deletions
|
@ -1,91 +1,99 @@
|
||||||
// Communicate with the Scratch Device Manager through Socket.IO
|
// Communicate with the Scratch Device Manager through Socket.IO
|
||||||
window.ScratchDeviceManager = new (function () {
|
window.ScratchDeviceManager = new (function () {
|
||||||
var self = this;
|
var instance = this;
|
||||||
|
var sockets = [];
|
||||||
|
|
||||||
|
// Assume it's OK until we find out otherwise
|
||||||
|
var isConnected = true;
|
||||||
|
|
||||||
// device-manager.scratch.mit.edu = 127.0.0.1
|
// device-manager.scratch.mit.edu = 127.0.0.1
|
||||||
self.deviceManagerHost = 'https://device-manager.scratch.mit.edu:3030';
|
instance.deviceManagerHost = 'https://device-manager.scratch.mit.edu:3030';
|
||||||
|
|
||||||
// work around https://github.com/socketio/socket.io-client/issues/812
|
// work around https://github.com/socketio/socket.io-client/issues/812
|
||||||
function connectNamespace(namespace) {
|
function connectNamespace(namespace) {
|
||||||
return io(self.deviceManagerHost + namespace, {forceNew: true});
|
return io(instance.deviceManagerHost + namespace, {forceNew: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.wedo2_list = function (callback) {
|
function onClose(){
|
||||||
$.ajax(self.deviceManagerHost + '/wedo2/list', {
|
for(var i=0; i<sockets.length; i++){
|
||||||
dataType: 'text',
|
sockets[i].disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onbeforeunload = onClose;
|
||||||
|
|
||||||
|
instance.device_list = function (ext_type, ext_name, device_spec, callback) {
|
||||||
|
var url = instance.deviceManagerHost + '/' + ext_type + '/list';
|
||||||
|
var data = {
|
||||||
|
name: ext_name,
|
||||||
|
spec: device_spec
|
||||||
|
};
|
||||||
|
$.ajax(url, {
|
||||||
|
data: {data: JSON.stringify(data)},
|
||||||
|
dataType: 'json',
|
||||||
success: function (data, textStatus, jqXHR) {
|
success: function (data, textStatus, jqXHR) {
|
||||||
var deviceList = JSON.parse(data);
|
isConnected = true;
|
||||||
if (deviceList.constructor == Array) {
|
if (data.constructor == Array) {
|
||||||
callback(deviceList);
|
callback(data, ext_type, ext_name);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
error: function (jqXHR, textStatus, errorThrown) {
|
||||||
|
isConnected = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: handle multiple devices
|
// Attempt to open a device-specific socket connection to the Device Manager.
|
||||||
self.wedo2_open = function (deviceId, callback) {
|
// This must call `callback` exactly once no matter what.
|
||||||
var socket = connectNamespace('/wedo2');
|
// The callback will receive a connected socket on success or `null` on failure.
|
||||||
var pluginDevice = new RawWeDo2(deviceId, socket);
|
instance.socket_open = function (ext_name, deviceType, deviceId, callback) {
|
||||||
socket.on('deviceWasOpened', function (event) {
|
function onDeviceWasOpened () {
|
||||||
callback(pluginDevice);
|
// If this is the first event on this socket then respond with success.
|
||||||
});
|
if (clearOpenTimeout()) {
|
||||||
socket.emit('open', {deviceId: deviceId});
|
callback(socket);
|
||||||
};
|
|
||||||
|
|
||||||
function RawWeDo2(deviceId, socket) {
|
|
||||||
var WeDo = this;
|
|
||||||
var eventHandlers = {};
|
|
||||||
|
|
||||||
WeDo.close = function() {
|
|
||||||
socket.close();
|
|
||||||
};
|
|
||||||
|
|
||||||
WeDo.setMotorOn = function(motorIndex, power) {
|
|
||||||
socket.emit('motorOn', {motorIndex:motorIndex, power:power});
|
|
||||||
};
|
|
||||||
|
|
||||||
WeDo.setMotorOff = function(motorIndex) {
|
|
||||||
socket.emit('motorOff', {motorIndex:motorIndex});
|
|
||||||
};
|
|
||||||
|
|
||||||
WeDo.setMotorBrake = function(motorIndex) {
|
|
||||||
socket.emit('motorBrake', {motorIndex:motorIndex});
|
|
||||||
};
|
|
||||||
|
|
||||||
WeDo.setLED = function(rgb) {
|
|
||||||
socket.emit('setLED', {rgb:rgb});
|
|
||||||
};
|
|
||||||
|
|
||||||
WeDo.playTone = function(tone, durationMs) {
|
|
||||||
socket.emit('playTone', {tone:tone, ms:durationMs});
|
|
||||||
};
|
|
||||||
|
|
||||||
WeDo.stopTone = function() {
|
|
||||||
socket.emit('stopTone');
|
|
||||||
};
|
|
||||||
|
|
||||||
function setHandler(eventName, handler) {
|
|
||||||
if (eventHandlers.hasOwnProperty(eventName)) {
|
|
||||||
var oldHandler = eventHandlers[eventName];
|
|
||||||
if (oldHandler) {
|
|
||||||
socket.removeListener(eventName, oldHandler);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (handler) {
|
|
||||||
socket.on(eventName, handler);
|
function onDisconnect () {
|
||||||
|
var socketIndex = sockets.indexOf(socket);
|
||||||
|
if (socketIndex >= 0) {
|
||||||
|
sockets.splice(socketIndex, 1);
|
||||||
|
}
|
||||||
|
// If this is the first event on this socket then respond with failure.
|
||||||
|
if (clearOpenTimeout()) {
|
||||||
|
callback(null);
|
||||||
}
|
}
|
||||||
eventHandlers[eventName] = handler;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// function handler(event) { access event.sensorName and event.sensorValue }
|
function onTimeout () {
|
||||||
WeDo.setSensorHandler = function (handler) {
|
// This will trigger `onDisconnect()`
|
||||||
setHandler('sensorChanged', handler);
|
socket.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the timeout is still pending, clear it and return true. Otherwise, return false.
|
||||||
|
// Callers can use the return value to determine whether they are the first to respond on this socket.
|
||||||
|
function clearOpenTimeout () {
|
||||||
|
if (openTimeout !== null) {
|
||||||
|
clearTimeout(openTimeout);
|
||||||
|
openTimeout = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var socket = connectNamespace('/' + deviceType);
|
||||||
|
sockets.push(socket);
|
||||||
|
|
||||||
|
socket.on('deviceWasOpened', onDeviceWasOpened);
|
||||||
|
socket.on('disconnect', onDisconnect);
|
||||||
|
var openTimeout = setTimeout(onTimeout, 10 * 1000);
|
||||||
|
|
||||||
|
socket.emit('open', {deviceId: deviceId, name: ext_name});
|
||||||
};
|
};
|
||||||
|
|
||||||
WeDo.setDeviceWasClosedHandler = function (handler) {
|
instance.isConnected = function () {
|
||||||
// TODO: resolve this ambiguity
|
return isConnected;
|
||||||
setHandler('disconnect', handler);
|
|
||||||
setHandler('deviceWasClosed', handler);
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -106,8 +106,13 @@ window.ScratchExtensions = new (function () {
|
||||||
var callback = function (retval) {
|
var callback = function (retval) {
|
||||||
Scratch.FlashApp.ASobj.ASextensionReporterDone(ext_name, job_id, retval);
|
Scratch.FlashApp.ASobj.ASextensionReporterDone(ext_name, job_id, retval);
|
||||||
};
|
};
|
||||||
|
if(handlers[ext_name]._getStatus().status != 2){
|
||||||
|
callback(false);
|
||||||
|
}
|
||||||
|
else{
|
||||||
args.push(callback);
|
args.push(callback);
|
||||||
handlers[ext_name][reporter].apply(handlers[ext_name], args);
|
handlers[ext_name][reporter].apply(handlers[ext_name], args);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
lib.getReporterForceAsync = function (ext_name, reporter, args, job_id) {
|
lib.getReporterForceAsync = function (ext_name, reporter, args, job_id) {
|
||||||
|
@ -134,8 +139,9 @@ window.ScratchExtensions = new (function () {
|
||||||
|
|
||||||
if (ext_name in deviceSpecs) {
|
if (ext_name in deviceSpecs) {
|
||||||
switch (deviceSpecs[ext_name].type) {
|
switch (deviceSpecs[ext_name].type) {
|
||||||
|
case 'ble':
|
||||||
case 'wedo2':
|
case 'wedo2':
|
||||||
if (!ScratchDeviceManager) {
|
if (!(ScratchDeviceManager && ScratchDeviceManager.isConnected())) {
|
||||||
return {status: 0, msg: 'Missing Scratch Device Manager'};
|
return {status: 0, msg: 'Missing Scratch Device Manager'};
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -195,29 +201,28 @@ window.ScratchExtensions = new (function () {
|
||||||
if (!awaitingSpecs['hid']) awaitingSpecs['hid'] = {};
|
if (!awaitingSpecs['hid']) awaitingSpecs['hid'] = {};
|
||||||
awaitingSpecs['hid'][spec.vendor + '_' + spec.product] = ext_name;
|
awaitingSpecs['hid'][spec.vendor + '_' + spec.product] = ext_name;
|
||||||
}
|
}
|
||||||
else if (spec.type == 'serial') {
|
else {
|
||||||
awaitingSpecs['serial'] = ext_name;
|
awaitingSpecs[spec.type] = ext_name;
|
||||||
}
|
|
||||||
else if (spec.type == 'wedo2') {
|
|
||||||
awaitingSpecs['wedo2'] = ext_name;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plugin) {
|
for (var specType in awaitingSpecs) {
|
||||||
if (awaitingSpecs['hid']) {
|
if (!awaitingSpecs.hasOwnProperty(specType)) continue;
|
||||||
|
|
||||||
|
if (plugin && specType == 'hid') {
|
||||||
|
var awaitingHid = awaitingSpecs['hid'];
|
||||||
plugin.hid_list(function (deviceList) {
|
plugin.hid_list(function (deviceList) {
|
||||||
var hidList = awaitingSpecs['hid'];
|
|
||||||
for (var i = 0; i < deviceList.length; i++) {
|
for (var i = 0; i < deviceList.length; i++) {
|
||||||
var ext_name = hidList[deviceList[i]["vendor_id"] + '_' + deviceList[i]["product_id"]];
|
var deviceID = deviceList[i]["vendor_id"] + '_' + deviceList[i]["product_id"];
|
||||||
if (ext_name) {
|
var hid_ext_name = awaitingHid[deviceID];
|
||||||
handlers[ext_name]._deviceConnected(new HidDevice(deviceList[i], ext_name));
|
if (hid_ext_name) {
|
||||||
|
handlers[hid_ext_name]._deviceConnected(new HidDevice(deviceList[i], hid_ext_name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
else if (plugin && specType == 'serial') {
|
||||||
if (awaitingSpecs['serial']) {
|
|
||||||
ext_name = awaitingSpecs['serial'];
|
ext_name = awaitingSpecs['serial'];
|
||||||
plugin.serial_list(function (deviceList) {
|
plugin.serial_list(function (deviceList) {
|
||||||
for (var i = 0; i < deviceList.length; i++) {
|
for (var i = 0; i < deviceList.length; i++) {
|
||||||
|
@ -225,15 +230,10 @@ window.ScratchExtensions = new (function () {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
else if (ScratchDeviceManager) {
|
||||||
|
ext_name = awaitingSpecs[specType];
|
||||||
|
ScratchDeviceManager.device_list(specType, ext_name, deviceSpecs[ext_name], deviceListCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ScratchDeviceManager && awaitingSpecs['wedo2']) {
|
|
||||||
ext_name = awaitingSpecs['wedo2'];
|
|
||||||
ScratchDeviceManager.wedo2_list(function(deviceList) {
|
|
||||||
for (var i = 0; i < deviceList.length; ++i) {
|
|
||||||
handlers[ext_name]._deviceConnected(new WeDo2Device(deviceList[i].id || deviceList[i], ext_name));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!shouldLookForDevices()) {
|
if (!shouldLookForDevices()) {
|
||||||
|
@ -241,6 +241,15 @@ window.ScratchExtensions = new (function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function deviceListCallback(deviceList, ext_type, ext_name) {
|
||||||
|
for (var i = 0; i < deviceList.length; ++i) {
|
||||||
|
var deviceConstructor = Devices[ext_type];
|
||||||
|
var deviceId = deviceList[i].id || deviceList[i];
|
||||||
|
var device = new deviceConstructor(deviceId, ext_type, ext_name);
|
||||||
|
handlers[ext_name]._deviceConnected(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function checkPolling() {
|
function checkPolling() {
|
||||||
if (poller || !shouldLookForDevices()) return;
|
if (poller || !shouldLookForDevices()) return;
|
||||||
|
|
||||||
|
@ -265,6 +274,7 @@ window.ScratchExtensions = new (function () {
|
||||||
function createDevicePlugin() {
|
function createDevicePlugin() {
|
||||||
if (plugin) return;
|
if (plugin) return;
|
||||||
|
|
||||||
|
try {
|
||||||
// TODO: delegate more of this to the other files
|
// TODO: delegate more of this to the other files
|
||||||
if (isOffline) {
|
if (isOffline) {
|
||||||
// Talk to the AIR Native Extension through the offline editor's plugin emulation.
|
// Talk to the AIR Native Extension through the offline editor's plugin emulation.
|
||||||
|
@ -287,6 +297,11 @@ window.ScratchExtensions = new (function () {
|
||||||
// Talk to the actual plugin, but make it pretend to be asynchronous.
|
// Talk to the actual plugin, but make it pretend to be asynchronous.
|
||||||
plugin = new window.ScratchPlugin.PluginWrapper(plugin);
|
plugin = new window.ScratchPlugin.PluginWrapper(plugin);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.error('Error creating plugin or wrapper:', e);
|
||||||
|
plugin = null;
|
||||||
|
}
|
||||||
|
|
||||||
// Wait a moment to access the plugin and claim any devices that plugins are
|
// Wait a moment to access the plugin and claim any devices that plugins are
|
||||||
// interested in.
|
// interested in.
|
||||||
|
@ -383,13 +398,70 @@ window.ScratchExtensions = new (function () {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: create a base class for these device classes so that we can share common code
|
function WeDo2Device(id, ext_type, ext_name) {
|
||||||
function WeDo2Device(id, ext_name) {
|
|
||||||
var dev = null;
|
var dev = null;
|
||||||
|
this.ext_type = ext_type;
|
||||||
|
this.ext_name = ext_name;
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
|
||||||
|
function RawWeDo2(deviceId, socket) {
|
||||||
|
var WeDo = this;
|
||||||
|
var eventHandlers = {};
|
||||||
|
|
||||||
|
WeDo.close = function() {
|
||||||
|
socket.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
WeDo.setMotorOn = function(motorIndex, power) {
|
||||||
|
socket.emit('motorOn', {motorIndex:motorIndex, power:power});
|
||||||
|
};
|
||||||
|
|
||||||
|
WeDo.setMotorOff = function(motorIndex) {
|
||||||
|
socket.emit('motorOff', {motorIndex:motorIndex});
|
||||||
|
};
|
||||||
|
|
||||||
|
WeDo.setMotorBrake = function(motorIndex) {
|
||||||
|
socket.emit('motorBrake', {motorIndex:motorIndex});
|
||||||
|
};
|
||||||
|
|
||||||
|
WeDo.setLED = function(rgb) {
|
||||||
|
socket.emit('setLED', {rgb:rgb});
|
||||||
|
};
|
||||||
|
|
||||||
|
WeDo.playTone = function(tone, durationMs) {
|
||||||
|
socket.emit('playTone', {tone:tone, ms:durationMs});
|
||||||
|
};
|
||||||
|
|
||||||
|
WeDo.stopTone = function() {
|
||||||
|
socket.emit('stopTone');
|
||||||
|
};
|
||||||
|
|
||||||
|
function setHandler(eventName, handler) {
|
||||||
|
if (eventHandlers.hasOwnProperty(eventName)) {
|
||||||
|
var oldHandler = eventHandlers[eventName];
|
||||||
|
if (oldHandler) {
|
||||||
|
socket.removeListener(eventName, oldHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (handler) {
|
||||||
|
socket.on(eventName, handler);
|
||||||
|
}
|
||||||
|
eventHandlers[eventName] = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
// function handler(event) { access event.sensorName and event.sensorValue }
|
||||||
|
WeDo.setSensorHandler = function (handler) {
|
||||||
|
setHandler('sensorChanged', handler);
|
||||||
|
};
|
||||||
|
|
||||||
|
WeDo.setDeviceWasClosedHandler = function (handler) {
|
||||||
|
// TODO: resolve this ambiguity
|
||||||
|
setHandler('disconnect', handler);
|
||||||
|
setHandler('deviceWasClosed', handler);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function disconnect() {
|
function disconnect() {
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
self.close();
|
self.close();
|
||||||
|
@ -397,16 +469,27 @@ window.ScratchExtensions = new (function () {
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.is_open = function() {
|
||||||
|
return !!dev;
|
||||||
|
};
|
||||||
|
|
||||||
this.open = function(readyCallback) {
|
this.open = function(readyCallback) {
|
||||||
ScratchDeviceManager.wedo2_open(self.id, function(d) {
|
ScratchDeviceManager.socket_open(self.ext_name, self.ext_type, self.id, function(socket) {
|
||||||
dev = d;
|
if (socket) {
|
||||||
if (dev) {
|
dev = new RawWeDo2(self.id, socket);
|
||||||
devices[ext_name] = self;
|
devices[ext_name] = self;
|
||||||
dev.setDeviceWasClosedHandler(disconnect);
|
dev.setDeviceWasClosedHandler(disconnect);
|
||||||
}
|
}
|
||||||
if (readyCallback) readyCallback(d ? self : null);
|
else {
|
||||||
|
dev = null;
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
if (readyCallback) {
|
||||||
|
readyCallback(dev ? self : null);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
this.close = function() {
|
this.close = function() {
|
||||||
if (!dev) return;
|
if (!dev) return;
|
||||||
dev.close();
|
dev.close();
|
||||||
|
@ -415,9 +498,6 @@ window.ScratchExtensions = new (function () {
|
||||||
|
|
||||||
checkPolling();
|
checkPolling();
|
||||||
};
|
};
|
||||||
this.is_open = function() {
|
|
||||||
return !!dev;
|
|
||||||
};
|
|
||||||
|
|
||||||
// The `handler` should be a function like: function handler(event) {...}
|
// The `handler` should be a function like: function handler(event) {...}
|
||||||
// The `event` will contain properties called `sensorName` and `sensorValue`.
|
// The `event` will contain properties called `sensorName` and `sensorValue`.
|
||||||
|
@ -452,4 +532,84 @@ window.ScratchExtensions = new (function () {
|
||||||
dev.stopTone();
|
dev.stopTone();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function BleDevice(id, ext_type, ext_name) {
|
||||||
|
var self = this;
|
||||||
|
this.ext_name = ext_name;
|
||||||
|
this.ext_type = ext_type;
|
||||||
|
this.socket = null;
|
||||||
|
|
||||||
|
this.id = id;
|
||||||
|
|
||||||
|
var onActions = [];
|
||||||
|
var onceActions = [];
|
||||||
|
|
||||||
|
function disconnect() {
|
||||||
|
setTimeout(function () {
|
||||||
|
self.close();
|
||||||
|
handlers[self.ext_name]._deviceRemoved(self);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emit = function(action, data){
|
||||||
|
if(self.socket){
|
||||||
|
self.socket.emit(action, data);
|
||||||
|
}
|
||||||
|
return !!self.socket;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.on = function(action, callback){
|
||||||
|
if(self.is_open()){
|
||||||
|
self.socket.on(action, callback);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
onActions.push([action, callback]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.once = function(action, callback){
|
||||||
|
if(self.is_open()){
|
||||||
|
self.socket.once(action, callback);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
onceActions.push([action, callback]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.open = function(readyCallback) {
|
||||||
|
ScratchDeviceManager.socket_open(self.ext_name, ext_type, self.id, function(s) {
|
||||||
|
self.socket = s;
|
||||||
|
if (self.socket) {
|
||||||
|
devices[self.ext_name] = self;
|
||||||
|
onActions.forEach(function(element){
|
||||||
|
self.socket.on(element[0], element[1]);
|
||||||
|
});
|
||||||
|
onceActions.forEach(function(element){
|
||||||
|
self.socket.once(element[0], element[1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
self.socket.on('disconnect', disconnect);
|
||||||
|
self.socket.on('deviceWasClosed', disconnect);
|
||||||
|
|
||||||
|
}
|
||||||
|
if (readyCallback) readyCallback(self.socket ? self : null);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.close = function() {
|
||||||
|
if (!self.socket) return;
|
||||||
|
self.socket.close();
|
||||||
|
delete devices[self.ext_name];
|
||||||
|
self.socket = null;
|
||||||
|
|
||||||
|
checkPolling();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.is_open = function() {
|
||||||
|
return !!self.socket;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
Devices = {ble: BleDevice, wedo2: WeDo2Device};
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -7,7 +7,9 @@ window.ScratchDeviceHost = new (function () {
|
||||||
return isConnected;
|
return isConnected;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!window.chrome) return;
|
if (!(window.chrome && window.chrome.runtime && window.chrome.runtime.connect)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var extensionID = 'clmabinlolakdafkoajkfjjengcdmnpm';
|
var extensionID = 'clmabinlolakdafkoajkfjjengcdmnpm';
|
||||||
var callNumber = 0;
|
var callNumber = 0;
|
||||||
|
|
|
@ -148,6 +148,9 @@
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Each block must have a unique function name even if the implementation is identical.
|
||||||
|
ext.whenTilted = ext.isTilted;
|
||||||
|
|
||||||
ext.getTilt = function (tiltDir) {
|
ext.getTilt = function (tiltDir) {
|
||||||
var tiltValue;
|
var tiltValue;
|
||||||
switch(tiltDir) {
|
switch(tiltDir) {
|
||||||
|
@ -332,7 +335,7 @@
|
||||||
[' ', 'set light color to %n', 'setLED', 50],
|
[' ', 'set light color to %n', 'setLED', 50],
|
||||||
['w', 'play note %d.note for %n seconds', 'playNote', 60, 0.5],
|
['w', 'play note %d.note for %n seconds', 'playNote', 60, 0.5],
|
||||||
['h', 'when distance %m.lessMore %n', 'whenDistance', strings.COMP_LESS, 50],
|
['h', 'when distance %m.lessMore %n', 'whenDistance', strings.COMP_LESS, 50],
|
||||||
['h', 'when tilted %m.tiltDirAny', 'isTilted', strings.TILT_ANY],
|
['h', 'when tilted %m.tiltDirAny', 'whenTilted', strings.TILT_ANY],
|
||||||
['r', 'distance', 'getDistance'],
|
['r', 'distance', 'getDistance'],
|
||||||
['b', 'tilted %m.tiltDirAny ?', 'isTilted', strings.TILT_ANY],
|
['b', 'tilted %m.tiltDirAny ?', 'isTilted', strings.TILT_ANY],
|
||||||
['r', 'tilt angle %m.tiltDir', 'getTilt', strings.TILT_UP]
|
['r', 'tilt angle %m.tiltDir', 'getTilt', strings.TILT_UP]
|
||||||
|
|
Loading…
Reference in a new issue