diff --git a/Provider/Documentation/build_frontend.md b/Provider/Documentation/build_frontend.md index d998a5d..33ab010 100644 --- a/Provider/Documentation/build_frontend.md +++ b/Provider/Documentation/build_frontend.md @@ -62,3 +62,21 @@ Because `CONFIG_CALENDINK_DEPLOY_WEB_PAGES` is enabled, CMake will automatically 1. Detect your `frontend/dist/` folder. 2. Run `mklittlefs` to package it into `www.bin`. 3. Flash `www.bin` directly to the active `www_0` partition on the ESP32! + +## 5. Over-The-Air (OTA) Updates + +Once the backend supports it (Phase 2+), you can update the frontend without using USB or `idf.py`. + +1. **Build the assets**: `npm run build:esp32` +2. **Package the image**: `npm run ota:package` + - This generates a `frontend/bin/www.bin` file. + - **Configuration**: If the tool is not in your PATH, add its path to `frontend/.env`: + ```env + MKLITTLEFS_PATH=C:\path\to\mklittlefs.exe + ``` + *(Note: The script also supports `littlefs-python.exe` usually found in the `build/littlefs_py_venv/Scripts/` folder).* +3. **Upload via Dashboard**: + - Open the dashboard in your browser. + - Go to the **Frontend Update** section. + - Select the `www.bin` file and click **Flash**. + - The device will automatically write to the inactive partition and reboot. diff --git a/Provider/frontend/.env b/Provider/frontend/.env new file mode 100644 index 0000000..808554a --- /dev/null +++ b/Provider/frontend/.env @@ -0,0 +1,2 @@ +VITE_API_BASE=http://192.168.50.216 +MKLITTLEFS_PATH=W:\Classified\Calendink\Provider\build\littlefs_py_venv\Scripts\littlefs-python.exe diff --git a/Provider/frontend/package.json b/Provider/frontend/package.json index b729be1..c4265e3 100644 --- a/Provider/frontend/package.json +++ b/Provider/frontend/package.json @@ -7,6 +7,7 @@ "dev": "vite", "build": "vite build", "build:esp32": "vite build && node scripts/gzip.js", + "ota:package": "node scripts/package.js", "preview": "vite preview" }, "devDependencies": { diff --git a/Provider/frontend/scripts/package.js b/Provider/frontend/scripts/package.js new file mode 100644 index 0000000..6024c88 --- /dev/null +++ b/Provider/frontend/scripts/package.js @@ -0,0 +1,113 @@ +/** + * OTA Packaging Script + * Generates www.bin from dist/ using mklittlefs or littlefs-python. + */ + +import { execSync } from 'child_process'; +import { resolve, dirname } from 'path'; +import { fileURLToPath } from 'url'; +import { existsSync, readFileSync, mkdirSync } from 'fs'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const projectRoot = resolve(__dirname, '..'); +const distDir = resolve(projectRoot, 'dist'); +const binDir = resolve(projectRoot, 'bin'); +const outputFile = resolve(binDir, 'www.bin'); + +// Ensure bin directory exists +if (!existsSync(binDir)) { + mkdirSync(binDir, { recursive: true }); +} + +// Configuration matching partitions.csv (1MB = 1048576 bytes) +const FS_SIZE = 1048576; +const BLOCK_SIZE = 4096; +const PAGE_SIZE = 256; + +console.log('--- OTA Packaging ---'); + +/** + * Simple .env parser to load MKLITTLEFS_PATH without external dependencies + */ +function loadEnv() { + const envPaths = [ + resolve(projectRoot, '.env.local'), + resolve(projectRoot, '.env') + ]; + + for (const path of envPaths) { + if (existsSync(path)) { + console.log(`Loading config from: ${path}`); + const content = readFileSync(path, 'utf8'); + content.split('\n').forEach(line => { + const [key, ...valueParts] = line.split('='); + if (key && valueParts.length > 0) { + const value = valueParts.join('=').trim().replace(/^["']|["']$/g, ''); + process.env[key.trim()] = value; + } + }); + } + } +} + +loadEnv(); + +if (!existsSync(distDir)) { + console.error('Error: dist/ directory not found. Run "npm run build:esp32" first.'); + process.exit(1); +} + +// Try to find mklittlefs or littlefs-python +const findTool = () => { + // 1. Check environment variable (from manual set or .env) + if (process.env.MKLITTLEFS_PATH) { + if (existsSync(process.env.MKLITTLEFS_PATH)) { + return process.env.MKLITTLEFS_PATH; + } + console.warn(`Warning: MKLITTLEFS_PATH set to ${process.env.MKLITTLEFS_PATH} but file not found.`); + } + + // 2. Check system PATH + const tools = ['mklittlefs', 'littlefs-python']; + for (const tool of tools) { + try { + execSync(`${tool} --version`, { stdio: 'ignore' }); + return tool; + } catch (e) { + // Not in path + } + } + + return null; +}; + +const tool = findTool(); + +if (!tool) { + console.error('Error: No LittleFS tool found (checked mklittlefs and littlefs-python).'); + console.info('Please set MKLITTLEFS_PATH in your .env file.'); + console.info('Example: MKLITTLEFS_PATH=C:\\Espressif\\tools\\mklittlefs\\v3.2.0\\mklittlefs.exe'); + process.exit(1); +} + +try { + console.log(`Using tool: ${tool}`); + console.log(`Packaging ${distDir} -> ${outputFile}...`); + + let cmd; + // Check if it is the Python version or the C++ version + if (tool.includes('littlefs-python')) { + // Python style: littlefs-python create