Files
pybricks-code/src/lwp3-bootloader/actions.ts
T
David Lechner e67ae1c52b use "error" when type is Error
This renames "err" to "error" when the type is Error for consistency
(fixes redux complaints about non-serializable err).
2022-10-17 13:44:21 -05:00

302 lines
7.8 KiB
TypeScript

// SPDX-License-Identifier: MIT
// Copyright (c) 2020-2022 The Pybricks Authors
import { Action } from 'redux';
import { createAction } from '../actions';
import { Command, HubType, ProtectionLevel, Result } from './protocol';
/**
* Initiate a connection.
*/
export const connect = createAction(() => ({
type: 'bootloader.action.connection.connect',
}));
/**
* The connection has been made.
*/
export const didConnect = createAction(() => ({
type: 'bootloader.action.connection.didConnect',
}));
/**
* Possible reasons a device could fail to connect.
*/
export enum BootloaderConnectionFailureReason {
/** Web Bluetooth is not available */
NoWebBluetooth = 'no-web-bluetooth',
/** Bluetooth is not available */
NoBluetooth = 'no-bluetooth',
/** Connected but failed to find the bootloader GATT service */
GattServiceNotFound = 'gatt-service-not-found',
/** The connection was canceled */
Canceled = 'canceled',
/** The reason is not known */
Unknown = 'unknown',
}
const didFailToConnectType = 'bootloader.action.connection.didFailToConnect';
function didFailToConnectCreator(
reason: BootloaderConnectionFailureReason.NoWebBluetooth,
): Action<typeof didFailToConnectType> & {
reason: BootloaderConnectionFailureReason.NoWebBluetooth;
};
function didFailToConnectCreator(
reason: BootloaderConnectionFailureReason.NoBluetooth,
): Action<typeof didFailToConnectType> & {
reason: BootloaderConnectionFailureReason.NoBluetooth;
};
function didFailToConnectCreator(
reason: BootloaderConnectionFailureReason.GattServiceNotFound,
): Action<typeof didFailToConnectType> & {
reason: BootloaderConnectionFailureReason.GattServiceNotFound;
};
function didFailToConnectCreator(
reason: BootloaderConnectionFailureReason.Canceled,
): Action<typeof didFailToConnectType> & {
reason: BootloaderConnectionFailureReason.Canceled;
};
function didFailToConnectCreator(
reason: BootloaderConnectionFailureReason.Unknown,
error: Error,
): Action<typeof didFailToConnectType> & {
reason: BootloaderConnectionFailureReason.Unknown;
error: Error;
};
function didFailToConnectCreator<T extends BootloaderConnectionFailureReason>(
reason: T,
arg1?: Error,
): Action<typeof didFailToConnectType> & {
reason: T;
err: T extends BootloaderConnectionFailureReason.Unknown ? Error : never;
};
function didFailToConnectCreator(
reason: BootloaderConnectionFailureReason,
arg1?: Error,
): Action<typeof didFailToConnectType> & {
reason: BootloaderConnectionFailureReason;
err?: Error;
} {
if (reason === BootloaderConnectionFailureReason.Unknown) {
return {
type: didFailToConnectType,
reason,
err: arg1,
};
}
return { type: didFailToConnectType, reason };
}
/**
* The connection was not successful.
*/
export const didFailToConnect = createAction(didFailToConnectCreator);
/**
* There was a connection error.
*/
export const didError = createAction((error: Error) => ({
type: 'bootloader.action.connection.didError',
error,
}));
/**
* Send a message using the connection.
*/
export const send = createAction((data: ArrayBuffer, withResponse = true) => ({
type: 'bootloader.action.connection.send',
data,
withResponse,
}));
/**
* Finished sending a message.
*/
export const didSend = createAction(() => ({
type: 'bootloader.action.connection.didSend',
}));
/**
* Sending a message failed with error.
*/
export const didFailToSend = createAction((error: Error) => ({
type: 'bootloader.action.connection.didFailToSend',
error,
}));
/**
* The connection received a message.
*/
export const didReceive = createAction((data: DataView) => ({
type: 'bootloader.action.connection.didReceive',
data,
}));
/**
* Initiate disconnection.
*/
export const disconnect = createAction(() => ({
type: 'bootloader.action.connection.disconnect',
}));
/**
* The connection has been closed.
*/
export const didDisconnect = createAction(() => ({
type: 'bootloader.action.connection.didDisconnect',
}));
/**
* Disconnecting failed.
*/
export const didFailToDisconnect = createAction(() => ({
type: 'bootloader.action.connection.didFailToDisconnect',
}));
// LWP3 bootloader request message actions
/**
* Creates a request to erase the flash memory.
*/
export const eraseRequest = createAction((id: number, isCityHub: boolean) => ({
type: 'bootloader.action.request.eraseRequest',
id,
isCityHub,
}));
/**
* Creates a request to program the flash memory.
* @param address The starting address in the flash memory.
* @param payload The bytes to write (max 14 bytes!)
*/
export const programRequest = createAction(
(id: number, address: number, payload: ArrayBuffer) => ({
type: 'bootloader.action.request.programRequest',
id,
address,
payload,
}),
);
/**
* Creates a request to reboot the hub.
*/
export const rebootRequest = createAction((id: number) => ({
type: 'bootloader.action.request.rebootRequest',
id,
}));
/**
* Creates a request to initialize the firmware flashing process.
* @param firmwareSize The size of the firmware to written to flash memory.
*/
export const initRequest = createAction((id: number, firmwareSize: number) => ({
type: 'bootloader.action.request.initRequest',
id,
firmwareSize,
}));
/**
* Creates a request to get information about the hub.
*/
export const infoRequest = createAction((id: number) => ({
type: 'bootloader.action.request.infoRequest',
id,
}));
/**
* Creates a request to get the checksum of the bytes that have been written
* to flash so far.
*/
export const checksumRequest = createAction((id: number) => ({
type: 'bootloader.action.request.checksumRequest',
id,
}));
/**
* Creates a request to get the bootloader flash memory protection state.
*/
export const stateRequest = createAction((id: number) => ({
type: 'bootloader.action.request.stateRequest',
id,
}));
/**
* Creates a request to disconnect the hub.
*/
export const disconnectRequest = createAction((id: number) => ({
type: 'bootloader.action.request.disconnectRequest',
id,
}));
/**
* Creates an action that indicates a request was sent.
* @param id The unique identifier of the action.
*/
export const didRequest = createAction((id: number) => ({
type: 'bootloader.action.didRequest',
id,
}));
/**
* Creates an action that indicates a request failed to send.
* @param id The unique identifier of the action.
* @param error The error message.
*/
export const didFailToRequest = createAction((id: number, error: Error) => ({
type: 'bootloader.action.didFailToRequest',
id,
error,
}));
// Bootloader response actions for receiving responses from the connection.
export const eraseResponse = createAction((result: Result) => ({
type: 'bootloader.action.response.eraseResponse',
result,
}));
export const programResponse = createAction((checksum: number, count: number) => ({
type: 'bootloader.action.response.programResponse',
checksum,
count,
}));
export const initResponse = createAction((result: Result) => ({
type: 'bootloader.action.response.initResponse',
result,
}));
export const infoResponse = createAction(
(version: number, startAddress: number, endAddress: number, hubType: HubType) => ({
type: 'bootloader.action.response.infoResponse',
version,
startAddress,
endAddress,
hubType,
}),
);
export const checksumResponse = createAction((checksum: number) => ({
type: 'bootloader.action.response.checksumResponse',
checksum,
}));
export const stateResponse = createAction((level: ProtectionLevel) => ({
type: 'bootloader.action.response.stateResponse',
level,
}));
export const errorResponse = createAction((command: Command) => ({
type: 'bootloader.action.response.errorResponse',
command,
}));