/// /** * Async TypeScript MicroPython interface (for serial and network connections, REPL & WebREPL) * * - License: MIT * - Repository: https://github.com/metachris/micropython-ctl * - Author: chris@linuxuser.at / https://twitter.com/metachris */ import WebSocket from 'isomorphic-ws'; import { Buffer } from 'buffer/'; import { InvalidPassword, CouldNotConnect, ScriptExecutionError } from './errors'; import { WEBSERVER_PORT } from './settings'; export { InvalidPassword, CouldNotConnect, ScriptExecutionError, WEBSERVER_PORT }; export declare enum ConnectionMode { SERIAL = "SERIAL", NETWORK = "NETWORK", PROXY = "PROXY" } export declare enum ConnectionState { CONNECTING = "CONNECTING", OPEN = "OPEN", CLOSED = "CLOSED" } export declare enum ReplMode { TERMINAL = "TERMINAL", SCRIPT_RAW_MODE = "SCRIPT_RAW_MODE", GETVER_WAITING_RESPONSE = "GETVER_WAITING_RESPONSE", PUTFILE_WAITING_FIRST_RESPONSE = "PUTFILE_WAITING_FIRST_RESPONSE", PUTFILE_WAITING_FINAL_RESPONSE = "PUTFILE_WAITING_FINAL_RESPONSE" } export declare enum RawReplState { WAITING_FOR_SCRIPT = "WAITING_FOR_SCRIPT", SCRIPT_SENT = "SCRIPT_SENT", SCRIPT_RECEIVING_RESPONSE = "SCRIPT_RECEIVING_RESPONSE", SCRIPT_EXECUTED = "SCRIPT_EXECUTED" } declare enum RawReplReceivingResponseSubState { SCRIPT_RECEIVING_OUTPUT = "SCRIPT_RECEIVING_OUTPUT", SCRIPT_RECEIVING_ERROR = "SCRIPT_RECEIVING_ERROR", SCRIPT_WAITING_FOR_END = "SCRIPT_WAITING_FOR_END" } declare type promiseResolve = (value: string | PromiseLike) => void; declare type promiseReject = (reason: any) => void; export interface DeviceState { connectionMode: ConnectionMode; connectionPath: string | null; port: any; ws: WebSocket | null; wsConnectTimeout: NodeJS.Timeout | undefined; wsConnectTimeoutTriggered: boolean; connectionState: ConnectionState; replMode: ReplMode; replPassword: string; replPromise: Promise | null; replPromiseResolve: promiseResolve | null; replPromiseReject: promiseReject | null; rawReplState: RawReplState; lastCommand: string; inputBuffer: string; errorBuffer: string; broadcastCommandOutputAsTerminalData: boolean; dataRawBuffer: Buffer; isReadingUntil: boolean; readUntilData: Buffer; readUntilBuffer: Buffer; readUntilTimeout: any; readUntilPromise: Promise | null; readUntilPromiseResolve: promiseResolve | null; readUntilPromiseReject: promiseReject | null; lastRunScriptTimeNeeded: number; receivingResponseSubState: RawReplReceivingResponseSubState; putFileSize: number; putFileData: Uint8Array; putFileName: string; putFileDest: string; } export interface FileListEntry { filename: string; isDir: boolean; size: number; mTime: number; sha256?: string; } /** * Main class for a MicroPython device connection. * * See also https://github.com/metachris/micropython-ctl * * ``` * const micropython = new MicroPythonDevice() * * // Connect to micropython device over network * await micropython.connectNetwork('DEVICE_IP', 'WEBREPL_PASSWORD') * * // Or connect to micropython device over serial interface * await micropython.connectSerial('/dev/ttyUSB0') * * // Run a Python script and capture the output * const output = await micropython.runScript('import os; print(os.listdir())') * * // List all files in the root * const files = await micropython.listFiles() * ``` */ export declare class MicroPythonDevice { private state; /** * Callback that is triggered when the connection is lost or closed. * * ```typescript * micropython.onclose = () => console.log('connection closed') * ``` */ onclose: () => void; /** * Callback to receive terminal (REPL) data * * ```typescript * micropython.onTerminalData = (data) => process.stdout.write(data) * ``` */ onTerminalData: (data: string) => void; constructor(); /** * Whether currently connected to a device. */ isConnected(): boolean; isSerialDevice(): boolean; isProxyConnection(): boolean; isTerminalMode(): boolean; /** * Get the state object. Mostly used for debugging purposes. */ getState(): DeviceState; private createReplPromise; /** The internal webserver is used to proxy runScript commands over an existing connection */ startInternalWebserver(): Promise; private connectProxy; /** * Connect to a device over the serial interface * * @param path Serial interface (eg. `/dev/ttyUSB0`, `/dev/tty.SLAB_USBtoUART`, ...) * @throws {CouldNotConnect} Connection failed */ connectSerial(path: string): Promise; /** * Connect to a device over the network (requires enabled WebREPL) * * @param host IP address or hostname * @param password webrepl password * @param timeoutSec Connection timeout (default: 5 sec). To disable, set to 0 * @throws {CouldNotConnect} Connection failed */ connectNetwork(host: string, password: string, timeoutSec?: number): Promise; /** * Handle special WebREPL only commands data * * getver, putfile, getfile */ private handlProtocolSpecialCommandsOutput; private handleWebsocketMessage; private clearBuffer; /** * Returns a promise that is resolved if `data` is received within `timeout` seconds, * otherwise rejected */ private readUntil; /** * Handle incoming data */ private handleProtocolData; sendData(data: string | Buffer | ArrayBuffer): void; private serialSendData; private wsSendData; disconnect(): Promise; private closeWebsocket; /** * Execute a Python script on the device, wait and return the output. * * @param script the python code * @param options * * @throws {ScriptExecutionError} on Python code execution error. Includes the Python traceback. */ runScript(script: string, options?: RunScriptOptions): Promise; private enterRawRepl; private exitRawRepl; /** * GET_VER webrepl command. Returns the micropython version. * Only works with network connections, not with serial. */ getVer(): Promise; listFiles(directory?: string, options?: ListFilesOptions): Promise; /** * Get the contents of a file. * * ```typescript * const data = await micropython.getFile('boot.py') * ``` * * @returns {Buffer} contents of the file in a Buffer * @param filename filename of file to download * @throws {ScriptExecutionError} if not found: "`OSError: [Errno 2] ENOENT`" */ getFile(filename: string): Promise; statPath(path: string): Promise<{ exists: boolean; isDir: boolean; size: number; }>; /** * * @param name * @throws {ScriptExecutionError} */ mkdir(name: string): Promise; /** * Uploading data to the device, saving as a file. * * We break the buffer into multiple chunks, and execute a raw repl command for each of them * in order to not fill up the device RAM. * * See also: * - https://github.com/dhylands/rshell/blob/master/rshell/main.py#L1079 * - https://github.com/scientifichackers/ampy/blob/master/ampy/files.py#L209 * * @param targetFilename * @param data */ putFile(targetFilename: string, data: Buffer, options?: PutFileOptions): Promise; /** * Remove a file or directory. Optional recursively * * @param path * @param recursive default: false * * @throws {ScriptExecutionError} if not found: "OSError: [Errno 2] ENOENT" * @throws {ScriptExecutionError} if directory not empty: "OSError: 39" */ remove(path: string, recursive?: boolean): Promise; /** * Rename a file or directory (uos.rename) * * @throws {ScriptExecutionError} if not found: "OSError: [Errno 2] ENOENT" * @throws {ScriptExecutionError} if directory not empty: "OSError: 39" */ rename(oldPath: string, newPath: string): Promise; /** * Reset a device. */ reset(options?: ResetOptions): Promise; /** * Get SHA256 hash of a file contents. * * ```typescript * const data = await micropython.getFileHash('boot.py') * ``` * * @param filename filename of target file * @returns sha256 hash, hexlified * @throws {ScriptExecutionError} if not found: "`OSError: [Errno 2] ENOENT`" */ getFileHash(filename: string): Promise; /** * Check whether a file is the same as provided, within a single `runScript` execution. * Does not work in browser. * * - If filesize is different, then file is different * - If filesize equal then compare sha256 hash * * This is a helper for bulk uploading directories, but only if they have changed. * * @throws {ScriptExecutionError} if not found: "OSError: [Errno 2] ENOENT" */ isFileTheSame(filename: string, data: Buffer): Promise; /** * Get information about the board. * * ```typescript * const boardInfo = await micropython.getBoardInfo() * console.log(boardInfo) * * { * sysname: 'esp32', * nodename: 'esp32', * release: '1.13.0', * version: 'v1.13 on 2020-09-02', * machine: 'ESP32 module with ESP32', * uniqueId: 'c44f3312f529', * memFree: 108736, * fsBlockSize: 4096, * fsBlocksTotal: 512, * fsBlocksFree: 438 * } * ``` */ getBoardInfo(): Promise; gcCollect(): Promise; } export interface ListFilesOptions { recursive?: boolean; includeSha256?: boolean; } export interface RunScriptOptions { /** Whether unnecessary indentation should be kept in the script (default: `false`) */ disableDedent?: boolean; /** Whether to stay in RAW repl mode after execution. Useful for large scripts that need to get executed piece by piece (uploading files, etc.) (default: `false`) */ stayInRawRepl?: boolean; /** Whether to broadcast the received data to the {@linkCode MicroPythonDevice.onTerminalData} callback while receiving (default: `false`) */ broadcastOutputAsTerminalData?: boolean; /** Whether to resolve the promise before waiting for the result (default: `false`) */ resolveBeforeResult?: boolean; runGcCollectBeforeCommand?: boolean; } export interface ResetOptions { softReset?: boolean; broadcastOutputAsTerminalData?: boolean; } export interface PutFileOptions { checkIfSimilarBeforeUpload?: boolean; } export interface BoardInfo { sysname: string; nodename: string; release: string; version: string; machine: string; uniqueId: string; memFree: number; fsBlockSize: number; fsBlocksTotal: number; fsBlocksFree: number; }