mirror of
https://github.com/crosspoint-reader/xteink-flasher.git
synced 2026-04-29 10:25:18 -07:00
Cleanup linting and repo
This commit is contained in:
@@ -41,3 +41,4 @@ yarn-error.log*
|
||||
next-env.d.ts
|
||||
|
||||
.idea
|
||||
.eslintcache
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
build
|
||||
out
|
||||
node_modules
|
||||
public
|
||||
.yarn
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"singleQuote": true
|
||||
}
|
||||
@@ -1,7 +1,3 @@
|
||||
compressionLevel: mixed
|
||||
|
||||
enableGlobalCache: false
|
||||
|
||||
nodeLinker: node-modules
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.11.0.cjs
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Dave Allie
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+85
-18
@@ -1,21 +1,88 @@
|
||||
import { defineConfig, globalIgnores } from "eslint/config";
|
||||
import nextVitals from "eslint-config-next/core-web-vitals";
|
||||
import nextTs from "eslint-config-next/typescript";
|
||||
import eslintConfigPrettier from "eslint-config-prettier/flat";
|
||||
/**
|
||||
* THIS FILE WAS AUTO-GENERATED.
|
||||
* PLEASE DO NOT EDIT IT MANUALLY.
|
||||
* ===============================
|
||||
* IF YOU'RE COPYING THIS INTO AN ESLINT CONFIG, REMOVE THIS COMMENT BLOCK.
|
||||
*/
|
||||
|
||||
const eslintConfig = defineConfig([
|
||||
...nextVitals,
|
||||
...nextTs,
|
||||
eslintConfigPrettier,
|
||||
import path from 'node:path';
|
||||
|
||||
// Override default ignores of eslint-config-next.
|
||||
globalIgnores([
|
||||
// Default ignores of eslint-config-next:
|
||||
".next/**",
|
||||
"out/**",
|
||||
"build/**",
|
||||
"next-env.d.ts",
|
||||
]),
|
||||
]);
|
||||
import { includeIgnoreFile } from '@eslint/compat';
|
||||
import js from '@eslint/js';
|
||||
import { configs, plugins } from 'eslint-config-airbnb-extended';
|
||||
import { rules as prettierConfigRules } from 'eslint-config-prettier';
|
||||
import prettierPlugin from 'eslint-plugin-prettier';
|
||||
|
||||
export default eslintConfig;
|
||||
const gitignorePath = path.resolve('.', '.gitignore');
|
||||
|
||||
const jsConfig = [
|
||||
// ESLint Recommended Rules
|
||||
{
|
||||
name: 'js/config',
|
||||
...js.configs.recommended,
|
||||
},
|
||||
// Stylistic Plugin
|
||||
plugins.stylistic,
|
||||
// Import X Plugin
|
||||
plugins.importX,
|
||||
// Airbnb Base Recommended Config
|
||||
...configs.base.recommended,
|
||||
];
|
||||
|
||||
const reactConfig = [
|
||||
// React Plugin
|
||||
plugins.react,
|
||||
// React Hooks Plugin
|
||||
plugins.reactHooks,
|
||||
// React JSX A11y Plugin
|
||||
plugins.reactA11y,
|
||||
// Airbnb React Recommended Config
|
||||
...configs.react.recommended,
|
||||
];
|
||||
|
||||
const typescriptConfig = [
|
||||
// TypeScript ESLint Plugin
|
||||
plugins.typescriptEslint,
|
||||
// Airbnb Base TypeScript Config
|
||||
...configs.base.typescript,
|
||||
// Airbnb React TypeScript Config
|
||||
...configs.react.typescript,
|
||||
];
|
||||
|
||||
const prettierConfig = [
|
||||
// Prettier Plugin
|
||||
{
|
||||
name: 'prettier/plugin/config',
|
||||
plugins: {
|
||||
prettier: prettierPlugin,
|
||||
},
|
||||
},
|
||||
// Prettier Config
|
||||
{
|
||||
name: 'prettier/config',
|
||||
rules: {
|
||||
...prettierConfigRules,
|
||||
'prettier/prettier': 'error',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default [
|
||||
// Ignore .gitignore files/folder in eslint
|
||||
includeIgnoreFile(gitignorePath),
|
||||
// Javascript Config
|
||||
...jsConfig,
|
||||
// React Config
|
||||
...reactConfig,
|
||||
// TypeScript Config
|
||||
...typescriptConfig,
|
||||
// Prettier Config
|
||||
...prettierConfig,
|
||||
// Overrides
|
||||
{
|
||||
rules: {
|
||||
'import-x/prefer-default-export': 'off',
|
||||
'react/require-default-props': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import type { NextConfig } from "next";
|
||||
import type { NextConfig } from 'next';
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
|
||||
+24
-10
@@ -3,27 +3,41 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"lint": "yarn eslint --max-warnings 0 --cache && yarn prettier --check . && yarn tsc",
|
||||
"lint:fix": "yarn eslint --max-warnings 0 --cache --fix && yarn prettier --write .",
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "eslint"
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"esptool-js": "^0.5.7",
|
||||
"next": "16.0.3",
|
||||
"react": "19.2.0",
|
||||
"react-dom": "19.2.0"
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/compat": "^2.0.0",
|
||||
"@eslint/js": "^9.39.1",
|
||||
"@stylistic/eslint-plugin": "^5.6.1",
|
||||
"@types/dom-serial": "^1.0.6",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "16.0.3",
|
||||
"@types/eslint-plugin-jsx-a11y": "^6.10.1",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/node": "^24.10.1",
|
||||
"@types/react": "^19.2.6",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"eslint": "^9.39.1",
|
||||
"eslint-config-airbnb-extended": "^2.3.2",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-import-resolver-typescript": "^4.4.4",
|
||||
"eslint-plugin-import-x": "^4.16.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||
"eslint-plugin-prettier": "^5.5.4",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks": "^7.0.1",
|
||||
"globals": "^16.5.0",
|
||||
"prettier": "^3.6.2",
|
||||
"typescript": "^5"
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.47.0"
|
||||
},
|
||||
"packageManager": "yarn@4.11.0"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": ["config:base"],
|
||||
"packageRules": [
|
||||
{
|
||||
"matchUpdateTypes": ["minor", "patch"],
|
||||
"matchCurrentVersion": "!/^0/",
|
||||
"automerge": true
|
||||
}
|
||||
],
|
||||
"postUpdateOptions": ["yarnDedupeHighest"]
|
||||
}
|
||||
+11
-11
@@ -1,14 +1,14 @@
|
||||
"use client";
|
||||
'use client';
|
||||
|
||||
import { ESPLoader, Transport } from "esptool-js";
|
||||
import { ESPLoader, Transport } from 'esptool-js';
|
||||
|
||||
export default class EspController {
|
||||
static async requestDevice() {
|
||||
if (!("serial" in navigator && navigator.serial)) {
|
||||
throw new Error("WebSerial is not supported in this browser");
|
||||
if (!('serial' in navigator && navigator.serial)) {
|
||||
throw new Error('WebSerial is not supported in this browser');
|
||||
}
|
||||
|
||||
return await navigator.serial.requestPort({
|
||||
return navigator.serial.requestPort({
|
||||
filters: [{ usbVendorId: 12346, usbProductId: 4097 }],
|
||||
});
|
||||
}
|
||||
@@ -58,7 +58,7 @@ export default class EspController {
|
||||
) {
|
||||
if (data.length !== 0x1000000) {
|
||||
throw new Error(
|
||||
`Data length must be 0x1000000, but got 0x${data.length.toString().padStart(7, "0")}`,
|
||||
`Data length must be 0x1000000, but got 0x${data.length.toString().padStart(7, '0')}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ export default class EspController {
|
||||
) {
|
||||
if (data.length !== 0x2000) {
|
||||
throw new Error(
|
||||
`Data length must be 0x2000, but got 0x${data.length.toString().padStart(4, "0")}`,
|
||||
`Data length must be 0x2000, but got 0x${data.length.toString().padStart(4, '0')}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ export default class EspController {
|
||||
) => void,
|
||||
) {
|
||||
const u8Array = new Uint8Array(0x2000);
|
||||
for (let i = 0; i < 0x2000; i++) {
|
||||
for (let i = 0; i < 0x2000; i += 1) {
|
||||
u8Array[i] = 255;
|
||||
}
|
||||
|
||||
@@ -133,9 +133,9 @@ export default class EspController {
|
||||
address,
|
||||
},
|
||||
],
|
||||
flashSize: "keep",
|
||||
flashMode: "keep",
|
||||
flashFreq: "keep",
|
||||
flashSize: 'keep',
|
||||
flashMode: 'keep',
|
||||
flashFreq: 'keep',
|
||||
|
||||
eraseAll: false,
|
||||
compress: true,
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
"use server";
|
||||
'use server';
|
||||
|
||||
const urls: Record<string, string> = {
|
||||
"3.0.8":
|
||||
"http://gotaserver.xteink.com/api/download/ESP32C3/V3.0.8/V3.0.8-EN.bin",
|
||||
'3.0.8':
|
||||
'http://gotaserver.xteink.com/api/download/ESP32C3/V3.0.8/V3.0.8-EN.bin',
|
||||
};
|
||||
|
||||
export async function getFirmware(version: string) {
|
||||
const url = urls[version];
|
||||
|
||||
if (!url) {
|
||||
throw new Error("Unknown firmware version");
|
||||
throw new Error('Unknown firmware version');
|
||||
}
|
||||
|
||||
const response = await fetch(url);
|
||||
|
||||
+10
-9
@@ -1,20 +1,21 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import React from 'react';
|
||||
import type { Metadata } from 'next';
|
||||
import { Geist, Geist_Mono } from 'next/font/google';
|
||||
import './globals.css';
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
subsets: ["latin"],
|
||||
variable: '--font-geist-sans',
|
||||
subsets: ['latin'],
|
||||
});
|
||||
|
||||
const geistMono = Geist_Mono({
|
||||
variable: "--font-geist-mono",
|
||||
subsets: ["latin"],
|
||||
variable: '--font-geist-mono',
|
||||
subsets: ['latin'],
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Xteink English Firmware Flasher",
|
||||
description: "Web based tool to help flash the Xteink device",
|
||||
title: 'Xteink English Firmware Flasher',
|
||||
description: 'Web based tool to help flash the Xteink device',
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
|
||||
+77
-75
@@ -1,11 +1,10 @@
|
||||
"use client";
|
||||
'use client';
|
||||
|
||||
import { useRef, useState } from "react";
|
||||
import EspController from "@/app/EspController";
|
||||
import Step from "@/components/Step/Step";
|
||||
|
||||
import styles from "./page.module.css";
|
||||
import { getFirmware } from "@/app/firmwareFetcher";
|
||||
import React, { useRef, useState } from 'react';
|
||||
import EspController from '@/app/EspController';
|
||||
import Step from '@/components/Step/Step';
|
||||
import { getFirmware } from '@/app/firmwareFetcher';
|
||||
import styles from './page.module.css';
|
||||
|
||||
const downloadData = (data: Uint8Array, fileName: string, mimeType: string) => {
|
||||
// @ts-expect-error types say no, but browser says yes
|
||||
@@ -14,25 +13,23 @@ const downloadData = (data: Uint8Array, fileName: string, mimeType: string) => {
|
||||
});
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
|
||||
const a = document.createElement("a");
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = fileName;
|
||||
a.style = "display: none";
|
||||
a.style = 'display: none';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
|
||||
setTimeout(function () {
|
||||
return window.URL.revokeObjectURL(url);
|
||||
}, 1000);
|
||||
setTimeout(() => window.URL.revokeObjectURL(url), 1000);
|
||||
};
|
||||
|
||||
type StepData = Array<{
|
||||
type StepData = {
|
||||
name: string;
|
||||
status: "pending" | "running" | "success" | "failed";
|
||||
status: 'pending' | 'running' | 'success' | 'failed';
|
||||
progress: number;
|
||||
errorMessage?: string;
|
||||
}>;
|
||||
}[];
|
||||
|
||||
export default function Home() {
|
||||
const fileInput = useRef<HTMLInputElement>(null);
|
||||
@@ -46,9 +43,8 @@ export default function Home() {
|
||||
oldStepData.map((oldData) => {
|
||||
if (oldData.name === step) {
|
||||
return { ...oldData, ...data };
|
||||
} else {
|
||||
return oldData;
|
||||
}
|
||||
return oldData;
|
||||
}),
|
||||
);
|
||||
};
|
||||
@@ -57,164 +53,164 @@ export default function Home() {
|
||||
const flashEnglishFirmware = async () => {
|
||||
clearSteps();
|
||||
|
||||
appendStep({ name: "Connect device", status: "running", progress: -1 });
|
||||
appendStep({ name: 'Connect device', status: 'running', progress: -1 });
|
||||
const espController = await EspController.fromRequestedDevice().catch(
|
||||
(e) => {
|
||||
updateStepData("Connect device", {
|
||||
status: "failed",
|
||||
updateStepData('Connect device', {
|
||||
status: 'failed',
|
||||
errorMessage: e.toString(),
|
||||
});
|
||||
throw e;
|
||||
},
|
||||
);
|
||||
await espController.connect().catch((e) => {
|
||||
updateStepData("Connect device", {
|
||||
status: "failed",
|
||||
updateStepData('Connect device', {
|
||||
status: 'failed',
|
||||
errorMessage: e.toString(),
|
||||
});
|
||||
throw e;
|
||||
});
|
||||
updateStepData("Connect device", { status: "success" });
|
||||
updateStepData('Connect device', { status: 'success' });
|
||||
|
||||
appendStep({ name: "Download firmware", status: "running", progress: -1 });
|
||||
const firmwareFile = await getFirmware("3.0.8").catch((e) => {
|
||||
updateStepData("Download firmware", {
|
||||
status: "failed",
|
||||
appendStep({ name: 'Download firmware', status: 'running', progress: -1 });
|
||||
const firmwareFile = await getFirmware('3.0.8').catch((e) => {
|
||||
updateStepData('Download firmware', {
|
||||
status: 'failed',
|
||||
errorMessage: e.toString(),
|
||||
});
|
||||
throw e;
|
||||
});
|
||||
updateStepData("Download firmware", { status: "success" });
|
||||
updateStepData('Download firmware', { status: 'success' });
|
||||
|
||||
appendStep({ name: "Flash OTA partition", status: "running", progress: 0 });
|
||||
appendStep({ name: 'Flash OTA partition', status: 'running', progress: 0 });
|
||||
await espController
|
||||
.writeEmptyOtaPartition((_, p, t) =>
|
||||
updateStepData("Flash OTA partition", { progress: p / t }),
|
||||
updateStepData('Flash OTA partition', { progress: p / t }),
|
||||
)
|
||||
.catch((e) => {
|
||||
updateStepData("Flash OTA partition", {
|
||||
status: "failed",
|
||||
updateStepData('Flash OTA partition', {
|
||||
status: 'failed',
|
||||
errorMessage: e.toString(),
|
||||
});
|
||||
throw e;
|
||||
});
|
||||
updateStepData("Flash OTA partition", { status: "success" });
|
||||
updateStepData('Flash OTA partition', { status: 'success' });
|
||||
|
||||
appendStep({
|
||||
name: "Flash OTA_0 partition",
|
||||
status: "running",
|
||||
name: 'Flash OTA_0 partition',
|
||||
status: 'running',
|
||||
progress: 0,
|
||||
});
|
||||
await espController
|
||||
.writeOta0(firmwareFile, (_, p, t) =>
|
||||
updateStepData("Flash OTA_0 partition", { progress: p / t }),
|
||||
updateStepData('Flash OTA_0 partition', { progress: p / t }),
|
||||
)
|
||||
.catch((e) => {
|
||||
updateStepData("Flash OTA_0 partition", {
|
||||
status: "failed",
|
||||
updateStepData('Flash OTA_0 partition', {
|
||||
status: 'failed',
|
||||
errorMessage: e.toString(),
|
||||
});
|
||||
throw e;
|
||||
});
|
||||
updateStepData("Flash OTA_0 partition", { status: "success" });
|
||||
updateStepData('Flash OTA_0 partition', { status: 'success' });
|
||||
|
||||
appendStep({ name: "Reset device", status: "running", progress: -0 });
|
||||
appendStep({ name: 'Reset device', status: 'running', progress: -0 });
|
||||
await espController.disconnect().catch((e) => {
|
||||
updateStepData("Reset device", {
|
||||
status: "failed",
|
||||
updateStepData('Reset device', {
|
||||
status: 'failed',
|
||||
errorMessage: e.toString(),
|
||||
});
|
||||
throw e;
|
||||
});
|
||||
updateStepData("Reset device", { status: "success" });
|
||||
updateStepData('Reset device', { status: 'success' });
|
||||
};
|
||||
|
||||
const saveFullFlash = async () => {
|
||||
clearSteps();
|
||||
|
||||
appendStep({ name: "Connect device", status: "running", progress: -1 });
|
||||
appendStep({ name: 'Connect device', status: 'running', progress: -1 });
|
||||
const espController = await EspController.fromRequestedDevice().catch(
|
||||
(e) => {
|
||||
updateStepData("Connect device", {
|
||||
status: "failed",
|
||||
updateStepData('Connect device', {
|
||||
status: 'failed',
|
||||
errorMessage: e.toString(),
|
||||
});
|
||||
throw e;
|
||||
},
|
||||
);
|
||||
await espController.connect().catch((e) => {
|
||||
updateStepData("Connect device", {
|
||||
status: "failed",
|
||||
updateStepData('Connect device', {
|
||||
status: 'failed',
|
||||
errorMessage: e.toString(),
|
||||
});
|
||||
throw e;
|
||||
});
|
||||
updateStepData("Connect device", { status: "success" });
|
||||
updateStepData('Connect device', { status: 'success' });
|
||||
|
||||
appendStep({ name: "Read flash", status: "running", progress: 0 });
|
||||
appendStep({ name: 'Read flash', status: 'running', progress: 0 });
|
||||
const firmwareFile = await espController
|
||||
.readFullFlash((_, p, t) =>
|
||||
updateStepData("Read flash", { progress: p / t }),
|
||||
updateStepData('Read flash', { progress: p / t }),
|
||||
)
|
||||
.catch((e) => {
|
||||
updateStepData("Read flash", {
|
||||
status: "failed",
|
||||
updateStepData('Read flash', {
|
||||
status: 'failed',
|
||||
errorMessage: e.toString(),
|
||||
});
|
||||
throw e;
|
||||
});
|
||||
updateStepData("Read flash", { status: "success" });
|
||||
updateStepData('Read flash', { status: 'success' });
|
||||
|
||||
downloadData(firmwareFile, "flash.bin", "application/octet-stream");
|
||||
downloadData(firmwareFile, 'flash.bin', 'application/octet-stream');
|
||||
};
|
||||
|
||||
const writeFullFlash = async () => {
|
||||
clearSteps();
|
||||
|
||||
appendStep({ name: "Read file", status: "running", progress: -1 });
|
||||
appendStep({ name: 'Read file', status: 'running', progress: -1 });
|
||||
const file = fileInput.current?.files?.[0];
|
||||
if (!file) {
|
||||
updateStepData("Read file", {
|
||||
status: "failed",
|
||||
errorMessage: "File could not be found",
|
||||
updateStepData('Read file', {
|
||||
status: 'failed',
|
||||
errorMessage: 'File could not be found',
|
||||
});
|
||||
return;
|
||||
}
|
||||
const fileData = new Uint8Array(await file.arrayBuffer());
|
||||
updateStepData("Read file", { status: "success" });
|
||||
updateStepData('Read file', { status: 'success' });
|
||||
|
||||
appendStep({ name: "Connect device", status: "running", progress: -1 });
|
||||
appendStep({ name: 'Connect device', status: 'running', progress: -1 });
|
||||
const espController = await EspController.fromRequestedDevice().catch(
|
||||
(e) => {
|
||||
updateStepData("Connect device", {
|
||||
status: "failed",
|
||||
updateStepData('Connect device', {
|
||||
status: 'failed',
|
||||
errorMessage: e.toString(),
|
||||
});
|
||||
throw e;
|
||||
},
|
||||
);
|
||||
await espController.connect().catch((e) => {
|
||||
updateStepData("Connect device", {
|
||||
status: "failed",
|
||||
updateStepData('Connect device', {
|
||||
status: 'failed',
|
||||
errorMessage: e.toString(),
|
||||
});
|
||||
throw e;
|
||||
});
|
||||
updateStepData("Connect device", { status: "success" });
|
||||
updateStepData('Connect device', { status: 'success' });
|
||||
|
||||
appendStep({ name: "Write flash", status: "running", progress: 0 });
|
||||
appendStep({ name: 'Write flash', status: 'running', progress: 0 });
|
||||
await espController
|
||||
.writeFullFlash(fileData, (_, p, t) =>
|
||||
updateStepData("Write flash", { progress: p / t }),
|
||||
updateStepData('Write flash', { progress: p / t }),
|
||||
)
|
||||
.catch((e) => {
|
||||
updateStepData("Write flash", {
|
||||
status: "failed",
|
||||
updateStepData('Write flash', {
|
||||
status: 'failed',
|
||||
errorMessage: e.toString(),
|
||||
});
|
||||
throw e;
|
||||
});
|
||||
updateStepData("Write flash", { status: "success" });
|
||||
updateStepData('Write flash', { status: 'success' });
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -222,17 +218,23 @@ export default function Home() {
|
||||
<h1>Xteink English Firmware Flasher</h1>
|
||||
<section className={styles.section}>
|
||||
<h2>Full flash controls</h2>
|
||||
<button onClick={saveFullFlash}>Save full flash</button>
|
||||
<div style={{ display: "flex" }}>
|
||||
<button type="button" onClick={saveFullFlash}>
|
||||
Save full flash
|
||||
</button>
|
||||
<div style={{ display: 'flex' }}>
|
||||
<input ref={fileInput} type="file" />
|
||||
<button style={{ flexGrow: 1 }} onClick={writeFullFlash}>
|
||||
<button
|
||||
type="button"
|
||||
style={{ flexGrow: 1 }}
|
||||
onClick={writeFullFlash}
|
||||
>
|
||||
Write full flash from file
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
<section className={styles.section}>
|
||||
<h2>OTA fast flash controls</h2>
|
||||
<button onClick={flashEnglishFirmware}>
|
||||
<button type="button" onClick={flashEnglishFirmware}>
|
||||
Flash English firmware (3.0.8) via OTA
|
||||
</button>
|
||||
</section>
|
||||
|
||||
@@ -1,50 +1,74 @@
|
||||
import React from 'react';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
type StepProps = { name: string, status: 'pending' | 'running' | 'success' | 'failed', progress: number, errorMessage?: string};
|
||||
interface StepProps {
|
||||
name: string;
|
||||
status: 'pending' | 'running' | 'success' | 'failed';
|
||||
progress: number;
|
||||
errorMessage?: string;
|
||||
}
|
||||
|
||||
function ProgressBar({ progress }: { progress: number }) {
|
||||
return (
|
||||
<div style={{ width: 100, background: 'lightgrey', height: 14 }}>
|
||||
<div style={{width: `${Math.round(progress * 10000) / 100}%`, transition: 'width', height: '100%', backgroundColor: 'green' }}></div>
|
||||
</div>
|
||||
)
|
||||
<div style={{ width: 100, background: 'lightgrey', height: 14 }}>
|
||||
<div
|
||||
style={{
|
||||
width: `${Math.round(progress * 10000) / 100}%`,
|
||||
transition: 'width',
|
||||
height: '100%',
|
||||
backgroundColor: 'green',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function StepStatus({ status, progress }: Pick<StepProps, 'status' | 'progress'>) {
|
||||
function StepStatus({
|
||||
status,
|
||||
progress,
|
||||
}: Pick<StepProps, 'status' | 'progress'>) {
|
||||
let statusIcon;
|
||||
switch (status) {
|
||||
case 'pending':
|
||||
statusIcon = (<span>P</span>)
|
||||
statusIcon = <span>P</span>;
|
||||
break;
|
||||
case 'running':
|
||||
statusIcon = (<span>Running</span>)
|
||||
statusIcon = <span>Running</span>;
|
||||
break;
|
||||
case 'success':
|
||||
statusIcon = (<span>Success</span>)
|
||||
statusIcon = <span>Success</span>;
|
||||
break;
|
||||
case 'failed':
|
||||
statusIcon = (<span>Failed</span>)
|
||||
statusIcon = <span>Failed</span>;
|
||||
break;
|
||||
default:
|
||||
statusIcon = (<span>Unknown status</span>)
|
||||
statusIcon = <span>Unknown status</span>;
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{statusIcon}
|
||||
{status === 'running' && progress !== -1 && (<ProgressBar progress={progress} />)}
|
||||
{status === 'running' && progress !== -1 && (
|
||||
<ProgressBar progress={progress} />
|
||||
)}
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default function Step({name, status, progress, errorMessage}: StepProps) {
|
||||
export default function Step({
|
||||
name,
|
||||
status,
|
||||
progress,
|
||||
errorMessage,
|
||||
}: StepProps) {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<span>{name}:</span>
|
||||
<StepStatus status={status} progress={progress} />
|
||||
{errorMessage && (<span className={styles.errorMessage}>{errorMessage}</span>)}
|
||||
{errorMessage && (
|
||||
<span className={styles.errorMessage}>{errorMessage}</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+6
-2
@@ -4,16 +4,20 @@
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"incremental": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitAny": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"github": {
|
||||
"silent": true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user