mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-12 01:23:52 +00:00
ble-device-info-service: add actions
This adds actions that are triggered when the device info service characteristics are read.
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
|
||||
import { Dispatch as ReduxDispatch } from 'redux';
|
||||
import { AppAction } from './app/actions';
|
||||
import { BleDIServiceAction } from './ble-device-info-service/actions';
|
||||
import {
|
||||
BlePybricksServiceAction,
|
||||
BlePybricksServiceCommandAction,
|
||||
@@ -34,6 +35,7 @@ export type Action =
|
||||
| AppAction
|
||||
| BLEAction
|
||||
| BLEConnectAction
|
||||
| BleDIServiceAction
|
||||
| BlePybricksServiceAction
|
||||
| BlePybricksServiceCommandAction
|
||||
| BlePybricksServiceEventAction
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2021 The Pybricks Authors
|
||||
|
||||
import { Action } from 'redux';
|
||||
import { PnpId } from './protocol';
|
||||
|
||||
export enum BleDIServiceActionType {
|
||||
DidReceiveFirmwareRevision = 'action.bleDIService.didReceiveFirmwareRevision',
|
||||
DidReceiveSoftwareRevision = 'action.bleDIService.didReceiveSoftwareRevision',
|
||||
DidReceivePnPId = 'action.bleDIService.didReceivePnPId',
|
||||
}
|
||||
|
||||
/** Action that indicates the firmware revision characteristic was read. */
|
||||
export type BleDIServiceDidReceiveFirmwareRevisionAction =
|
||||
Action<BleDIServiceActionType.DidReceiveFirmwareRevision> & {
|
||||
version: string;
|
||||
};
|
||||
|
||||
/** Action that indicates the firmware revision characteristic was read. */
|
||||
export function bleDIServiceDidReceiveFirmwareRevision(
|
||||
version: string,
|
||||
): BleDIServiceDidReceiveFirmwareRevisionAction {
|
||||
return { type: BleDIServiceActionType.DidReceiveFirmwareRevision, version };
|
||||
}
|
||||
|
||||
/** Action that indicates the software revision characteristic was read. */
|
||||
export type BleDIServiceDidReceiveSoftwareRevisionAction =
|
||||
Action<BleDIServiceActionType.DidReceiveSoftwareRevision> & {
|
||||
version: string;
|
||||
};
|
||||
|
||||
/** Action that indicates the software revision characteristic was read. */
|
||||
export function bleDIServiceDidReceiveSoftwareRevision(
|
||||
version: string,
|
||||
): BleDIServiceDidReceiveSoftwareRevisionAction {
|
||||
return { type: BleDIServiceActionType.DidReceiveSoftwareRevision, version };
|
||||
}
|
||||
|
||||
/** Action that indicates the PnP ID characteristic was read. */
|
||||
export type BleDIServiceDidReceivePnPIdAction =
|
||||
Action<BleDIServiceActionType.DidReceivePnPId> & {
|
||||
pnpId: PnpId;
|
||||
};
|
||||
|
||||
/** Action that indicates the PnP ID characteristic was read. */
|
||||
export function bleDIServiceDidReceivePnPId(
|
||||
pnpId: PnpId,
|
||||
): BleDIServiceDidReceivePnPIdAction {
|
||||
return { type: BleDIServiceActionType.DidReceivePnPId, pnpId };
|
||||
}
|
||||
|
||||
/** Common type for all device info service actions. */
|
||||
export type BleDIServiceAction =
|
||||
| BleDIServiceDidReceiveFirmwareRevisionAction
|
||||
| BleDIServiceDidReceiveSoftwareRevisionAction
|
||||
| BleDIServiceDidReceivePnPIdAction;
|
||||
+13
-23
@@ -16,7 +16,11 @@ import {
|
||||
takeMaybe,
|
||||
} from 'typed-redux-saga/macro';
|
||||
import {
|
||||
PnpId,
|
||||
bleDIServiceDidReceiveFirmwareRevision,
|
||||
bleDIServiceDidReceivePnPId,
|
||||
bleDIServiceDidReceiveSoftwareRevision,
|
||||
} from '../ble-device-info-service/actions';
|
||||
import {
|
||||
decodePnpId,
|
||||
serviceUUID as deviceInfoServiceUUID,
|
||||
firmwareRevisionStringUUID,
|
||||
@@ -48,7 +52,7 @@ import {
|
||||
} from '../ble/actions';
|
||||
import { BleConnectionState } from '../ble/reducers';
|
||||
import { RootState } from '../reducers';
|
||||
import { ensureError, hex } from '../utils';
|
||||
import { ensureError } from '../utils';
|
||||
import {
|
||||
BleUartActionType,
|
||||
BleUartWriteAction,
|
||||
@@ -194,11 +198,9 @@ function* connect(_action: BleDeviceConnectAction): Generator {
|
||||
return;
|
||||
}
|
||||
|
||||
let firmwareVersion: string;
|
||||
try {
|
||||
firmwareVersion = decoder.decode(
|
||||
yield* call([firmwareVersionChar, 'readValue']),
|
||||
);
|
||||
const version = decoder.decode(yield* call([firmwareVersionChar, 'readValue']));
|
||||
yield* put(bleDIServiceDidReceiveFirmwareRevision(version));
|
||||
} catch (err) {
|
||||
server.disconnect();
|
||||
yield* takeMaybe(disconnectChannel);
|
||||
@@ -219,11 +221,9 @@ function* connect(_action: BleDeviceConnectAction): Generator {
|
||||
return;
|
||||
}
|
||||
|
||||
let protocolVersion: string;
|
||||
try {
|
||||
protocolVersion = decoder.decode(
|
||||
yield* call([softwareVersionChar, 'readValue']),
|
||||
);
|
||||
const version = decoder.decode(yield* call([softwareVersionChar, 'readValue']));
|
||||
yield* put(bleDIServiceDidReceiveSoftwareRevision(version));
|
||||
} catch (err) {
|
||||
server.disconnect();
|
||||
yield* takeMaybe(disconnectChannel);
|
||||
@@ -231,9 +231,6 @@ function* connect(_action: BleDeviceConnectAction): Generator {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: verify that minimum protocol version is met
|
||||
console.log(`Pybricks protocol version: ${protocolVersion}`);
|
||||
|
||||
let pnpIdChar: BluetoothRemoteGATTCharacteristic | undefined = undefined;
|
||||
try {
|
||||
pnpIdChar = yield* call([deviceInfoService, 'getCharacteristic'], pnpIdUUID);
|
||||
@@ -244,9 +241,9 @@ function* connect(_action: BleDeviceConnectAction): Generator {
|
||||
}
|
||||
|
||||
if (pnpIdChar) {
|
||||
let pnpId: PnpId;
|
||||
try {
|
||||
pnpId = decodePnpId(yield* call([pnpIdChar, 'readValue']));
|
||||
const pnpId = decodePnpId(yield* call([pnpIdChar, 'readValue']));
|
||||
yield* put(bleDIServiceDidReceivePnPId(pnpId));
|
||||
} catch (err) {
|
||||
server.disconnect();
|
||||
yield* takeMaybe(disconnectChannel);
|
||||
@@ -255,13 +252,6 @@ function* connect(_action: BleDeviceConnectAction): Generator {
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Vendor: ${hex(pnpId.vendorId, 4)}, Product: ${hex(
|
||||
pnpId.productId,
|
||||
4,
|
||||
)}, Version: ${hex(pnpId.productVersion, 4)}`,
|
||||
);
|
||||
}
|
||||
|
||||
let pybricksService: BluetoothRemoteGATTService;
|
||||
@@ -419,7 +409,7 @@ function* connect(_action: BleDeviceConnectAction): Generator {
|
||||
|
||||
tasks.push(yield* takeEvery(BleUartActionType.Write, writeUart, uartRxChar));
|
||||
|
||||
yield* put(didConnect(firmwareVersion));
|
||||
yield* put(didConnect());
|
||||
|
||||
// wait for disconnection
|
||||
yield* takeMaybe(disconnectChannel);
|
||||
|
||||
+3
-6
@@ -44,16 +44,13 @@ export function connect(): BleDeviceConnectAction {
|
||||
return { type: BleDeviceActionType.Connect };
|
||||
}
|
||||
|
||||
export type BleDeviceDidConnectAction = Action<BleDeviceActionType.DidConnect> & {
|
||||
firmwareVersion: string;
|
||||
};
|
||||
export type BleDeviceDidConnectAction = Action<BleDeviceActionType.DidConnect>;
|
||||
|
||||
/**
|
||||
* Creates an action that indicates a device was connected.
|
||||
* @param firmwareVersion The firmware version of the hub (e.g. 3.0.0a1)
|
||||
*/
|
||||
export function didConnect(firmwareVersion: string): BleDeviceDidConnectAction {
|
||||
return { type: BleDeviceActionType.DidConnect, firmwareVersion };
|
||||
export function didConnect(): BleDeviceDidConnectAction {
|
||||
return { type: BleDeviceActionType.DidConnect };
|
||||
}
|
||||
|
||||
export enum BleDeviceFailToConnectReasonType {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2021 The Pybricks Authors
|
||||
|
||||
import { firmwareVersion } from '@pybricks/firmware';
|
||||
import { Action } from '../actions';
|
||||
import {
|
||||
BleDeviceDidFailToConnectReason,
|
||||
@@ -30,10 +29,8 @@ test('connection', () => {
|
||||
.connection,
|
||||
).toBe(BleConnectionState.Connecting);
|
||||
expect(
|
||||
reducers(
|
||||
{ connection: BleConnectionState.Connecting } as State,
|
||||
didConnect(firmwareVersion),
|
||||
).connection,
|
||||
reducers({ connection: BleConnectionState.Connecting } as State, didConnect())
|
||||
.connection,
|
||||
).toBe(BleConnectionState.Connected);
|
||||
expect(
|
||||
reducers(
|
||||
|
||||
@@ -28,10 +28,8 @@ test('initial state', () => {
|
||||
describe('runtime', () => {
|
||||
test('', () => {
|
||||
expect(
|
||||
reducers(
|
||||
{ runtime: HubRuntimeState.Disconnected } as State,
|
||||
didConnect(firmwareVersion),
|
||||
).runtime,
|
||||
reducers({ runtime: HubRuntimeState.Disconnected } as State, didConnect())
|
||||
.runtime,
|
||||
).toBe(HubRuntimeState.Unknown);
|
||||
});
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@ import {
|
||||
import { AsyncSaga } from '../../test';
|
||||
import { Action } from '../actions';
|
||||
import { didCheckForUpdate } from '../app/actions';
|
||||
import { bleDIServiceDidReceiveFirmwareRevision } from '../ble-device-info-service/actions';
|
||||
import {
|
||||
BleDeviceFailToConnectReasonType,
|
||||
didFailToConnect as bleDidFailToConnect,
|
||||
didConnect,
|
||||
} from '../ble/actions';
|
||||
import { didFailToSaveAs, storageChanged } from '../editor/actions';
|
||||
import {
|
||||
@@ -81,7 +81,7 @@ test.each([
|
||||
didFailToFinish(FailToFinishReasonType.FirmwareSize),
|
||||
didFailToFinish(FailToFinishReasonType.Unknown, new Error('test error')),
|
||||
didCheckForUpdate(false),
|
||||
didConnect('3.0.0'),
|
||||
bleDIServiceDidReceiveFirmwareRevision('3.0.0'),
|
||||
didFailToSaveAs(new DOMException('test message', 'NotAllowedError')),
|
||||
])('actions that should show notification: %o', async (action: Action) => {
|
||||
const getToasts = jest.fn().mockReturnValue([]);
|
||||
@@ -113,7 +113,7 @@ test.each([
|
||||
didFailToFinish(FailToFinishReasonType.FailedToConnect),
|
||||
didSucceed({} as ServiceWorkerRegistration),
|
||||
didCheckForUpdate(true),
|
||||
didConnect(firmwareVersion),
|
||||
bleDIServiceDidReceiveFirmwareRevision(firmwareVersion),
|
||||
didFailToSaveAs(new DOMException('test message', 'AbortError')),
|
||||
])('actions that should not show a notification: %o', async (action: Action) => {
|
||||
const getToasts = jest.fn().mockReturnValue([]);
|
||||
|
||||
@@ -12,9 +12,12 @@ import * as semver from 'semver';
|
||||
import { delay, getContext, put, take, takeEvery } from 'typed-redux-saga/macro';
|
||||
import { AppActionType, AppDidCheckForUpdateAction, reload } from '../app/actions';
|
||||
import { appName } from '../app/constants';
|
||||
import {
|
||||
BleDIServiceActionType,
|
||||
BleDIServiceDidReceiveFirmwareRevisionAction,
|
||||
} from '../ble-device-info-service/actions';
|
||||
import {
|
||||
BleDeviceActionType,
|
||||
BleDeviceDidConnectAction,
|
||||
BleDeviceDidFailToConnectAction,
|
||||
BleDeviceFailToConnectReasonType,
|
||||
} from '../ble/actions';
|
||||
@@ -393,12 +396,14 @@ function* showNoUpdateInfo(action: AppDidCheckForUpdateAction): Generator {
|
||||
});
|
||||
}
|
||||
|
||||
function* checkVersion(action: BleDeviceDidConnectAction): Generator {
|
||||
function* checkVersion(
|
||||
action: BleDIServiceDidReceiveFirmwareRevisionAction,
|
||||
): Generator {
|
||||
// ensure the actual hub firmware version is the same as the shipped
|
||||
// firmware version or newer
|
||||
if (
|
||||
!semver.satisfies(
|
||||
pythonVersionToSemver(action.firmwareVersion),
|
||||
pythonVersionToSemver(action.version),
|
||||
`>=${pythonVersionToSemver(firmwareVersion)}`,
|
||||
)
|
||||
) {
|
||||
@@ -423,5 +428,5 @@ export default function* (): Generator {
|
||||
yield* takeEvery(NotificationActionType.Add, addNotification);
|
||||
yield* takeEvery(ServiceWorkerActionType.DidUpdate, showServiceWorkerUpdate);
|
||||
yield* takeEvery(AppActionType.DidCheckForUpdate, showNoUpdateInfo);
|
||||
yield* takeEvery(BleDeviceActionType.DidConnect, checkVersion);
|
||||
yield* takeEvery(BleDIServiceActionType.DidReceiveFirmwareRevision, checkVersion);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user