add web install tools
This commit is contained in:
parent
10b04509ba
commit
14a8b44221
|
@ -0,0 +1,24 @@
|
|||
.item {
|
||||
margin: 0.5em;
|
||||
}
|
||||
.itemTitle {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.2em;
|
||||
}
|
||||
button.installButton, button.showConsole, button.hideConsole {
|
||||
font-size: 1em;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
select.consoleBaud {
|
||||
display: inline-block;
|
||||
max-width: 10em;
|
||||
font-size: 1em;
|
||||
}
|
||||
.console {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
body {
|
||||
font-size: 16px;
|
||||
font-family: system-ui;
|
||||
line-height: 1.5em;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<script src="https://cdn.jsdelivr.net/npm/xterm@4.19.0/lib/xterm.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@4.19.0/css/xterm.css">
|
||||
<script type="module" src="install.js"></script>
|
||||
<link rel="stylesheet" href="install.css"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
</body>
|
||||
<div class="heading"></div>
|
||||
<div class="console"></div>
|
||||
<div class="content"></div>
|
||||
<div id="terminal"></div>
|
||||
</html>
|
|
@ -0,0 +1,151 @@
|
|||
import {XtermOutputHandler} from "./installUtil.js";
|
||||
import ESPInstaller from "./installUtil.js";
|
||||
(function(){
|
||||
let espLoaderTerminal;
|
||||
let espInstaller;
|
||||
let releaseData={};
|
||||
const addEl=ESPInstaller.addEl; //shorter typing
|
||||
let showConsole;
|
||||
let hideConsole;
|
||||
const enableConsole=(enable,disableBoth)=>{
|
||||
if (showConsole) showConsole.disabled=!enable || disableBoth;
|
||||
if (hideConsole) hideConsole.disabled=enable || disableBoth;
|
||||
}
|
||||
const showError=(txt)=>{
|
||||
let hFrame=document.querySelector('.heading');
|
||||
if (hFrame){
|
||||
hFrame.textContent=txt;
|
||||
hFrame.classList.add("error");
|
||||
}
|
||||
else{
|
||||
alert(txt);
|
||||
}
|
||||
}
|
||||
const buildHeading=(user,repo,element)=>{
|
||||
let hFrame=document.querySelector(element||'.heading');
|
||||
if (! hFrame) return;
|
||||
hFrame.textContent='';
|
||||
let h=addEl('h2',undefined,hFrame,`ESP32 Install ${user}:${repo}`)
|
||||
}
|
||||
const checkChip=(chipFamily,assetName)=>{
|
||||
//for now only ESP32
|
||||
if (chipFamily != "ESP32"){
|
||||
throw new Error(`unexpected chip family ${chipFamily}, expected ESP32`);
|
||||
}
|
||||
return assetName;
|
||||
}
|
||||
const baudRates=[1200,
|
||||
2400,
|
||||
4800,
|
||||
9600,
|
||||
14400,
|
||||
19200,
|
||||
28800,
|
||||
38400,
|
||||
57600,
|
||||
115200,
|
||||
230400,
|
||||
460800];
|
||||
const buildConsoleButtons=(element)=>{
|
||||
let bFrame=document.querySelector(element||'.console');
|
||||
if (! bFrame) return;
|
||||
bFrame.textContent='';
|
||||
let cLine=addEl('div','buttons',bFrame);
|
||||
let bSelect=addEl('select','consoleBaud',cLine);
|
||||
baudRates.forEach((baud)=>{
|
||||
let v=addEl('option',undefined,bSelect,baud+'');
|
||||
v.setAttribute('value',baud);
|
||||
});
|
||||
bSelect.value=115200;
|
||||
showConsole=addEl('button','showConsole',cLine,'ShowConsole');
|
||||
showConsole.addEventListener('click',async()=>{
|
||||
enableConsole(false);
|
||||
await espInstaller.startConsole(bSelect.value);
|
||||
})
|
||||
hideConsole=addEl('button','hideConsole',cLine,'HideConsole');
|
||||
hideConsole.addEventListener('click',async()=>{
|
||||
await espInstaller.stopConsole();
|
||||
enableConsole(true);
|
||||
})
|
||||
}
|
||||
const buildButtons=(user,repo,element)=>{
|
||||
let bFrame=document.querySelector(element||'.content');
|
||||
if (! bFrame) return;
|
||||
bFrame.textContent='';
|
||||
if (!releaseData.assets) return;
|
||||
let version=releaseData.name;
|
||||
if (! version){
|
||||
alert("no version found in release data");
|
||||
return;
|
||||
}
|
||||
addEl('div','version',bFrame,`Version: ${version}`);
|
||||
let items={};
|
||||
releaseData.assets.forEach((asset)=>{
|
||||
let name=asset.name;
|
||||
let base=name.replace(/-all\.bin/,'').replace(/-update\.bin/,'');
|
||||
if (items[base] === undefined){
|
||||
items[base]={};
|
||||
}
|
||||
let item=items[base];
|
||||
item.label=base.replace(/-[0-9][0-9]*/,'');
|
||||
if (name.match(/-update\./)){
|
||||
item.update=name;
|
||||
}
|
||||
else{
|
||||
item.basic=name;
|
||||
}
|
||||
});
|
||||
for (let k in items){
|
||||
let item=items[k];
|
||||
let line=addEl('div','item',bFrame);
|
||||
addEl('div','itemTitle',line,item.label);
|
||||
let btLine=addEl('div','buttons',line);
|
||||
let tb=addEl('button','installButton',line,'Initial');
|
||||
tb.addEventListener('click',async ()=>{
|
||||
enableConsole(false,true);
|
||||
await espInstaller.installClicked(
|
||||
true,
|
||||
user,
|
||||
repo,
|
||||
version,
|
||||
4096,
|
||||
(chip)=>checkChip(chip,item.basic)
|
||||
)
|
||||
enableConsole(true);
|
||||
});
|
||||
tb=addEl('button','installButton',line,'Update');
|
||||
tb.addEventListener('click',async ()=>{
|
||||
enableConsole(false,true);
|
||||
await espInstaller.installClicked(
|
||||
false,
|
||||
user,
|
||||
repo,
|
||||
version,
|
||||
65536,
|
||||
(chip)=>checkChip(chip,item.update)
|
||||
)
|
||||
enableConsole(true);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
window.onload = async () => {
|
||||
if (! ESPInstaller.checkAvailable()){
|
||||
showError("your browser does not support the ESP flashing (no serial)");
|
||||
return;
|
||||
}
|
||||
let user = window.gitHubUser||ESPInstaller.getParam('user');
|
||||
let repo = window.gitHubRepo || ESPInstaller.getParam('repo');
|
||||
if (!user || !repo) {
|
||||
alert("missing parameter user or repo");
|
||||
}
|
||||
try {
|
||||
espLoaderTerminal = new XtermOutputHandler('terminal');
|
||||
espInstaller = new ESPInstaller(espLoaderTerminal);
|
||||
buildHeading(user, repo);
|
||||
buildConsoleButtons();
|
||||
releaseData = await espInstaller.getReleaseInfo(user, repo);
|
||||
buildButtons(user, repo);
|
||||
} catch(error){alert("unable to query release info for user "+user+", repo "+repo+": "+error)};
|
||||
}
|
||||
})();
|
|
@ -0,0 +1,155 @@
|
|||
<?php
|
||||
$api="https://api.github.com/repos/#user#/#repo#/releases/latest";
|
||||
$download="https://github.com/#user#/#repo#/releases/download/#dlVersion#/#dlName#";
|
||||
$manifest="?dlName=#mName#&dlVersion=#mVersion#&user=#user#&repo=#repo#";
|
||||
$allowed=array(
|
||||
'user'=> array('wellenvogel'),
|
||||
'repo'=> array('esp32-nmea2000')
|
||||
);
|
||||
if (!function_exists('getallheaders')) {
|
||||
function getallheaders()
|
||||
{
|
||||
$headers = [];
|
||||
foreach ($_SERVER as $name => $value) {
|
||||
if (substr($name, 0, 5) == 'HTTP_') {
|
||||
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
|
||||
}
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
}
|
||||
function safeName($name){
|
||||
return preg_replace('[^0-9_a-zA-Z.-]','',$name);
|
||||
}
|
||||
function replaceVars($str,$vars){
|
||||
foreach ($vars as $n => &$v){
|
||||
$str=str_replace("#".$n."#",$v,$str);
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
function fillUserAndRepo($vars=null){
|
||||
global $allowed;
|
||||
if ($vars == null) {
|
||||
$vars=array();
|
||||
}
|
||||
foreach (array('user','repo') as $n){
|
||||
if (! isset($_REQUEST[$n])){
|
||||
die("missing parameter $n");
|
||||
}
|
||||
$v=$_REQUEST[$n];
|
||||
$av=$allowed[$n];
|
||||
if (! in_array($v,$av)){
|
||||
die("value $v for $n not allowed");
|
||||
}
|
||||
$vars[$n]=$v;
|
||||
}
|
||||
return $vars;
|
||||
}
|
||||
function addVars($vars,$names){
|
||||
foreach ($names as $n){
|
||||
if (! isset($_REQUEST[$n])){
|
||||
die("missing parameter $n");
|
||||
}
|
||||
$safe=safeName($_REQUEST[$n]);
|
||||
$vars[$n]=$safe;
|
||||
}
|
||||
return $vars;
|
||||
}
|
||||
|
||||
function curl_exec_follow(/*resource*/ $ch, /*int*/ &$maxredirect = null) {
|
||||
$mr = $maxredirect === null ? 5 : intval($maxredirect);
|
||||
if (ini_get('open_basedir') == '' && ini_get('safe_mode' == 'Off') && false) {
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $mr > 0);
|
||||
curl_setopt($ch, CURLOPT_MAXREDIRS, $mr);
|
||||
} else {
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
|
||||
if ($mr > 0) {
|
||||
$newurl = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
|
||||
$rch = curl_copy_handle($ch);
|
||||
curl_setopt($rch, CURLOPT_HEADER, true);
|
||||
curl_setopt($rch, CURLOPT_NOBODY, true);
|
||||
curl_setopt($rch, CURLOPT_FORBID_REUSE, false);
|
||||
curl_setopt($rch, CURLOPT_RETURNTRANSFER, true);
|
||||
do {
|
||||
curl_setopt($rch, CURLOPT_URL, $newurl);
|
||||
$header = curl_exec($rch);
|
||||
if (curl_errno($rch)) {
|
||||
$code = 0;
|
||||
} else {
|
||||
$code = curl_getinfo($rch, CURLINFO_HTTP_CODE);
|
||||
if ($code == 301 || $code == 302) {
|
||||
preg_match('/Location:(.*?)\n/', $header, $matches);
|
||||
$newurl = trim(array_pop($matches));
|
||||
} else {
|
||||
$code = 0;
|
||||
}
|
||||
}
|
||||
} while ($code && --$mr);
|
||||
curl_close($rch);
|
||||
if (!$mr) {
|
||||
if ($maxredirect === null) {
|
||||
trigger_error('Too many redirects. When following redirects, libcurl hit the maximum amount.', E_USER_WARNING);
|
||||
} else {
|
||||
$maxredirect = 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_URL, $newurl);
|
||||
}
|
||||
}
|
||||
curl_setopt(
|
||||
$ch,
|
||||
CURLOPT_HEADERFUNCTION,
|
||||
function ($curl, $header) {
|
||||
header($header);
|
||||
return strlen($header);
|
||||
}
|
||||
);
|
||||
curl_setopt(
|
||||
$ch,
|
||||
CURLOPT_WRITEFUNCTION,
|
||||
function ($curl, $body) {
|
||||
echo $body;
|
||||
return strlen($body);
|
||||
}
|
||||
);
|
||||
header('Access-Control-Allow-Origin:*');
|
||||
return curl_exec($ch);
|
||||
}
|
||||
function proxy($url)
|
||||
{
|
||||
$headers=getallheaders();
|
||||
$ch = curl_init($url);
|
||||
curl_setopt_array(
|
||||
$ch,
|
||||
[
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_CONNECTTIMEOUT => 30,
|
||||
]
|
||||
);
|
||||
$FWHDR = ['User-Agent'];
|
||||
$outHeaders = array();
|
||||
foreach ($FWHDR as $k) {
|
||||
if (isset($headers[$k])) {
|
||||
array_push($outHeaders, "$k: $headers[$k]");
|
||||
}
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $outHeaders);
|
||||
$response = curl_exec_follow($ch);
|
||||
curl_close($ch);
|
||||
}
|
||||
|
||||
if (isset($_REQUEST['api'])) {
|
||||
$vars=fillUserAndRepo();
|
||||
proxy(replaceVars($api,$vars));
|
||||
exit(0);
|
||||
}
|
||||
if (isset($_REQUEST['dlName'])){
|
||||
$vars=fillUserAndRepo();
|
||||
$vars=addVars($vars,array('dlName','dlVersion'));
|
||||
proxy(replaceVars($download,$vars));
|
||||
exit(0);
|
||||
}
|
||||
die("invalid request");
|
||||
?>
|
|
@ -0,0 +1,338 @@
|
|||
import {ESPLoader,Transport} from "https://cdn.jsdelivr.net/npm/esptool-js@0.2.1/bundle.js";
|
||||
/**
|
||||
* write all messages to the console
|
||||
*/
|
||||
class ConsoleOutputHandler{
|
||||
clean() {
|
||||
}
|
||||
writeLine(data) {
|
||||
console.log("ESPInstaller:",data);
|
||||
}
|
||||
write(data) {
|
||||
console.log(data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write messages to an instance of xterm
|
||||
* to use this, include in your html
|
||||
<script src="https://cdn.jsdelivr.net/npm/xterm@4.19.0/lib/xterm.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@4.19.0/css/xterm.css">
|
||||
* and create a div element
|
||||
<div id="terminal"/>
|
||||
* provide the id of this div to the constructor
|
||||
*/
|
||||
class XtermOutputHandler {
|
||||
constructor(termId) {
|
||||
let termElement = document.getElementById(termId);
|
||||
if (termElement) {
|
||||
this.term = new Terminal({ cols: 120, rows: 40 , convertEol: true });
|
||||
this.term.open(termElement);
|
||||
}
|
||||
this.clean=this.clean.bind(this);
|
||||
this.writeLine=this.writeLine.bind(this);
|
||||
this.write=this.write.bind(this);
|
||||
}
|
||||
clean() {
|
||||
if (!this.term) return;
|
||||
this.term.clear();
|
||||
}
|
||||
writeLine(data) {
|
||||
if (!this.term) {
|
||||
console.log("TERM:", data);
|
||||
return;
|
||||
};
|
||||
this.term.writeln(data);
|
||||
}
|
||||
write(data) {
|
||||
if (!this.term) {
|
||||
console.log("TERM:", data);
|
||||
return;
|
||||
};
|
||||
this.term.write(data)
|
||||
}
|
||||
};
|
||||
class ESPInstaller{
|
||||
constructor(outputHandler){
|
||||
this.espLoaderTerminal=outputHandler|| new ConsoleOutputHandler();
|
||||
this.transport=undefined;
|
||||
this.esploader=undefined;
|
||||
this.chipFamily=undefined;
|
||||
this.base=import.meta.url.replace(/[^/]*$/,"install.php");
|
||||
this.consoleDevice=undefined;
|
||||
this.consoleReader=undefined;
|
||||
}
|
||||
/**
|
||||
* get an URL query parameter
|
||||
* @param key
|
||||
* @returns
|
||||
*/
|
||||
static getParam(key){
|
||||
let value=RegExp(""+key+"[^&]+").exec(window.location.search);
|
||||
// Return the unescaped value minus everything starting from the equals sign or an empty string
|
||||
return decodeURIComponent(!!value ? value.toString().replace(/^[^=]+./,"") : "");
|
||||
};
|
||||
/**
|
||||
* add an HTML element
|
||||
* @param {*} type
|
||||
* @param {*} clazz
|
||||
* @param {*} parent
|
||||
* @param {*} text
|
||||
* @returns
|
||||
*/
|
||||
static addEl(type, clazz, parent, text) {
|
||||
let el = document.createElement(type);
|
||||
if (clazz) {
|
||||
if (!(clazz instanceof Array)) {
|
||||
clazz = clazz.split(/ */);
|
||||
}
|
||||
clazz.forEach(function (ce) {
|
||||
el.classList.add(ce);
|
||||
});
|
||||
}
|
||||
if (text) el.textContent = text;
|
||||
if (parent) parent.appendChild(el);
|
||||
return el;
|
||||
}
|
||||
/**
|
||||
* call a function for each matching element
|
||||
* @param {*} selector
|
||||
* @param {*} cb
|
||||
*/
|
||||
static forEachEl(selector,cb){
|
||||
let arr=document.querySelectorAll(selector);
|
||||
for (let i=0;i<arr.length;i++){
|
||||
cb(arr[i]);
|
||||
}
|
||||
}
|
||||
static checkAvailable(){
|
||||
if (! navigator.serial || ! navigator.serial.requestPort) return false;
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* execute a reset on the connected device
|
||||
*/
|
||||
async resetTransport() {
|
||||
if (!this.transport) {
|
||||
throw new Error("not connected");
|
||||
}
|
||||
this.espLoaderTerminal.writeLine("Resetting...");
|
||||
await this.transport.device.setSignals({
|
||||
dataTerminalReady: false,
|
||||
requestToSend: true,
|
||||
});
|
||||
await this.transport.device.setSignals({
|
||||
dataTerminalReady: false,
|
||||
requestToSend: false,
|
||||
});
|
||||
};
|
||||
|
||||
async disconnect(){
|
||||
if (this.consoleDevice){
|
||||
try{
|
||||
if (this.consoleReader){
|
||||
await this.consoleReader.cancel();
|
||||
this.consoleReader=undefined;
|
||||
}
|
||||
await this.consoleDevice.close();
|
||||
}catch(e){
|
||||
console.log(`error cancel serial read ${e}`);
|
||||
}
|
||||
this.consoleDevice=undefined;
|
||||
}
|
||||
if (this.transport){
|
||||
try{
|
||||
await this.transport.disconnect();
|
||||
await this.transport.waitForUnlock(1500);
|
||||
}catch (e){}
|
||||
this.transport=undefined;
|
||||
}
|
||||
this.esploader=undefined;
|
||||
}
|
||||
async connect() {
|
||||
this.espLoaderTerminal.clean();
|
||||
await this.disconnect();
|
||||
let device = await navigator.serial.requestPort({});
|
||||
if (!device) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.transport = new Transport(device);
|
||||
this.esploader = new ESPLoader(this.transport, 115200, this.espLoaderTerminal);
|
||||
let foundChip = await this.esploader.main_fn();
|
||||
if (!foundChip) {
|
||||
throw new Error("unable to read chip id");
|
||||
}
|
||||
this.espLoaderTerminal.writeLine(`chip: ${foundChip}`);
|
||||
await this.esploader.flash_id();
|
||||
this.chipFamily = this.esploader.chip.CHIP_NAME;
|
||||
this.espLoaderTerminal.writeLine(`chipFamily: ${this.chipFamily}`);
|
||||
} catch (e) {
|
||||
this.disconnect();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async startConsole(baud) {
|
||||
await this.disconnect();
|
||||
try {
|
||||
let device = await navigator.serial.requestPort({});
|
||||
if (!device) {
|
||||
return;
|
||||
}
|
||||
this.consoleDevice=device;
|
||||
let br=baud || 115200;
|
||||
await device.open({
|
||||
baudRate: br
|
||||
});
|
||||
this.consoleReader=device.readable.getReader();
|
||||
this.espLoaderTerminal.clean();
|
||||
this.espLoaderTerminal.writeLine(`Console at ${br}:`);
|
||||
while (this.consoleReader) {
|
||||
let {value:val,done:done} = await this.consoleReader.read();
|
||||
if (typeof val !== 'undefined') {
|
||||
this.espLoaderTerminal.write(val);
|
||||
}
|
||||
if (done){
|
||||
console.log("Console reader stopped");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) { this.espLoaderTerminal.writeLine(`Error: ${e}`) }
|
||||
this.espLoaderTerminal.writeLine("Console reader stopped");
|
||||
}
|
||||
async stopConsole(){
|
||||
await this.disconnect();
|
||||
}
|
||||
|
||||
isConnected(){
|
||||
return this.transport !== undefined;
|
||||
}
|
||||
checkConnected(){
|
||||
if (! this.isConnected){
|
||||
throw new Error("not connected");
|
||||
}
|
||||
}
|
||||
|
||||
getChipFamily(){
|
||||
this.checkConnected();
|
||||
return this.chipFamily;
|
||||
}
|
||||
/**
|
||||
* flass the device
|
||||
* @param {*} fileList : an array of entries {data:blob,address:number}
|
||||
*/
|
||||
async writeFlash(fileList){
|
||||
this.checkConnected();
|
||||
this.espLoaderTerminal.writeLine(`Flashing....`);
|
||||
await this.esploader.write_flash(
|
||||
fileList,
|
||||
"keep",
|
||||
"keep",
|
||||
"keep",
|
||||
false
|
||||
)
|
||||
await this.resetTransport();
|
||||
this.espLoaderTerminal.writeLine(`Done.`);
|
||||
}
|
||||
/**
|
||||
* fetch a release asset from github
|
||||
* @param {*} user
|
||||
* @param {*} repo
|
||||
* @param {*} version
|
||||
* @param {*} name
|
||||
* @returns
|
||||
*/
|
||||
async getReleaseAsset(user,repo,version,name){
|
||||
const url=this.base+"?dlName="+encodeURIComponent(name)+
|
||||
"&dlVersion="+encodeURIComponent(version)+
|
||||
"&user="+encodeURIComponent(user)+
|
||||
"&repo="+encodeURIComponent(repo);
|
||||
this.espLoaderTerminal.writeLine(`downloading image from ${url}`);
|
||||
const resp=await fetch(url);
|
||||
if (! resp.ok){
|
||||
throw new Error(`unable to download image from ${url}: ${resp.status}`);
|
||||
}
|
||||
const reader=new FileReader();
|
||||
const blob= await resp.blob();
|
||||
let data=await new Promise((resolve)=>{
|
||||
reader.addEventListener("load",() => resolve(reader.result));
|
||||
reader.readAsBinaryString(blob);
|
||||
});
|
||||
this.espLoaderTerminal.writeLine(`successfully loaded ${data.length} bytes`);
|
||||
return data;
|
||||
}
|
||||
/**
|
||||
* handle the click of an install button
|
||||
* @param {*} isFull
|
||||
* @param {*} user
|
||||
* @param {*} repo
|
||||
* @param {*} version
|
||||
* @param {*} address
|
||||
* @param {*} assetName the name of the asset file.
|
||||
* can be a function - will be called with the chip family
|
||||
* and must return the asset file name
|
||||
* @returns
|
||||
*/
|
||||
async installClicked(isFull, user, repo, version, address, assetName) {
|
||||
try {
|
||||
await this.connect();
|
||||
let assetFileName = assetName;
|
||||
if (typeof (assetName) === 'function') {
|
||||
assetFileName = assetName(this.getChipFamily());
|
||||
}
|
||||
let imageData = await this.getReleaseAsset(user, repo, version, assetFileName);
|
||||
if (!imageData || imageData.length == 0) {
|
||||
throw new Error(`no image data fetched`);
|
||||
}
|
||||
let fileList = [
|
||||
{ data: imageData, address: address }
|
||||
];
|
||||
let txt = isFull ? "baseImage (all data will be erased)" : "update";
|
||||
if (!confirm(`ready to install ${version}\n${txt}`)) {
|
||||
this.espLoaderTerminal.writeLine("aborted by user...");
|
||||
await this.disconnect();
|
||||
return;
|
||||
}
|
||||
await this.writeFlash(fileList);
|
||||
await this.disconnect();
|
||||
} catch (e) {
|
||||
this.espLoaderTerminal.writeLine(`Error: ${e}`);
|
||||
alert(`Error: ${e}`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* fetch the release info from the github API
|
||||
* @param {*} user
|
||||
* @param {*} repo
|
||||
* @returns
|
||||
*/
|
||||
async getReleaseInfo(user,repo){
|
||||
let url=this.base+"?api=1&user="+encodeURIComponent(user)+"&repo="+encodeURIComponent(repo)
|
||||
let resp=await fetch(url);
|
||||
if (! resp.ok){
|
||||
throw new Error(`unable to query release info from ${url}: ${resp.status}`);
|
||||
}
|
||||
return await resp.json();
|
||||
}
|
||||
/**
|
||||
* get the release info in a parsed form
|
||||
* @param {*} user
|
||||
* @param {*} repo
|
||||
* @returns an object: {version:nnn, assets:[name1,name2,...]}
|
||||
*/
|
||||
async getParsedReleaseInfo(user,repo){
|
||||
let raw=await this.getReleaseInfo(user,repo);
|
||||
let rt={
|
||||
version:raw.name,
|
||||
assets:[]
|
||||
};
|
||||
if (! raw.assets) return rt;
|
||||
raw.assets.forEach((asset)=>{
|
||||
rt.assets.push(asset.name);
|
||||
})
|
||||
return rt;
|
||||
}
|
||||
};
|
||||
export {ConsoleOutputHandler, XtermOutputHandler};
|
||||
export default ESPInstaller;
|
Loading…
Reference in New Issue