/** * Universal OTA Bundle Creator * Packs FW (Provider.bin) and WWW (www_v*.bin) into a single .bundle file. */ import { readFileSync, writeFileSync, readdirSync, existsSync, mkdirSync } from 'fs'; import { resolve, dirname } from 'path'; import { fileURLToPath } from 'url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const projectRoot = resolve(__dirname, '..'); const providerRoot = resolve(projectRoot, '..'); const binDir = resolve(projectRoot, 'bin'); // Paths const fwFile = resolve(providerRoot, 'build', 'Provider.bin'); const bundleDir = resolve(projectRoot, 'bundles'); if (!existsSync(bundleDir)) { mkdirSync(bundleDir, { recursive: true }); } console.log('--- Universal Bundle Packaging ---'); // 1. Find the latest www.bin with proper semantic version sorting const binFiles = readdirSync(binDir) .filter(f => f.startsWith('www_v') && f.endsWith('.bin')) .sort((a, b) => { const getParts = (s) => { const m = s.match(/v(\d+)\.(\d+)\.(\d+)/); return m ? m.slice(1).map(Number) : [0, 0, 0]; }; const [aMajor, aMinor, aRev] = getParts(a); const [bMajor, bMinor, bRev] = getParts(b); return (bMajor - aMajor) || (bMinor - aMinor) || (bRev - aRev); }); if (binFiles.length === 0) { console.error('Error: No www_v*.bin found in frontend/bin/. Run "npm run ota:package" first.'); process.exit(1); } const wwwFile = resolve(binDir, binFiles[0]); if (!existsSync(fwFile)) { console.error(`Error: Firmware binary not found at ${fwFile}. Run "idf.py build" first.`); process.exit(1); } try { console.log(`Packing Firmware: ${fwFile}`); console.log(`Packing Frontend: ${wwwFile}`); const fwBuf = readFileSync(fwFile); const wwwBuf = readFileSync(wwwFile); // Create 12-byte header // Magic: BNDL (4 bytes) // FW Size: uint32 (4 bytes) // WWW Size: uint32 (4 bytes) const header = Buffer.alloc(12); header.write('BNDL', 0); header.writeUInt32LE(fwBuf.length, 4); header.writeUInt32LE(wwwBuf.length, 8); const bundleBuf = Buffer.concat([header, fwBuf, wwwBuf]); const outputFile = resolve(bundleDir, `universal_v${binFiles[0].replace('www_v', '').replace('.bin', '')}.bundle`); writeFileSync(outputFile, bundleBuf); console.log('-------------------------------'); console.log(`Success: Bundle created at ${outputFile}`); console.log(`Total size: ${(bundleBuf.length / 1024 / 1024).toFixed(2)} MB`); console.log('-------------------------------'); } catch (e) { console.error('Error creating bundle:', e.message); process.exit(1); }