flasher.meshcore.dev/index.html

320 lines
14 KiB
HTML
Raw Permalink Normal View History

2025-02-23 10:19:53 +01:00
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MeshCore flasher</title>
<link href="/css/beer.css" rel="stylesheet">
<link href="/css/flasher.css" rel="stylesheet">
<script type="module" src="/flasher.js"></script>
2025-02-23 10:19:53 +01:00
</head>
2026-01-10 23:15:50 +01:00
2025-02-23 10:19:53 +01:00
<body class="dark">
2025-03-09 16:29:43 +01:00
<div id="app" v-cloak>
2026-01-10 23:15:50 +01:00
<div class="snackbar" :class="snackbar.class" popover>
<i v-if="snackbar.icon">{{ snackbar.icon }}</i>
<span v-html="snackbar.text"></span>
</div>
<div class="flash-container" data-iframe-height>
2025-03-09 16:29:43 +01:00
<div v-if="flashing.active">
<header>
<nav>
<i>developer_board</i>
<span class="small">{{ selected.device.name }}</span>
<i>chevron_right</i>
<i>{{ selected.firmware.icon || config.role[selected.firmware.role].icon }}</i>
2026-01-10 23:15:50 +01:00
<span class="small strong">{{ selected.firmware.title || config.role[selected.firmware.role].title }}</span>
2025-03-09 16:29:43 +01:00
</nav>
</header>
<article v-if="flashing.error">
<div class="row">
<div class="max">
<h6>Flashing failed!</h6>
<p><span>{{ flashing.error }}</span></p>
</div>
</div>
2026-01-10 23:15:50 +01:00
<nav class="small-margin">
<button @click="retry">
<i>bolt</i>
<span>Retry</span>
</button>
<button @click="close">
<i>arrow_back</i>
<span>Close</span>
</button>
</nav>
2025-03-09 16:29:43 +01:00
</article>
<article v-else>
<div class="row">
2026-01-10 23:15:50 +01:00
<div class="max" v-if="flashing.percent < 100">
2025-03-09 16:29:43 +01:00
<h6><progress class="circle small"></progress> Flashing...</h6>
<p>Please do not disconnect the device</p>
</div>
<div class="max" v-else>
<h6>Flashing complete!</h6>
2026-01-10 23:15:50 +01:00
<nav class="small-margin">
<button @click="openSerialGUI()" v-if="selected.firmware.role === 'roomServer' || selected.firmware.role === 'repeater'">
<i>manufacturing</i>
<span>Configure via USB</span>
<div class="tooltip left max">Open GUI to configure Repeaters and Room servers via USB serial</div>
</button>
<button @click="close">
<i>arrow_back</i>
<span>Close</span>
</button>
<button @click="retry">
<i>bolt</i>
<span>Flash Again</span>
</button>
</nav>
2025-03-09 16:29:43 +01:00
</div>
</div>
<div class="autoscroller">
2025-05-04 10:11:29 +02:00
<pre class="term" v-if="flashing.log">{{ flashing.log }}</pre>
2025-03-09 16:29:43 +01:00
</div>
<nav>
2026-01-10 23:15:50 +01:00
<progress :value="flashing.percent" max="100"></progress>
2025-03-09 16:29:43 +01:00
</nav>
</article>
</div>
<div v-else-if="selected.firmware">
<header>
<nav>
<button class="circle transparent" @click="stepBack"><i>arrow_back</i></button>
<i>developer_board</i>
<a class="small" href="javascript:;" @click="stepBack">{{ selected.device.name }}</a>
<i>chevron_right</i>
</nav>
2026-01-10 23:15:50 +01:00
<article class="no-margin">
<ul class="list">
<li>
<i>{{ getRoleFwValue(selected.firmware, 'icon') }}</i>&nbsp;
<div class="max">
<h6 class="small strong">{{ getRoleFwValue(selected.firmware, 'title') }} {{ getRoleFwValue(selected.firmware, 'subTitle') }}</h6>
<span class="text-wrap">{{ getRoleFwValue(selected.firmware, 'tooltip') }}</span>
</div>
</li>
</ul>
</article>
<div v-if="getNotice(selected)" v-html="getNotice(selected)"></div>
2025-05-04 10:11:29 +02:00
<nav class="version">
2025-03-09 16:29:43 +01:00
<div class="field label suffix border small">
<select v-model="selected.version">
<template v-for="(version, versionName) in selected.firmware.version">
<option v-if="version.files.length > 0">{{ versionName }}</option>
</template>
</select>
<label>Version</label>
<i>arrow_drop_down</i>
</div>
2026-01-10 23:15:50 +01:00
<div class="max pre" v-if="getSelFwValue('notes')">
<b>Changelog:</b><br>
<span v-html="formatChangeLog(getSelFwValue('notes'))"></span>
2025-05-04 10:11:29 +02:00
</div>
2025-03-09 16:29:43 +01:00
</nav>
</header>
<ul class="list border" v-if="selected.device.type === 'esp32'">
<li>
<label class="checkbox">
<input type="checkbox" v-model="selected.wipe">
<span>Erase device</span>
<div class="tooltip right max">
DO NOT carry out a full erase if you are simply updating your MeshCore device,
otherwise it will erase your MeshCore identity for that device.
</div>
</label>
</li>
</ul>
2025-05-04 10:11:29 +02:00
<template v-if="selected.device.type === 'nrf52'">
<div class="small-space"></div>
2026-01-10 23:15:50 +01:00
<button @click="dfuMode" :disabled="selected.nrfEraserFlashing || !flashing.supported">
<i>{{ flashing.dfuComplete ? 'check' : 'code' }}</i>
<span>{{ flashing.dfuComplete ? 'DFU mode active' : 'Enter DFU mode' }}</span>
<div class="tooltip right max" v-if="flashing.supported">
Enter DFU mode - this mode enables you to flash your firmware.
If you did not trigger the DFU mode manually, please click this button.
</div>
<div class="tooltip right max" v-else>
Your browser doesn't support Web Serial API. Please use Chrome or Edge on Desktop
</div>
</button>
<button @click="nrfErase" :disabled="!flashing.supported || selected.nrfEraserFlashing || selected.nrfEraserFlashingPercent > 0">
<i v-if="selected.nrfEraserFlashingPercent === 100">check</i>
<progress v-else-if="selected.nrfEraserFlashing" class="circle small"></progress>
<i v-else>delete_forever</i>
<span v-if="selected.nrfEraserFlashingPercent === 100">Flash Erased!</span>
<span v-if="selected.nrfEraserFlashingPercent > 0">Flashing erase firmware: {{selected.nrfEraserFlashingPercent}}%</span>
<span v-else-if="selected.nrfEraserFlashing">Connecting...</span>
<span v-else>Erase Flash</span>
<div class="tooltip right max" v-if="flashing.supported">
Flash special firmware that will format your flash and erase all user data.<br>
Click <b>Enter DFU mode</b> before erasing, if you did not trigger the DFU mode manually
</div>
<div class="tooltip right max" v-else>
Your browser doesn't support Web Serial API. Please use Chrome or Edge on Desktop
</div>
</button>
2025-05-04 10:11:29 +02:00
</template>
2025-03-09 16:29:43 +01:00
<div class="medium-space"></div>
<nav class="small-margin">
2026-01-10 23:15:50 +01:00
<template v-if="canFlash(selected.device)">
<button @click="flashDevice" :disabled="selected.nrfEraserFlashing || !flashing.supported">
<i>bolt</i>
<span>Flash!</span>
<div class="tooltip right max" v-if="flashing.supported">
Upload the firmware into your device. Existing firwmare will get overwritten.
<span v-if="selected.device.type === 'nrf52'">
If you did not trigger DFU mode manually, use the <b>Enter DFU mode</b> before flashing
</span>
</div>
<div class="tooltip right max" v-else>
Your browser doesn't support Web Serial API. Please use Chrome or Edge on Desktop
</div>
</button>
<div class="max"></div>
</template>
2025-03-09 16:29:43 +01:00
<button data-ui="#down" class="active" v-if="!getSelFwValue('customFile')">
<i>download</i>
<span>Download</span><i>arrow_drop_down</i>
<menu class="left no-wrap" id="down" data-ui="#down">
<li v-for="file in getSelFwValue('files')">
<a data-ui="menu-selector" :href="getFirmwarePath(file)" download>{{ file.title }}</a>
</li>
2026-01-10 23:15:50 +01:00
<li v-if="selected.device.type === 'nrf52' && selected.device.erase">
<a data-ui="menu-selector" :href="`${config.staticPath}/${selected.device.erase.replace('.zip', '.uf2')}`" download>{{ selected.device.erase.replace('.zip', '.uf2') }}</a>
</li>
<li v-if="selected.device.type === 'nrf52' && selected.device.bootloader">
<a data-ui="menu-selector" :href="`${config.staticPath}/${selected.device.bootloader}`" download>{{ selected.device.bootloader }}</a>
</li>
2025-03-09 16:29:43 +01:00
</menu>
<div class="tooltip left max">Download a copy of the firmware files for use with other flashers</div>
</button>
</nav>
</div>
<div v-else-if="selected.device">
<header>
<nav>
<button class="circle transparent" @click="stepBack"><i>arrow_back</i></button>
<i>developer_board</i>
2026-01-10 23:15:50 +01:00
<span class="small strong">{{ selected.device.name }}</span>
2025-03-09 16:29:43 +01:00
</nav>
<nav class="no-margin">
<h6 class="small max">Choose role</h6>
</nav>
</header>
<ul class="list border">
<template v-for="firmware in selected.device.firmware">
<li v-if="firmwareHasData(firmware)" :class="getRoleFwValue(firmware, 'class')">
<button class="transparent" @click="selected.firmware = firmware">
<i>{{ getRoleFwValue(firmware, 'icon') }}</i>
<span>{{ getRoleFwValue(firmware, 'title') }} {{ getRoleFwValue(firmware, 'subTitle') }}</span>
<div class="tooltip right max" v-if="getRoleFwValue(firmware, 'tooltip')" v-html="getRoleFwValue(firmware, 'tooltip')"></div>
</button>
</li>
</template>
</ul>
</div>
<div v-else>
<header>
<nav>
<i>bolt</i>
2026-01-10 23:15:50 +01:00
<h5 class="small max logo-top">
<img src="/img/meshcore.svg"> <span>flasher</span>
2026-01-10 23:15:50 +01:00
</h5>
2025-05-04 10:11:29 +02:00
<button class="transparent hide-mobile" @click="openSerialGUI()">
<i>manufacturing</i>
<span>Repeater Setup</span>
<div class="tooltip left max">Open GUI to configure Repeaters and Room servers via USB serial</div>
</button>
2025-03-09 16:29:43 +01:00
<button class="transparent" @click="openSerialCon()">
<i>terminal</i>
<span>Console</span>
<div class="tooltip left max">Open serial console to manage Routers and Room servers</div>
</button>
</nav>
<nav class="no-margin">
<h6 class="small max">Choose device</h6>
</nav>
</header>
2026-01-10 23:15:50 +01:00
<div class="field label prefix suffix border w-100 no-margin">
<i>search</i>
<input type="text" placeholder=" " v-model="deviceFilterText" autofocus>
<label>Filter</label>
</div>
2025-03-09 16:29:43 +01:00
<ul class="list border">
2026-01-10 23:15:50 +01:00
<li v-for="(deviceGroup, type) in devices">
<details open>
<summary style="background-color: var(--surface-container)">
<div>
<strong v-if="type === 'ripple'">Ripple Firmware</strong>
<strong v-else-if="type === 'meshos'">MeshOS Firmware</strong>
<strong v-else-if="type === 'community'">Community Firmware</strong>
2026-01-10 23:15:50 +01:00
<div class="tooltip right max">
<template v-if="type === 'ripple'">
Freemium firmware provided by <a class="inverse-link" target="_blank" href="https://buymeacoffee.com/ripplebiz">Ripple Radios</a>
</template>
<template v-if="type === 'meshos'">
Freemium firmware provided by Andy Kirby
</template>
<template v-else-if="type === 'community'">
2026-01-10 23:15:50 +01:00
Open Source <a class="inverse-link" target="_blank" href="https://github.com/meshcore-dev/MeshCore">Community firmware</a>
</template>
</div>
</div>
</summary>
<ul class="list border">
<li v-for="device in deviceGroup">
<template v-if="device.firmware.length > 0">
<button class="device-select transparent" @click="selected.device = device">
<img class="icon" :title="device.type" :src="`/img/${device.type}.svg`" v-if="device.type !== 'noflash'">
<i v-else>developer_board</i>
<span>{{ device.name }}</span>
<div class="tooltip right max" v-if="device.tooltip" v-html="device.tooltip"></div>
<div class="max"></div>
</button>
<div class="max"></div>
<img class="icon" :src="device.icon" v-if="device.icon" >
</template>
</li>
</ul>
</details>
2025-03-09 16:29:43 +01:00
</li>
<li>
2026-01-10 23:15:50 +01:00
<label>
<i>unknown_document</i>&nbsp;
<strong style="font-size: small;">Custom Firmware</strong>
2025-03-09 16:29:43 +01:00
<div class="tooltip right max">
Flash custom firmware file from your computer.
Flasher supports <b>.bin</b> files for esp32 devices and OTA <b>.zip</b> files for nRF52 devices.
</div>
<input type="file" accept=".zip,.bin" @change="customFirmwareLoad">
2026-01-10 23:15:50 +01:00
</label>
2025-03-09 16:29:43 +01:00
</li>
</ul>
</div>
</div>
<div v-if="serialCon.opened" class="overlay active">
<datalist id="command-db">
<option v-for="(desc, command) in commandReference" :value="command">{{ desc }}</option>
</datalist>
<header>
<nav>
<button class="circle transparent" @click="closeSerialCon()"><i>arrow_back</i></button>
<h6 class="small max">Serial Console</h6>
</nav>
</header>
2026-01-10 23:15:50 +01:00
<pre class="console" ref="consoleWindow" tabindex="0" @mouseup="consoleMouseUp">
2025-03-09 16:29:43 +01:00
<code>{{ serialCon.content }}</code>
<div class="holder">
<span>&gt;</span>
<input ref="consoleEditBox" class="console-input" type="text" v-model="serialCon.edit" @keydown.enter.prevent="sendCommand(serialCon.edit)" list="command-db">
</div>
</pre>
</div>
</div>
<script src="/lib/iframeResizer.contentWindow.min.js"></script>
2025-02-23 10:19:53 +01:00
</body>
</html>