mirror of
https://github.com/cesanta/mongoose.git
synced 2026-11-01 13:10:43 +00:00
Add OTA to the device dashboard. Implement for H5
This commit is contained in:
1 parent
d5b5cec797
commit
2f014237b2
19 files changed
+3332
-7534
No files matched your search
@@ -2,7 +2,7 @@
|
||||
|
||||
'use strict';
|
||||
import { h, render, useState, useEffect, useRef, html, Router } from './bundle.js';
|
||||
import { Icons, Login, Setting, Button, Stat, tipColors, Colored, Notification, Pagination } from './components.js';
|
||||
import { Icons, Login, Setting, Button, Stat, tipColors, Colored, Notification, Pagination, UploadFileButton } from './components.js';
|
||||
|
||||
const Logo = props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12.87 12.85"><defs><style>.ll-cls-1{fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:0.5px;}</style></defs><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="ll-cls-1" d="M12.62,1.82V8.91A1.58,1.58,0,0,1,11,10.48H4a1.44,1.44,0,0,1-1-.37A.69.69,0,0,1,2.84,10l-.1-.12a.81.81,0,0,1-.15-.48V5.57a.87.87,0,0,1,.86-.86H4.73V7.28a.86.86,0,0,0,.86.85H9.42a.85.85,0,0,0,.85-.85V3.45A.86.86,0,0,0,10.13,3,.76.76,0,0,0,10,2.84a.29.29,0,0,0-.12-.1,1.49,1.49,0,0,0-1-.37H2.39V1.82A1.57,1.57,0,0,1,4,.25H11A1.57,1.57,0,0,1,12.62,1.82Z"/><path class="ll-cls-1" d="M10.48,10.48V11A1.58,1.58,0,0,1,8.9,12.6H1.82A1.57,1.57,0,0,1,.25,11V3.94A1.57,1.57,0,0,1,1.82,2.37H8.9a1.49,1.49,0,0,1,1,.37l.12.1a.76.76,0,0,1,.11.14.86.86,0,0,1,.14.47V7.28a.85.85,0,0,1-.85.85H8.13V5.57a.86.86,0,0,0-.85-.86H3.45a.87.87,0,0,0-.86.86V9.4a.81.81,0,0,0,.15.48l.1.12a.69.69,0,0,0,.13.11,1.44,1.44,0,0,0,1,.37Z"/></g></g></svg>`;
|
||||
|
||||
@@ -46,6 +46,7 @@ function Sidebar({url, show}) {
|
||||
<div class="flex flex-1 flex-col">
|
||||
<${NavLink} title="Dashboard" icon=${Icons.home} href="/" url=${url} />
|
||||
<${NavLink} title="Settings" icon=${Icons.settings} href="/settings" url=${url} />
|
||||
<${NavLink} title="Firmware Update" icon=${Icons.download} href="/update" url=${url} />
|
||||
<${NavLink} title="Events" icon=${Icons.alert} href="/events" url=${url} />
|
||||
<//>
|
||||
<//>
|
||||
@@ -143,13 +144,16 @@ function Chart({data}) {
|
||||
<//>`;
|
||||
};
|
||||
|
||||
function DeveloperNote({text}) {
|
||||
function DeveloperNote({text, children}) {
|
||||
return html`
|
||||
<div class="flex p-4 gap-2">
|
||||
<${Icons.info} class="self-start basis-[30px] grow-0 shrink-0 text-green-600" />
|
||||
<div class="text-sm">
|
||||
<div class="font-semibold mt-1">Developer Note<//>
|
||||
${text.split('.').map(v => html` <p class="my-2 text-slate-500">${v}<//>`)}
|
||||
<div class="text-sm text-slate-500">
|
||||
<div class="flex items-center">
|
||||
<${Icons.info} class="self-start basis-[30px] grow-0 shrink-0 text-green-600 mr-2" />
|
||||
<div class="font-semibold">Developer Note<//>
|
||||
<//>
|
||||
${(text || '').split('.').map(v => html` <p class="my-2 ">${v}<//>`)}
|
||||
${children}
|
||||
<//>
|
||||
<//>`;
|
||||
};
|
||||
@@ -181,6 +185,95 @@ function Main({}) {
|
||||
<//>`;
|
||||
};
|
||||
|
||||
function FirmwareStatus({title, info, children}) {
|
||||
return html`
|
||||
<div class="bg-white py-1 divide-y border rounded">
|
||||
<div class="font-light uppercase flex items-center text-gray-600 px-4 py-2">
|
||||
${title}
|
||||
<//>
|
||||
<div class="px-4 py-2 relative">
|
||||
<div class="my-1">CRC32: ${info.valid ? info.crc32.toString(16) : 'n/a'}<//>
|
||||
<div class="my-1">Size: ${info.valid ? info.size : 'n/a'}<//>
|
||||
<div class="my-1">Flashed at: ${info.valid ? new Date(info.time * 1000).toLocaleString() : 'n/a'}<//>
|
||||
<div class="my-1">State: ${info.valid ? (info.golden == 0 ? 'commtited' : 'NOT committed') : 'n/a'}<//>
|
||||
${children}
|
||||
<//>
|
||||
<//>`;
|
||||
};
|
||||
|
||||
|
||||
function FirmwareUpdate({}) {
|
||||
const [info, setInfo] = useState([{}, {}]);
|
||||
const refresh = () => fetch('api/firmware/status').then(r => r.json()).then(r => setInfo(r));
|
||||
useEffect(refresh, []);
|
||||
const state = ['new', 'dirty', 'clean'][(info.state || 0) % 3];
|
||||
const oncommit = ev => fetch('api/firmware/commit')
|
||||
.then(r => r.json())
|
||||
.then(refresh);
|
||||
const onreboot = ev => fetch('api/sys/reset')
|
||||
.then(r => r.json())
|
||||
.then(r => new Promise(r => setTimeout(ev => { refresh(); r(); }, 3000)));
|
||||
const onrollback = ev => fetch('api/firmware/rollback')
|
||||
.then(onreboot);
|
||||
const onupload = function(ok, name, size) {
|
||||
if (!ok) return false;
|
||||
return new Promise(r => setTimeout(ev => { refresh(); r(); }, 3000));
|
||||
};
|
||||
const clean = info[0].valid && info[0].golden == 0;
|
||||
return html`
|
||||
<div class="m-4 gap-4 grid grid-cols-1 lg:grid-cols-2">
|
||||
<${FirmwareStatus} title="Current firmware image" info=${info[0]}>
|
||||
<${Button} cls="mr-2" title="Commit this firmware"
|
||||
onclick=${oncommit} icon=${Icons.thumbUp} disabled=${clean} />
|
||||
<${Button} title="Reboot device" onclick=${onreboot} icon=${Icons.refresh} clsx="absolute top-4 right-4" />
|
||||
<${UploadFileButton} class="mt-2"
|
||||
title="Upload new firmware: choose .bin file:" onupload=${onupload}
|
||||
url="api/firmware/upload" accept=".bin,.uf2" />
|
||||
<//>
|
||||
<${FirmwareStatus} title="Previous firmware image" info=${info[1]}>
|
||||
<${Button} title="Rollback to this firmware" onclick=${onrollback}
|
||||
icon=${Icons.backward} disabled=${info[1].valid == false} />
|
||||
<//>
|
||||
|
||||
<div class="bg-white border shadow-lg">
|
||||
<${DeveloperNote}>
|
||||
<div class="my-2">
|
||||
When new firmware gets flashed, its status is unreliable, "not
|
||||
committed". In order to become "committed" (verified), a firmware must
|
||||
be committed. If a firmware is not committed, then the next boot
|
||||
reverts back to the previous firmware.
|
||||
<//>
|
||||
<div class="my-2">
|
||||
This GUI loads a firmware file and sends it chunk by chunk to the
|
||||
device, passing current chunk offset, total firmware size and a file name:
|
||||
api/firmware/upload?offset=X&total=Y&name=Z
|
||||
<//>
|
||||
<//>
|
||||
<//>
|
||||
|
||||
<div class="bg-white border shadow-lg">
|
||||
<${DeveloperNote}>
|
||||
<div>
|
||||
Firmware udpdate mechanism defines 3 API functions that the
|
||||
target device must implement: ota_begin(), ota_write() and ota_end()
|
||||
<//>
|
||||
<div class="my-2">
|
||||
RESTful API handlers use ota_xxx() API to save firmware to flash.
|
||||
The last 0-length chunk triggers ota_end() which performs firmware
|
||||
update using saved firmware image
|
||||
<//>
|
||||
<div class="my-2">
|
||||
<a class="link text-blue-600 underline"
|
||||
href="https://mongoose.ws/webinars/">Subscribe to our free webinar</a> to
|
||||
get detailed explanations about possible firmware updates strategies
|
||||
and implementation demo
|
||||
<//>
|
||||
<//>
|
||||
<//>
|
||||
|
||||
<//>`;
|
||||
};
|
||||
|
||||
function Settings({}) {
|
||||
const [settings, setSettings] = useState(null);
|
||||
const [saveResult, setSaveResult] = useState(null);
|
||||
@@ -256,6 +349,7 @@ const App = function({}) {
|
||||
<${Router} onChange=${ev => setUrl(ev.url)} history=${History.createHashHistory()} >
|
||||
<${Main} default=${true} />
|
||||
<${Settings} path="settings" />
|
||||
<${FirmwareUpdate} path="update" />
|
||||
<${Events} path="events" />
|
||||
<//>
|
||||
<//>
|
||||
|
||||
Reference in new issue
Block a user