Files
esp32-configs/static/index.md
T
2026-05-30 14:48:09 +08:00

9.9 KiB

About

ESPhome project by Shenzhen Athom Technology Co., Ltd., China.

Installation

Choose a firmware version and device, then connect it over USB to install the matching pre-built firmware.

Select firmware

The installer will use the selected version and device manifest.

<style> .installer { margin-top: 1.5rem; } .controls { display: grid; grid-template-columns: minmax(150px, 0.7fr) minmax(210px, 1fr) minmax(170px, 0.8fr); gap: 14px; margin-bottom: 18px; } .field { display: grid; gap: 6px; color: #333; font-weight: 700; } .field select, .field input { box-sizing: border-box; width: 100%; min-height: 42px; border: 1px solid #c6cbd1; border-radius: 6px; padding: 8px 10px; background: #fff; color: #222; font: inherit; } .field select:disabled, .field input:disabled { background: #f4f6f8; color: #68727d; cursor: not-allowed; } .device-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 10px; margin-bottom: 20px; } .device-card { min-height: 60px; border: 1px solid #d3d7dc; border-radius: 6px; padding: 10px 12px; background: #fff; color: #24292f; text-align: left; cursor: pointer; font: inherit; } .device-card:hover, .device-card:focus { border-color: #1e9fea; outline: none; } .device-card[aria-pressed="true"] { border-color: #1e9fea; box-shadow: inset 4px 0 0 #1e9fea; } .device-name { display: block; font-weight: 700; line-height: 1.25; } .device-meta { display: block; margin-top: 4px; color: #65717c; font-size: 0.82rem; line-height: 1.2; overflow-wrap: anywhere; } .empty { grid-column: 1 / -1; margin: 0; color: #65717c; } .install-box { display: flex; align-items: center; justify-content: space-between; gap: 20px; border-top: 1px solid #d8dde3; padding-top: 18px; } .install-box h3 { margin: 0 0 4px; color: #24292f; } .install-box p, .status { margin: 0; color: #4d5660; } .status { margin-top: 12px; min-height: 1.4em; } .installer[aria-disabled="true"] esp-web-install-button { opacity: 0.55; pointer-events: none; } @media (max-width: 760px) { .controls { grid-template-columns: 1fr; } .install-box { align-items: flex-start; flex-direction: column; } } </style> <script type="module" src="https://unpkg.com/esp-web-tools@10/dist/web/install-button.js?module"></script> <script> const state = { versions: [], selectedVersion: "", selectedDevice: "", filter: "", ready: false }; const installer = document.querySelector("#installer"); const versionSelect = document.querySelector("#version-select"); const deviceSelect = document.querySelector("#device-select"); const search = document.querySelector("#device-search"); const grid = document.querySelector("#device-grid"); const installButton = document.querySelector("#install-button"); const title = document.querySelector("#selected-title"); const detail = document.querySelector("#selected-detail"); const status = document.querySelector("#status"); function formatDeviceName(id) { return id .split("-") .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) .join(" "); } function normalizeDevice(device, version) { return { id: device.id, name: device.name || formatDeviceName(device.id), yaml: device.yaml || `${device.id}.yaml`, manifest: device.manifest || `firmware/${version}/${device.id}.manifest.json` }; } function normalizeVersion(version) { return { version: version.version, tag: version.tag || `esphome-${version.version}`, devices: (version.devices || []).map((device) => normalizeDevice(device, version.version)) }; } async function loadInstallerIndex() { try { const response = await fetch("firmware/versions.json", { cache: "no-store" }); if (!response.ok) { throw new Error(`versions.json returned ${response.status}`); } const data = await response.json(); state.versions = (data.versions || []).map(normalizeVersion); if (state.versions.length === 0) { throw new Error("versions.json does not include firmware versions"); } const latestVersion = state.versions.find((item) => item.version === data.latest); state.selectedVersion = latestVersion?.version || state.versions[0]?.version || ""; state.ready = true; status.textContent = `Loaded ${state.versions.length} firmware version${state.versions.length === 1 ? "" : "s"}; latest is ${state.selectedVersion}.`; } catch (error) { state.versions = []; state.selectedVersion = ""; state.selectedDevice = ""; state.ready = false; status.textContent = "Firmware version index is not available yet; installation is disabled."; } state.selectedDevice = getCurrentDevices()[0]?.id || ""; render(); } function getCurrentVersion() { return state.versions.find((item) => item.version === state.selectedVersion) || state.versions[0]; } function getCurrentDevices() { return getCurrentVersion()?.devices || []; } function getSelectedDevice() { return getCurrentDevices().find((device) => device.id === state.selectedDevice) || getCurrentDevices()[0]; } function getVisibleDevices() { const query = state.filter.trim().toLowerCase(); const devices = getCurrentDevices(); if (!query) { return devices; } return devices.filter((device) => `${device.name} ${device.id} ${device.yaml}`.toLowerCase().includes(query) ); } function chooseVersion(version) { state.selectedVersion = version; state.selectedDevice = getCurrentDevices()[0]?.id || ""; render(); } function chooseDevice(deviceId) { state.selectedDevice = deviceId; render(); } function renderVersionSelect() { versionSelect.replaceChildren(); for (const item of state.versions) { const option = document.createElement("option"); option.value = item.version; option.textContent = item.version; option.selected = item.version === state.selectedVersion; versionSelect.append(option); } versionSelect.value = state.selectedVersion; versionSelect.disabled = !state.ready; } function renderDeviceSelect() { deviceSelect.replaceChildren(); for (const device of getCurrentDevices()) { const option = document.createElement("option"); option.value = device.id; option.textContent = device.name; option.selected = device.id === state.selectedDevice; deviceSelect.append(option); } deviceSelect.value = state.selectedDevice; deviceSelect.disabled = !state.ready; } function renderGrid() { grid.replaceChildren(); const visibleDevices = getVisibleDevices(); if (visibleDevices.length === 0) { const empty = document.createElement("p"); empty.className = "empty"; empty.textContent = state.ready ? "No matching devices." : "Firmware index is not available."; grid.append(empty); return; } for (const device of visibleDevices) { const button = document.createElement("button"); button.type = "button"; button.className = "device-card"; button.setAttribute("aria-pressed", String(device.id === state.selectedDevice)); button.innerHTML = ''; button.querySelector(".device-name").textContent = device.name; button.querySelector(".device-meta").textContent = device.yaml; button.addEventListener("click", () => chooseDevice(device.id)); grid.append(button); } } function renderInstaller() { const version = getCurrentVersion(); const device = getSelectedDevice(); installer.setAttribute("aria-disabled", String(!state.ready)); search.disabled = !state.ready; if (!version || !device) { title.textContent = "No firmware available"; detail.textContent = "Firmware index is unavailable; installation is disabled."; installButton.removeAttribute("manifest"); installButton.setAttribute("disabled", ""); return; } title.textContent = `${device.name} - ${version.version}`; detail.textContent = `${device.yaml} -> ${device.manifest}`; installButton.setAttribute("manifest", device.manifest); installButton.removeAttribute("disabled"); } function render() { renderVersionSelect(); renderDeviceSelect(); renderGrid(); renderInstaller(); } versionSelect.addEventListener("change", (event) => chooseVersion(event.target.value)); deviceSelect.addEventListener("change", (event) => chooseDevice(event.target.value)); search.addEventListener("input", (event) => { state.filter = event.target.value; renderGrid(); }); loadInstallerIndex(); </script>