diff --git a/dist/cpinstaller.js b/dist/cpinstaller.js index 906275b..326460b 100644 --- a/dist/cpinstaller.js +++ b/dist/cpinstaller.js @@ -110,8 +110,16 @@ export class CPInstallButton extends InstallButton { } async connectedCallback() { - // Required - this.boardIds = this.getAttribute("boardid").split(","); + // Load the Board Definitions before the button is ever clicked + const response = await fetch(BOARD_DEFS); + this.boardDefs = await response.json(); + + let boardIds = this.getAttribute("boardid") + if (!boardIds || boardIds.trim().length === 0) { + this.boardIds = Object.keys(this.boardDefs); + } else { + this.boardIds = boardIds.split(","); + } // If there is only one board id, then select it by default if (this.boardIds.length === 1) { @@ -123,10 +131,6 @@ export class CPInstallButton extends InstallButton { this.releaseVersion = this.getAttribute("version"); } - // Load the Board Definitions before the button is ever clicked - const response = await fetch(BOARD_DEFS); - this.boardDefs = await response.json(); - super.connectedCallback(); } @@ -439,6 +443,18 @@ export class CPInstallButton extends InstallButton { for (let boardId of this.boardIds) { options.push({id: boardId, name: this.getBoardName(boardId)}); } + + options.sort((a, b) => { + let boardA = a.name.trim().toLowerCase(); + let boardB = b.name.trim().toLowerCase(); + if (boardA < boardB) { + return -1; + } + if (boardA > boardB) { + return 1; + } + return 0; + }); return options; } diff --git a/dist/cpinstaller.min.js b/dist/cpinstaller.min.js index 10d29a1..ca9eb75 100644 --- a/dist/cpinstaller.min.js +++ b/dist/cpinstaller.min.js @@ -1,4 +1,4 @@ -"use strict";import{html}from"https://cdn.jsdelivr.net/npm/lit-html/+esm";import{map}from"https://cdn.jsdelivr.net/npm/lit-html/directives/map/+esm";import*as toml from"https://cdn.jsdelivr.net/npm/iarna-toml-esm@3.0.5/+esm";import*as zip from"https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.6.65/+esm";import*as esptoolPackage from"https://cdn.jsdelivr.net/npm/esp-web-flasher@5.1.2/dist/web/index.js/+esm";import{REPL}from"https://cdn.jsdelivr.net/gh/adafruit/circuitpython-repl-js/repl.js";import{InstallButton,ESP_ROM_BAUD}from"./base_installer.min.js";const PREFERRED_BAUDRATE=921600,COPY_CHUNK_SIZE=65536,DEFAULT_RELEASE_LATEST=!1,BOARD_DEFS="https://adafruit-circuit-python.s3.amazonaws.com/esp32_boards.json",CSS_DIALOG_CLASS="cp-installer-dialog",FAMILY_TO_CHIP_MAP={esp32s2:esptoolPackage.CHIP_FAMILY_ESP32S2,esp32s3:esptoolPackage.CHIP_FAMILY_ESP32S3,esp32c3:esptoolPackage.CHIP_FAMILY_ESP32C3,esp32:esptoolPackage.CHIP_FAMILY_ESP32},attrMap={bootloader:"bootloaderUrl",uf2file:"uf2FileUrl",binfile:"binFileUrl"};class CPInstallButton extends InstallButton{constructor(){super(),this.releaseVersion="[version]",this.boardName="ESP32-based device",this.boardIds=null,this.selectedBoardId=null,this.bootloaderUrl=null,this.boardDefs=null,this.uf2FileUrl=null,this.binFileUrl=null,this.releaseVersion=0,this.chipFamily=null,this.dialogCssClass=CSS_DIALOG_CLASS,this.dialogs={...this.dialogs,...this.cpDialogs},this.bootDriveHandle=null,this.circuitpyDriveHandle=null,this._bootDriveName=null,this._serialPortName=null,this.replSerialDevice=null,this.repl=null,this.fileCache=[],this.reader=null,this.writer=null,this.tomlSettings=null,this.init()}static get observedAttributes(){return Object.keys(attrMap)}parseVersion(e){var t={},e=e.match(/(\d+)\.(\d+)\.(\d+)(?:-([a-z]+)\.(\d+))?/);return e&&4<=e.length&&(t.major=e[1],t.minor=e[2],t.patch=e[3],e[4]&&e[5]?(t.suffix=e[4],t.suffixVersion=e[5]):(t.suffix="stable",t.suffixVersion=0)),t}sortReleases(e){const r=["major","minor","patch","suffix","suffixVersion"];return e.sort((e,t)=>{var i,s=this.parseVersion(e.version),a=this.parseVersion(t.version);for(i of r){if(s[i]a[i])return 1}return 0}),e}async connectedCallback(){this.boardIds=this.getAttribute("boardid").split(","),1===this.boardIds.length&&(this.selectedBoardId=this.boardIds[0]),this.getAttribute("version")&&(this.releaseVersion=this.getAttribute("version"));var e=await fetch(BOARD_DEFS);this.boardDefs=await e.json(),super.connectedCallback()}async loadBoard(e){let t=null;if(Object.keys(this.boardDefs).includes(e)){e=this.boardDefs[e],e=(this.chipFamily=e.chipfamily,e.name&&(this.boardName=e.name),e.bootloader&&(this.bootloaderUrl=this.updateBinaryUrl(e.bootloader)),this.sortReleases(e.releases));if(this.releaseVersion)for(var i of e)if(i.version==this.releaseVersion){t=i;break}t||(t=DEFAULT_RELEASE_LATEST?e[e.length-1]:e[0],this.releaseVersion=t.version),t.uf2file&&(this.uf2FileUrl=this.updateBinaryUrl(t.uf2file)),t.binfile&&(this.binFileUrl=this.updateBinaryUrl(t.binfile))}this.getAttribute("chipfamily")&&(this.chipFamily=this.getAttribute("chipfamily")),this.getAttribute("boardname")&&(this.boardName=this.getAttribute("boardname")),this.menuTitle="CircuitPython Installer for "+this.boardName}attributeChangedCallback(e,t,i){this[attrMap[e]]=i?this.updateBinaryUrl(i):null}updateBinaryUrl(e){return e=e&&e.replace("https://downloads.circuitpython.org/","https://adafruit-circuit-python.s3.amazonaws.com/")}flows={uf2FullProgram:{label:"Full CircuitPython [version] Install",steps:[this.stepWelcome,this.stepSerialConnect,this.stepConfirm,this.stepEraseAll,this.stepBootloader,this.stepSelectBootDrive,this.stepCopyUf2,this.stepSelectCpyDrive,this.stepCredentials,this.stepSuccess],isEnabled:async()=>this.hasNativeUsb()&&!!this.bootloaderUrl&&!!this.uf2FileUrl},binFullProgram:{label:"Full CircuitPython [version] Install",steps:[this.stepWelcome,this.stepSerialConnect,this.stepConfirm,this.stepEraseAll,this.stepFlashBin,this.stepSetupRepl,this.stepCredentials,this.stepSuccess],isEnabled:async()=>!this.hasNativeUsb()&&!!this.binFileUrl},uf2Only:{label:"Upgrade/Install CircuitPython [version] UF2 Only",steps:[this.stepWelcome,this.stepSelectBootDrive,this.stepCopyUf2,this.stepSelectCpyDrive,this.stepCredentials,this.stepSuccess],isEnabled:async()=>this.hasNativeUsb()&&!!this.uf2FileUrl},binOnly:{label:"Upgrade CircuitPython [version] Bin Only",steps:[this.stepWelcome,this.stepSerialConnect,this.stepConfirm,this.stepEraseAll,this.stepFlashBin,this.stepSuccess],isEnabled:async()=>!!this.binFileUrl},bootloaderOnly:{label:"Install Bootloader Only",steps:[this.stepWelcome,this.stepSerialConnect,this.stepConfirm,this.stepEraseAll,this.stepBootloader,this.stepSuccess],isEnabled:async()=>this.hasNativeUsb()&&!!this.bootloaderUrl},credentialsOnlyRepl:{label:"Update WiFi credentials",steps:[this.stepWelcome,this.stepSetupRepl,this.stepCredentials,this.stepSuccess],isEnabled:async()=>!this.hasNativeUsb()},credentialsOnlyDrive:{label:"Update WiFi credentials",steps:[this.stepWelcome,this.stepSelectCpyDrive,this.stepCredentials,this.stepSuccess],isEnabled:async()=>this.hasNativeUsb()}};cpDialogs={boardSelect:{closeable:!0,template:i=>html` +"use strict";import{html}from"https://cdn.jsdelivr.net/npm/lit-html/+esm";import{map}from"https://cdn.jsdelivr.net/npm/lit-html/directives/map/+esm";import*as toml from"https://cdn.jsdelivr.net/npm/iarna-toml-esm@3.0.5/+esm";import*as zip from"https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.6.65/+esm";import*as esptoolPackage from"https://cdn.jsdelivr.net/npm/esp-web-flasher@5.1.2/dist/web/index.js/+esm";import{REPL}from"https://cdn.jsdelivr.net/gh/adafruit/circuitpython-repl-js/repl.js";import{InstallButton,ESP_ROM_BAUD}from"./base_installer.min.js";const PREFERRED_BAUDRATE=921600,COPY_CHUNK_SIZE=65536,DEFAULT_RELEASE_LATEST=!1,BOARD_DEFS="https://adafruit-circuit-python.s3.amazonaws.com/esp32_boards.json",CSS_DIALOG_CLASS="cp-installer-dialog",FAMILY_TO_CHIP_MAP={esp32s2:esptoolPackage.CHIP_FAMILY_ESP32S2,esp32s3:esptoolPackage.CHIP_FAMILY_ESP32S3,esp32c3:esptoolPackage.CHIP_FAMILY_ESP32C3,esp32:esptoolPackage.CHIP_FAMILY_ESP32},attrMap={bootloader:"bootloaderUrl",uf2file:"uf2FileUrl",binfile:"binFileUrl"};class CPInstallButton extends InstallButton{constructor(){super(),this.releaseVersion="[version]",this.boardName="ESP32-based device",this.boardIds=null,this.selectedBoardId=null,this.bootloaderUrl=null,this.boardDefs=null,this.uf2FileUrl=null,this.binFileUrl=null,this.releaseVersion=0,this.chipFamily=null,this.dialogCssClass=CSS_DIALOG_CLASS,this.dialogs={...this.dialogs,...this.cpDialogs},this.bootDriveHandle=null,this.circuitpyDriveHandle=null,this._bootDriveName=null,this._serialPortName=null,this.replSerialDevice=null,this.repl=null,this.fileCache=[],this.reader=null,this.writer=null,this.tomlSettings=null,this.init()}static get observedAttributes(){return Object.keys(attrMap)}parseVersion(e){var t={},e=e.match(/(\d+)\.(\d+)\.(\d+)(?:-([a-z]+)\.(\d+))?/);return e&&4<=e.length&&(t.major=e[1],t.minor=e[2],t.patch=e[3],e[4]&&e[5]?(t.suffix=e[4],t.suffixVersion=e[5]):(t.suffix="stable",t.suffixVersion=0)),t}sortReleases(e){const r=["major","minor","patch","suffix","suffixVersion"];return e.sort((e,t)=>{var i,s=this.parseVersion(e.version),a=this.parseVersion(t.version);for(i of r){if(s[i]a[i])return 1}return 0}),e}async connectedCallback(){var e=await fetch(BOARD_DEFS),e=(this.boardDefs=await e.json(),this.getAttribute("boardid"));e&&0!==e.trim().length?this.boardIds=e.split(","):this.boardIds=Object.keys(this.boardDefs),1===this.boardIds.length&&(this.selectedBoardId=this.boardIds[0]),this.getAttribute("version")&&(this.releaseVersion=this.getAttribute("version")),super.connectedCallback()}async loadBoard(e){let t=null;if(Object.keys(this.boardDefs).includes(e)){e=this.boardDefs[e],e=(this.chipFamily=e.chipfamily,e.name&&(this.boardName=e.name),e.bootloader&&(this.bootloaderUrl=this.updateBinaryUrl(e.bootloader)),this.sortReleases(e.releases));if(this.releaseVersion)for(var i of e)if(i.version==this.releaseVersion){t=i;break}t||(t=DEFAULT_RELEASE_LATEST?e[e.length-1]:e[0],this.releaseVersion=t.version),t.uf2file&&(this.uf2FileUrl=this.updateBinaryUrl(t.uf2file)),t.binfile&&(this.binFileUrl=this.updateBinaryUrl(t.binfile))}this.getAttribute("chipfamily")&&(this.chipFamily=this.getAttribute("chipfamily")),this.getAttribute("boardname")&&(this.boardName=this.getAttribute("boardname")),this.menuTitle="CircuitPython Installer for "+this.boardName}attributeChangedCallback(e,t,i){this[attrMap[e]]=i?this.updateBinaryUrl(i):null}updateBinaryUrl(e){return e=e&&e.replace("https://downloads.circuitpython.org/","https://adafruit-circuit-python.s3.amazonaws.com/")}flows={uf2FullProgram:{label:"Full CircuitPython [version] Install",steps:[this.stepWelcome,this.stepSerialConnect,this.stepConfirm,this.stepEraseAll,this.stepBootloader,this.stepSelectBootDrive,this.stepCopyUf2,this.stepSelectCpyDrive,this.stepCredentials,this.stepSuccess],isEnabled:async()=>this.hasNativeUsb()&&!!this.bootloaderUrl&&!!this.uf2FileUrl},binFullProgram:{label:"Full CircuitPython [version] Install",steps:[this.stepWelcome,this.stepSerialConnect,this.stepConfirm,this.stepEraseAll,this.stepFlashBin,this.stepSetupRepl,this.stepCredentials,this.stepSuccess],isEnabled:async()=>!this.hasNativeUsb()&&!!this.binFileUrl},uf2Only:{label:"Upgrade/Install CircuitPython [version] UF2 Only",steps:[this.stepWelcome,this.stepSelectBootDrive,this.stepCopyUf2,this.stepSelectCpyDrive,this.stepCredentials,this.stepSuccess],isEnabled:async()=>this.hasNativeUsb()&&!!this.uf2FileUrl},binOnly:{label:"Upgrade CircuitPython [version] Bin Only",steps:[this.stepWelcome,this.stepSerialConnect,this.stepConfirm,this.stepEraseAll,this.stepFlashBin,this.stepSuccess],isEnabled:async()=>!!this.binFileUrl},bootloaderOnly:{label:"Install Bootloader Only",steps:[this.stepWelcome,this.stepSerialConnect,this.stepConfirm,this.stepEraseAll,this.stepBootloader,this.stepSuccess],isEnabled:async()=>this.hasNativeUsb()&&!!this.bootloaderUrl},credentialsOnlyRepl:{label:"Update WiFi credentials",steps:[this.stepWelcome,this.stepSetupRepl,this.stepCredentials,this.stepSuccess],isEnabled:async()=>!this.hasNativeUsb()},credentialsOnlyDrive:{label:"Update WiFi credentials",steps:[this.stepWelcome,this.stepSelectCpyDrive,this.stepCredentials,this.stepSuccess],isEnabled:async()=>this.hasNativeUsb()}};cpDialogs={boardSelect:{closeable:!0,template:i=>html`

There are multiple boards are available. Select the board you have:

@@ -103,4 +103,4 @@

`:""} `,buttons:[this.closeButton]},error:{closeable:!0,template:e=>html`

Installation Error: ${e.message}

- `,buttons:[this.closeButton]}};getBoardName(e){return Object.keys(this.boardDefs).includes(e)?this.boardDefs[e].name:null}getBoardOptions(){var e,t=[];for(e of this.boardIds)t.push({id:e,name:this.getBoardName(e)});return t}async stepWelcome(){this.showDialog(this.dialogs.welcome,{boardName:this.boardName})}async stepSerialConnect(){this.showDialog(this.dialogs.espSerialConnect)}async stepConfirm(){this.showDialog(this.dialogs.confirm,{boardName:this.boardName})}async stepEraseAll(){this.showDialog(this.dialogs.actionWaiting,{action:"Erasing Flash"});try{await this.espStub.eraseFlash()}catch(e){this.errorMsg("Unable to finish erasing Flash memory. Please try again.")}await this.nextStep()}async stepFlashBin(){this.binFileUrl?(await this.downloadAndInstall(this.binFileUrl),await this.espHardReset(),await this.nextStep()):this.errorMsg("Missing bin file URL. Please make sure the installer button has this specified.")}async stepBootloader(){this.bootloaderUrl?(await this.downloadAndInstall(this.bootloaderUrl,"combined.bin",!0),await this.nextStep()):this.errorMsg("Missing bootloader file URL. Please make sure the installer button has this specified.")}async stepSelectBootDrive(){var e=await this.getBootDriveName();e&&this.logMsg("Waiting for user to select a bootloader volume named "+e),this.showDialog(this.dialogs.bootDriveSelect,{drivename:e||"Bootloader"})}async stepSelectCpyDrive(){this.logMsg("Waiting for user to select CIRCUITPY drive"),this.showDialog(this.dialogs.circuitpyDriveSelect)}async stepCopyUf2(){this.bootDriveHandle?(this.showDialog(this.dialogs.actionProgress,{action:"Copying "+this.uf2FileUrl}),await this.downloadAndCopy(this.uf2FileUrl),await this.nextStep()):this.errorMsg("No boot drive selected. stepSelectBootDrive should preceed this step.")}async stepSetupRepl(){var e=await this.getSerialPortName();let t=e?`There may be several devices listed, but look for one called something like ${e}.`:"There may be several devices listed. If you aren't sure which to choose, look for one that includes the name of your microcontroller.";this.showDialog(this.dialogs.cpSerial,{serialPortInstructions:t})}async stepCredentials(){this.tomlSettings=await this.getCurrentSettings(),console.log(this.tomlSettings);var e={wifi_ssid:this.getSetting("CIRCUITPY_WIFI_SSID"),wifi_password:this.getSetting("CIRCUITPY_WIFI_PASSWORD"),api_password:this.getSetting("CIRCUITPY_WEB_API_PASSWORD","passw0rd"),api_port:this.getSetting("CIRCUITPY_WEB_API_PORT",80)};this.hasNativeUsb(),this.showDialog(this.dialogs.credentials,e)}async stepSuccess(){let e={};this.repl&&(await this.repl.waitForPrompt(),this.currentFlow||this.currentFlow.steps.includes(this.stepCredentials))&&(e=await this.getDeviceHostInfo()),this.showDialog(this.dialogs.success,e)}async stepClose(){this.closeDialog()}async bootDriveSelectHandler(e){var t=await this.getBootDriveName();let i;try{i=await window.showDirectoryPicker({mode:"readwrite"})}catch(e){return}t&&t!=i.name?alert(`The selected drive named ${i.name} does not match the expected name of ${t}. Please select the correct drive.`):await this._verifyPermission(i)?(this.bootDriveHandle=i,await this.nextStep()):alert("Unable to write to the selected folder")}async circuitpyDriveSelectHandler(e){let t;try{t=await window.showDirectoryPicker({mode:"readwrite"})}catch(e){return}await this.getBootOut(t)?await this._verifyPermission(t)?(this.circuitpyDriveHandle=t,await this.nextStep()):alert("Unable to write to the selected folder"):alert("Expecting a folder with boot_out.txt. Please select the root folder of your CIRCUITPY drive.")}async espToolConnectHandler(e){await this.onReplDisconnected(e),await this.espDisconnect();let t;try{t=await this.espConnect({log:(...e)=>this.logMsg(...e),debug:()=>{},error:(...e)=>this.errorMsg(...e)})}catch(e){return void this.errorMsg("Unable to open Serial connection to board. Make sure the port is not already in use by another application or in another browser tab.")}try{this.updateEspConnected(this.connectionStates.CONNECTING),await t.initialize(),this.updateEspConnected(this.connectionStates.CONNECTED)}catch(e){return await t.disconnect(),this.updateEspConnected(this.connectionStates.DISCONNECTED),void this.errorMsg("Unable to connect to the board. Make sure it is in bootloader mode by holding the boot0 button when powering on and try again.")}try{this.logMsg("Connected to "+t.chipName),this.logMsg("MAC Address: "+this.formatMacAddr(t.macAddr())),FAMILY_TO_CHIP_MAP[this.chipFamily]==t.chipFamily?(this.logMsg("This chip checks out"),this.espStub=await t.runStub(),this.espStub.addEventListener("disconnect",()=>{this.updateEspConnected(this.connectionStates.DISCONNECTED),this.espStub=null}),await this.setBaudRateIfChipSupports(t.chipFamily,PREFERRED_BAUDRATE),await this.nextStep()):(this.errorMsg("Oops, this is the wrong firmware for your board."),await this.espDisconnect())}catch(e){await t.disconnect(),this.updateEspConnected(this.connectionStates.DISCONNECTED),this.errorMsg("Oops, we lost connection to your board before completing the install. Please check your USB connection and click Connect again. Refresh the browser if it becomes unresponsive.")}}async onSerialReceive(e){await this.repl.onSerialReceive(e)}async cpSerialConnectHandler(e){await this.espDisconnect(),await this.onReplDisconnected(e);try{this.replSerialDevice=await navigator.serial.requestPort()}catch(e){return}try{await this.replSerialDevice.open({baudRate:ESP_ROM_BAUD})}catch(e){console.error("Error. Unable to open Serial Port. Make sure it isn't already in use in another tab or application.")}await this.setupRepl(),this.nextStep()}async setupRepl(){this.replSerialDevice&&(this.repl=new REPL,this.repl.serialTransmit=this.serialTransmit.bind(this),this.replSerialDevice.addEventListener("message",this.onSerialReceive.bind(this)),this._readLoopPromise=this._readSerialLoop().catch(async function(e){await this.onReplDisconnected()}.bind(this)),this.replSerialDevice.writable)&&(this.writer=this.replSerialDevice.writable.getWriter(),await this.writer.ready)}async onReplDisconnected(e){if(this.reader){try{await this.reader.cancel()}catch(e){}this.reader=null}if(this.writer&&(await this.writer.releaseLock(),this.writer=null),this.replSerialDevice){try{await this.replSerialDevice.close()}catch(e){}this.replSerialDevice=null}}async buttonClickHandler(e,t=!1){!(1{var i=Math.round(e/t*100);i>s&&(a.value=i,this.logMsg(i+`% (${e}/${t})...`),s=i)},0,0)}catch(e){this.errorMsg(`Unable to flash file: ${t}. Error Message: `+e)}}}async downloadAndCopy(t,i=null){if(i=i||this.bootDriveHandle){var s,a=this.currentDialogElement.querySelector("#stepProgress"),[t,r]=(a.value=0,await this.downloadAndExtract(t)),o=await(await i.getFileHandle(t,{create:!0})).createWritable(),l=r.size;let e=0;for(;e{let e={};for(;0==Object.entries(e).length||null===e.ip;)e=await this.getDeviceHostInfo(),await this.sleep(300)},1e4)}catch(e){return console.warn("Unable to get IP Address. Network Credentials may be incorrect"),null}}else{if(!this.circuitpyDriveHandle)return this.errorMsg("Connect to the CIRCUITPY drive or the REPL first"),null;e=toml.stringify(e);await this.writeFile("settings.toml",e)}}async getCurrentSettings(){let e;if(this.repl)e=await this.runCode(["f = open('settings.toml', 'r')","print(f.read())","f.close()"]);else{if(!this.circuitpyDriveHandle)return this.errorMsg("Connect to the CIRCUITPY drive or the REPL first"),{};e=await this.readFile("settings.toml")}return e?toml.parse(e):(this.logMsg("Unable to read settings.toml from CircuitPython. It may not exist. Continuing..."),{})}async espDisconnect(){this.espStub&&(await this.espStub.disconnect(),this.espStub.removeEventListener("disconnect",this.espDisconnect.bind(this)),this.updateEspConnected(this.connectionStates.DISCONNECTED),this.espStub=null),this.port&&(await this.port.close(),this.port=null)}async serialTransmit(e){var t=new TextEncoder;this.writer&&(t=t.encode(e),await this.writer.ready.catch(e=>{this.errorMsg("Ready error: "+e)}),await this.writer.write(t).catch(e=>{this.errorMsg("Chunk error: "+e)}),await this.writer.ready)}async _readSerialLoop(){if(this.replSerialDevice){var e=new Event("message"),t=new TextDecoder;if(this.replSerialDevice.readable)for(this.reader=this.replSerialDevice.readable.getReader();;){var{value:i,done:s}=await this.reader.read();if(i&&(e.data=t.decode(i),this.replSerialDevice.dispatchEvent(e)),s){this.reader.releaseLock(),await this.onReplDisconnected();break}}this.logMsg("Read Loop Stopped. Closing Serial Port.")}}async getDeviceHostInfo(){return this.repl?{ip:this.repl.getIpAddress(),version:this.repl.getVersion()}:{}}hasNativeUsb(){return!(!this.chipFamily||"esp32c3".includes(this.chipFamily))}sleep(t){return new Promise(e=>setTimeout(e,t))}timeout(e,t){return Promise.race([e(),this.sleep(t).then(()=>{throw Error("Timed Out")})])}}customElements.define("cp-install-button",CPInstallButton,{extends:"button"});export{CPInstallButton}; \ No newline at end of file + `,buttons:[this.closeButton]}};getBoardName(e){return Object.keys(this.boardDefs).includes(e)?this.boardDefs[e].name:null}getBoardOptions(){var e,t=[];for(e of this.boardIds)t.push({id:e,name:this.getBoardName(e)});return t.sort((e,t)=>{e=e.name.trim().toLowerCase(),t=t.name.trim().toLowerCase();return ethis.logMsg(...e),debug:()=>{},error:(...e)=>this.errorMsg(...e)})}catch(e){return void this.errorMsg("Unable to open Serial connection to board. Make sure the port is not already in use by another application or in another browser tab.")}try{this.updateEspConnected(this.connectionStates.CONNECTING),await t.initialize(),this.updateEspConnected(this.connectionStates.CONNECTED)}catch(e){return await t.disconnect(),this.updateEspConnected(this.connectionStates.DISCONNECTED),void this.errorMsg("Unable to connect to the board. Make sure it is in bootloader mode by holding the boot0 button when powering on and try again.")}try{this.logMsg("Connected to "+t.chipName),this.logMsg("MAC Address: "+this.formatMacAddr(t.macAddr())),FAMILY_TO_CHIP_MAP[this.chipFamily]==t.chipFamily?(this.logMsg("This chip checks out"),this.espStub=await t.runStub(),this.espStub.addEventListener("disconnect",()=>{this.updateEspConnected(this.connectionStates.DISCONNECTED),this.espStub=null}),await this.setBaudRateIfChipSupports(t.chipFamily,PREFERRED_BAUDRATE),await this.nextStep()):(this.errorMsg("Oops, this is the wrong firmware for your board."),await this.espDisconnect())}catch(e){await t.disconnect(),this.updateEspConnected(this.connectionStates.DISCONNECTED),this.errorMsg("Oops, we lost connection to your board before completing the install. Please check your USB connection and click Connect again. Refresh the browser if it becomes unresponsive.")}}async onSerialReceive(e){await this.repl.onSerialReceive(e)}async cpSerialConnectHandler(e){await this.espDisconnect(),await this.onReplDisconnected(e);try{this.replSerialDevice=await navigator.serial.requestPort()}catch(e){return}try{await this.replSerialDevice.open({baudRate:ESP_ROM_BAUD})}catch(e){console.error("Error. Unable to open Serial Port. Make sure it isn't already in use in another tab or application.")}await this.setupRepl(),this.nextStep()}async setupRepl(){this.replSerialDevice&&(this.repl=new REPL,this.repl.serialTransmit=this.serialTransmit.bind(this),this.replSerialDevice.addEventListener("message",this.onSerialReceive.bind(this)),this._readLoopPromise=this._readSerialLoop().catch(async function(e){await this.onReplDisconnected()}.bind(this)),this.replSerialDevice.writable)&&(this.writer=this.replSerialDevice.writable.getWriter(),await this.writer.ready)}async onReplDisconnected(e){if(this.reader){try{await this.reader.cancel()}catch(e){}this.reader=null}if(this.writer&&(await this.writer.releaseLock(),this.writer=null),this.replSerialDevice){try{await this.replSerialDevice.close()}catch(e){}this.replSerialDevice=null}}async buttonClickHandler(e,t=!1){!(1{var i=Math.round(e/t*100);i>s&&(a.value=i,this.logMsg(i+`% (${e}/${t})...`),s=i)},0,0)}catch(e){this.errorMsg(`Unable to flash file: ${t}. Error Message: `+e)}}}async downloadAndCopy(t,i=null){if(i=i||this.bootDriveHandle){var s,a=this.currentDialogElement.querySelector("#stepProgress"),[t,r]=(a.value=0,await this.downloadAndExtract(t)),o=await(await i.getFileHandle(t,{create:!0})).createWritable(),l=r.size;let e=0;for(;e{let e={};for(;0==Object.entries(e).length||null===e.ip;)e=await this.getDeviceHostInfo(),await this.sleep(300)},1e4)}catch(e){return console.warn("Unable to get IP Address. Network Credentials may be incorrect"),null}}else{if(!this.circuitpyDriveHandle)return this.errorMsg("Connect to the CIRCUITPY drive or the REPL first"),null;e=toml.stringify(e);await this.writeFile("settings.toml",e)}}async getCurrentSettings(){let e;if(this.repl)e=await this.runCode(["f = open('settings.toml', 'r')","print(f.read())","f.close()"]);else{if(!this.circuitpyDriveHandle)return this.errorMsg("Connect to the CIRCUITPY drive or the REPL first"),{};e=await this.readFile("settings.toml")}return e?toml.parse(e):(this.logMsg("Unable to read settings.toml from CircuitPython. It may not exist. Continuing..."),{})}async espDisconnect(){this.espStub&&(await this.espStub.disconnect(),this.espStub.removeEventListener("disconnect",this.espDisconnect.bind(this)),this.updateEspConnected(this.connectionStates.DISCONNECTED),this.espStub=null),this.port&&(await this.port.close(),this.port=null)}async serialTransmit(e){var t=new TextEncoder;this.writer&&(t=t.encode(e),await this.writer.ready.catch(e=>{this.errorMsg("Ready error: "+e)}),await this.writer.write(t).catch(e=>{this.errorMsg("Chunk error: "+e)}),await this.writer.ready)}async _readSerialLoop(){if(this.replSerialDevice){var e=new Event("message"),t=new TextDecoder;if(this.replSerialDevice.readable)for(this.reader=this.replSerialDevice.readable.getReader();;){var{value:i,done:s}=await this.reader.read();if(i&&(e.data=t.decode(i),this.replSerialDevice.dispatchEvent(e)),s){this.reader.releaseLock(),await this.onReplDisconnected();break}}this.logMsg("Read Loop Stopped. Closing Serial Port.")}}async getDeviceHostInfo(){return this.repl?{ip:this.repl.getIpAddress(),version:this.repl.getVersion()}:{}}hasNativeUsb(){return!(!this.chipFamily||"esp32c3".includes(this.chipFamily))}sleep(t){return new Promise(e=>setTimeout(e,t))}timeout(e,t){return Promise.race([e(),this.sleep(t).then(()=>{throw Error("Timed Out")})])}}customElements.define("cp-install-button",CPInstallButton,{extends:"button"});export{CPInstallButton}; \ No newline at end of file