Merge pull request #383 from pybricks/dlech

beta 8
This commit is contained in:
David Lechner
2021-04-02 16:13:45 -05:00
committed by GitHub
11 changed files with 153 additions and 23 deletions
+3 -5
View File
@@ -21,15 +21,13 @@ module.exports = {
'no-multi-spaces': 'error',
'no-trailing-spaces': 'error',
'no-multiple-empty-lines': 'error',
'@typescript-eslint/no-unused-vars': ["error", { argsIgnorePattern: "^_" }],
'no-unused-vars': ["error", { argsIgnorePattern: "^_" }],
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'sort-imports': ['error', { ignoreDeclarationSort: true }],
'import/order': ['error', { alphabetize: { order: 'asc' } }],
},
settings: {
react: { version: 'detect' },
},
ignorePatterns: [
"test/env.js"
],
ignorePatterns: ['test/env.js'],
};
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@pybricks/pybricks-code",
"version": "1.0.0-beta.7",
"version": "1.0.0-beta.8",
"license": "MIT",
"author": "The Pybricks Authors",
"repository": {
@@ -10,7 +10,7 @@
"dependencies": {
"@blueprintjs/core": "^3.41.0",
"@craco/craco": "^6.1.1",
"@pybricks/firmware": "4.7.0",
"@pybricks/firmware": "4.7.1",
"@pybricks/ide-docs": "1.1.1",
"@pybricks/mpy-cross-v5": "^2.0.0",
"@shopify/react-i18n": "^5.3.0",
+15
View File
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2021 The Pybricks Authors
//
// Pybricks uses the standard Device Info service.
// Refer to Device Information Service (DIS) at https://www.bluetooth.com/specifications/specs/
// and assigned numbers at https://www.bluetooth.com/specifications/assigned-numbers/
/** Device Information service UUID. */
export const serviceUUID = 0x180a;
/** Firmware Revision String characteristic UUID. */
export const firmwareRevisionStringUUID = 0x2a26;
/** Software Revision String characteristic UUID. */
export const softwareRevisionStringUUID = 0x2a28;
+87 -3
View File
@@ -15,6 +15,11 @@ import {
takeEvery,
takeMaybe,
} from 'typed-redux-saga/macro';
import {
serviceUUID as deviceInfoServiceUUID,
firmwareRevisionStringUUID,
softwareRevisionStringUUID,
} from '../ble-device-info-service/protocol';
import {
BlePybricksServiceActionType,
didFailToWriteCommand,
@@ -53,6 +58,8 @@ import {
TxCharUUID as uartTxCharUUID,
} from './protocol';
const decoder = new TextDecoder();
function disconnect(
server: BluetoothRemoteGATTServer,
_action: BleDeviceDisconnectAction,
@@ -109,7 +116,11 @@ function* connect(_action: BleDeviceConnectAction): Generator {
device = yield* call(() =>
navigator.bluetooth.requestDevice({
filters: [{ services: [pybricksServiceUUID] }],
optionalServices: [pybricksServiceUUID, uartServiceUUID],
optionalServices: [
pybricksServiceUUID,
deviceInfoServiceUUID,
uartServiceUUID,
],
}),
);
} catch (err) {
@@ -145,6 +156,79 @@ function* connect(_action: BleDeviceConnectAction): Generator {
yield* takeEvery(BLEDeviceActionType.Disconnect, disconnect, server);
let deviceInfoService: BluetoothRemoteGATTService;
try {
deviceInfoService = yield* call(
[server, 'getPrimaryService'],
deviceInfoServiceUUID,
);
} catch (err) {
server.disconnect();
yield* takeMaybe(disconnectChannel);
if (err instanceof DOMException && err.code === DOMException.NOT_FOUND_ERR) {
yield* put(didFailToConnect({ reason: Reason.NoDeviceInfoService }));
} else {
yield* put(didFailToConnect({ reason: Reason.Unknown, err }));
}
return;
}
let firmwareVersionChar: BluetoothRemoteGATTCharacteristic;
try {
firmwareVersionChar = yield* call(
[deviceInfoService, 'getCharacteristic'],
firmwareRevisionStringUUID,
);
} catch (err) {
server.disconnect();
yield* takeMaybe(disconnectChannel);
yield* put(didFailToConnect({ reason: Reason.Unknown, err }));
return;
}
let firmwareVersion: string;
try {
firmwareVersion = decoder.decode(
yield* call([firmwareVersionChar, 'readValue']),
);
} catch (err) {
server.disconnect();
yield* takeMaybe(disconnectChannel);
yield* put(didFailToConnect({ reason: Reason.Unknown, err }));
return;
}
// TODO: save firmware version for later use
console.log(`Hub firmware version: ${firmwareVersion}`);
let softwareVersionChar: BluetoothRemoteGATTCharacteristic;
try {
softwareVersionChar = yield* call(
[deviceInfoService, 'getCharacteristic'],
softwareRevisionStringUUID,
);
} catch (err) {
server.disconnect();
yield* takeMaybe(disconnectChannel);
yield* put(didFailToConnect({ reason: Reason.Unknown, err }));
return;
}
let protocolVersion: string;
try {
protocolVersion = decoder.decode(
yield* call([softwareVersionChar, 'readValue']),
);
} catch (err) {
server.disconnect();
yield* takeMaybe(disconnectChannel);
yield* put(didFailToConnect({ reason: Reason.Unknown, err }));
return;
}
// TODO: verify that minimum protocol version is met
console.log(`Pybricks protocol version: ${protocolVersion}`);
let pybricksService: BluetoothRemoteGATTService;
try {
pybricksService = yield* call(
@@ -155,7 +239,7 @@ function* connect(_action: BleDeviceConnectAction): Generator {
server.disconnect();
yield* takeMaybe(disconnectChannel);
if (err instanceof DOMException && err.code === DOMException.NOT_FOUND_ERR) {
yield* put(didFailToConnect({ reason: Reason.NoService }));
yield* put(didFailToConnect({ reason: Reason.NoPybricksService }));
} else {
yield* put(didFailToConnect({ reason: Reason.Unknown, err }));
}
@@ -231,7 +315,7 @@ function* connect(_action: BleDeviceConnectAction): Generator {
server.disconnect();
yield* takeMaybe(disconnectChannel);
if (err instanceof DOMException && err.code === DOMException.NOT_FOUND_ERR) {
yield* put(didFailToConnect({ reason: Reason.NoService }));
yield* put(didFailToConnect({ reason: Reason.NoPybricksService }));
} else {
yield* put(didFailToConnect({ reason: Reason.Unknown, err }));
}
+7 -3
View File
@@ -58,7 +58,8 @@ export enum BleDeviceFailToConnectReasonType {
NoBluetooth = 'ble.device.didFailToConnect.noBluetooth',
Canceled = 'ble.device.didFailToConnect.canceled',
NoGatt = 'ble.device.didFailToConnect.noGatt',
NoService = 'ble.device.didFailToConnect.noService',
NoDeviceInfoService = 'ble.device.didFailToConnect.noDeviceInfoService',
NoPybricksService = 'ble.device.didFailToConnect.noPybricksService',
Unknown = 'ble.device.didFailToConnect.unknown',
}
@@ -74,7 +75,9 @@ export type BleDeviceFailToConnectCanceledReason = Reason<BleDeviceFailToConnect
export type BleDeviceFailToConnectNoGattReason = Reason<BleDeviceFailToConnectReasonType.NoGatt>;
export type BleDeviceFailToConnectNoServiceReason = Reason<BleDeviceFailToConnectReasonType.NoService>;
export type BleDeviceFailToConnectNoDeviceInfoServiceReason = Reason<BleDeviceFailToConnectReasonType.NoDeviceInfoService>;
export type BleDeviceFailToConnectNoPybricksServiceReason = Reason<BleDeviceFailToConnectReasonType.NoPybricksService>;
export type BleDeviceFailToConnectUnknownReason = Reason<BleDeviceFailToConnectReasonType.Unknown> & {
err: Error;
@@ -85,7 +88,8 @@ export type BleDeviceDidFailToConnectReason =
| BleDeviceFailToConnectNoBluetoothReason
| BleDeviceFailToConnectCanceledReason
| BleDeviceFailToConnectNoGattReason
| BleDeviceFailToConnectNoServiceReason
| BleDeviceFailToConnectNoDeviceInfoServiceReason
| BleDeviceFailToConnectNoPybricksServiceReason
| BleDeviceFailToConnectUnknownReason;
export type BleDeviceDidFailToConnectAction = Action<BleDeviceActionType.DidFailToConnect> &
+1
View File
@@ -37,6 +37,7 @@ body {
.#{$ns}-toast {
user-select: text;
max-width: 700px;
}
.#{$ns}-button {
+16 -1
View File
@@ -20,5 +20,20 @@ export default function Notification(props: OwnProps): JSX.Element {
fallback: en,
});
const { messageId, replacements } = props;
return <>{i18n.translate(messageId, replacements)}</>;
let message = i18n.translate(messageId, replacements) as
| React.ReactElement
| string;
// Use newline characters to create paragraphs
if (typeof message === 'string') {
message = (
<>
{message.split('\n').map((x, i) => (
<p key={i}>{x}</p>
))}
</>
);
}
return message;
}
+2 -2
View File
@@ -6,14 +6,14 @@
},
"ble": {
"gattPermission": "The web browser did not give permission to use Bluetooth Low Energy",
"gattServiceNotFound": "Connected to hub but failed to get {serviceName} service. Try removing the \"{hubName}\" device in your OS Bluetooth settings, then try again.",
"gattServiceNotFound": "Connected to hub but failed to get {serviceName} service.\nEnsure that you are using the most recent firmware.\nIf the problem persists, try removing the \"{hubName}\" device in your OS Bluetooth settings, then try connecting again.",
"noWebBluetooth": "This web browser does not support Web Bluetooth or it is not enabled.",
"noBluetooth": "No Bluetooth adapter could be found. Bluetooth won't work.",
"unexpectedError": "Unexpected error while trying to connect: {errorMessage}"
},
"editor": {
"programChanged": {
"message": "The program was changed in another window. Do you want to delete this program and replace it with the new program?",
"message": "The program was changed in another window.\nDo you want to delete this program and replace it with the new program?",
"action": "Reload"
}
},
+4 -1
View File
@@ -31,7 +31,10 @@ test.each([
bleDidFailToConnect({ reason: BleDeviceFailToConnectReasonType.NoWebBluetooth }),
bleDidFailToConnect({ reason: BleDeviceFailToConnectReasonType.NoBluetooth }),
bleDidFailToConnect({ reason: BleDeviceFailToConnectReasonType.NoGatt }),
bleDidFailToConnect({ reason: BleDeviceFailToConnectReasonType.NoService }),
bleDidFailToConnect({
reason: BleDeviceFailToConnectReasonType.NoDeviceInfoService,
}),
bleDidFailToConnect({ reason: BleDeviceFailToConnectReasonType.NoPybricksService }),
bleDidFailToConnect({
reason: BleDeviceFailToConnectReasonType.Unknown,
err: { name: 'test', message: 'unknown' },
+12 -2
View File
@@ -168,12 +168,18 @@ function* showBleDeviceDidFailToConnectError(
yield* showSingleton(Level.Error, MessageId.BleGattPermission);
break;
case BleDeviceFailToConnectReasonType.NoService:
case BleDeviceFailToConnectReasonType.NoPybricksService:
yield* showSingleton(Level.Error, MessageId.BleGattServiceNotFound, {
serviceName: 'Pybricks',
hubName: 'Pybricks Hub',
});
break;
case BleDeviceFailToConnectReasonType.NoDeviceInfoService:
yield* showSingleton(Level.Error, MessageId.BleGattServiceNotFound, {
serviceName: 'Device Information',
hubName: 'Pybricks Hub',
});
break;
case BleDeviceFailToConnectReasonType.NoBluetooth:
yield* showSingleton(Level.Error, MessageId.BleNoBluetooth);
break;
@@ -310,7 +316,11 @@ function* dismissCompilerError(): Generator {
function* showCompilerError(action: MpyDidFailToCompileAction): Generator {
yield* showSingleton(Level.Error, MessageId.MpyError, {
errorMessage: React.createElement('pre', undefined, action.err.join('\n')),
errorMessage: React.createElement(
'pre',
{ style: { whiteSpace: 'pre-wrap', wordBreak: 'keep-all' } },
action.err.join('\n'),
),
});
}
+4 -4
View File
@@ -1500,10 +1500,10 @@
schema-utils "^2.6.5"
source-map "^0.7.3"
"@pybricks/firmware@4.7.0":
version "4.7.0"
resolved "https://registry.yarnpkg.com/@pybricks/firmware/-/firmware-4.7.0.tgz#5b3cdfa009dc65ac7d3e3ccd769ab74143a1bda6"
integrity sha512-Ln5puxxiEiWOv39AFja4Rr7OeAhxe/yJnWFI0IJXVnAPqFR9FIrME8rDZhPXyw5yIT76MMUuv6B21IJmA9WURw==
"@pybricks/firmware@4.7.1":
version "4.7.1"
resolved "https://registry.yarnpkg.com/@pybricks/firmware/-/firmware-4.7.1.tgz#2d800bfc9ce2096e6790a43393a5d0f8a795c94f"
integrity sha512-nsYNhN2vOkclujsmeXyaziFz3xoCeLElUm81lQahxCCFl+bYaqQtXqhXqQ/pkYTWq+5bcd5cG8u/Y49jpeZ57A==
dependencies:
jszip "^3.5.0"