Download a screen's firmware and put it on from your own computer (0.2.68)

GitHub issue #8: Home Assistant on a server or virtual machine is out of
reach of the screen. New screen and Firmware & USB get a Download choice:
ESP Screens builds the profile's firmware as always and offers ESPHome's
factory image, which the owner flashes with ESPHome Web in Chrome or Edge.

- Firmware: a download job compiles without uploading; only the image of
  the profile's latest successful build in this process is served, a new
  job withdraws it, and a build without a factory image fails.
- USB on the Home Assistant machine stays first in both lists, also before
  a board is plugged in; Download and Later stay chosen once picked.
- The not-yet-in-Home-Assistant card knows a downloaded screen.
- The device name pattern is valid again under Chrome's v flag.
- Docs: README, DOCS, EASY_SETUP, DOCKER, TROUBLESHOOTING. App only;
  firmware stays 0.2.58.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Max Gramser
2026-09-17 15:42:32 +02:00
parent 6809d3b941
commit 66b1590184
14 changed files with 588 additions and 66 deletions
+10 -2
View File
@@ -998,8 +998,9 @@ class Manager:
nodes = {s.get('node') for s in screens}
devices = {s.get('device') for s in screens}
installed = getattr(self.firmware, 'installed', set())
downloaded = getattr(self.firmware, 'downloaded', set())
return [{'file': file, 'node': meta['node'], 'friendly': meta.get('friendly') or meta['node'] or file,
'installed': file in installed, 'api_key': meta.get('api_key')}
'installed': file in installed, 'downloaded': file in downloaded, 'api_key': meta.get('api_key')}
for file, meta in profiles.items()
if meta.get('screen') and meta.get('node') not in nodes and (meta.get('friendly') or None) not in devices]
@@ -1639,8 +1640,14 @@ def create_app(manager, development=False):
raise ValueError('Invalid override data.')
return web.json_response(manager.firmware.save_override(request.match_info['file'], data.get('content')))
async def firmware_create(request):
# Profile, missing wifi secrets and (with a USB port) the build and flash in one request.
# Profile, missing wifi secrets and (with a USB port or the download) the build and flash in one request.
return web.json_response(manager.firmware.install(await request.json()))
async def firmware_download(request):
"""New screen and Firmware & USB → Download: the factory image this app just built, for ESPHome Web on
the owner's own computer. Like the profile it came from, it holds the Wi-Fi password and the screen's keys."""
path, name = manager.firmware.image(request.match_info['file'])
return web.FileResponse(path, headers={'Content-Type': 'application/octet-stream',
'Content-Disposition': f'attachment; filename="{name}"'})
async def shutdown(app):
for task in (manager.updates.task, manager.firmware.task):
if task and not task.done():
@@ -1655,6 +1662,7 @@ def create_app(manager, development=False):
app.router.add_post('/api/firmware/jobs', firmware_start)
app.router.add_get('/api/firmware/profiles/{file}/override', firmware_override)
app.router.add_put('/api/firmware/profiles/{file}/override', firmware_override_save)
app.router.add_get('/api/firmware/profiles/{file}/download', firmware_download)
app.router.add_post('/api/firmware/profiles', firmware_create)
app.router.add_get('/', index)
app.router.add_get('/api/inventory', inventory)