Add ESP32 web control and UART bridge.

Replace ESPNOW passthrough with a Microdot-based web UI and WebSocket-to-UART bridge for preset selection.

Made-with: Cursor
This commit is contained in:
2026-03-03 19:28:08 +13:00
parent 615431d6c5
commit 646b988cdd
16 changed files with 3225 additions and 30 deletions

View File

@@ -0,0 +1,145 @@
{% args buttons, device_id %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Led Hoop</title>
<link rel="stylesheet" href="static/styles.css" />
</head>
<body data-device-id="{{ device_id }}">
<div class="header">
<button class="menu-btn" type="button" onclick="toggleMenu()" aria-label="Menu">☰ Menu</button>
<div class="menu" id="menu">
<button class="menu-item off" type="button" onclick="sendSelect('off', null); closeMenu();">Off</button>
<button class="menu-item" type="button" onclick="sendSelect('test', null); closeMenu();">Test</button>
<button class="menu-item" type="button" onclick="sendSelect('calibration', null); closeMenu();">Calibration</button>
<button class="menu-item" type="button" onclick="closeMenu(); openNewButtonEditor();">Add button</button>
<button class="menu-item" type="button" onclick="closeMenu(); saveCurrentButtons();">Save</button>
</div>
</div>
<div class="buttons" id="buttonsContainer">
{% for btn in buttons %}
<button class="btn" type="button" data-preset="{{ btn['preset'] }}" data-id="{{ btn['id'] }}"
draggable="true">{{ btn['id'] }}</button>
{% endfor %}
</div>
<div class="preset-editor" id="buttonEditor">
<div class="preset-editor-inner">
<h2 class="preset-editor-title" id="buttonEditorTitle">Button</h2>
<label class="preset-editor-field">
<span>Button label</span>
<input id="be-label" type="text" placeholder="e.g. grab" />
</label>
<label class="preset-editor-field">
<span>Preset name</span>
<input id="be-preset" type="text" placeholder="e.g. grab" />
</label>
<label class="preset-editor-field">
<span>Pattern (p)</span>
<select id="be-pattern">
<option value="spin">spin</option>
<option value="roll">roll</option>
<option value="grab">grab</option>
<option value="lift">lift</option>
<option value="flare">flare</option>
<option value="hook">hook</option>
<option value="invertsplit">invertsplit</option>
<option value="pose">pose</option>
<option value="backbalance">backbalance</option>
<option value="beat">beat</option>
<option value="crouch">crouch</option>
<option value="backbendsplit">backbendsplit</option>
<option value="straddle">straddle</option>
<option value="frontbalance">frontbalance</option>
<option value="elbowhang">elbowhang</option>
<option value="elbowhangspin">elbowhangspin</option>
<option value="dismount">dismount</option>
<option value="fluff">fluff</option>
<option value="elbowhangsplit">elbowhangsplit</option>
<option value="invert">invert</option>
<option value="backbend">backbend</option>
<option value="seat">seat</option>
<option value="kneehang">kneehang</option>
<option value="legswoop">legswoop</option>
<option value="split">split</option>
<option value="foothang">foothang</option>
<option value="point">point</option>
<option value="off">off</option>
<option value="on">on</option>
<option value="blink">blink</option>
<option value="rainbow">rainbow</option>
<option value="pulse">pulse</option>
<option value="transition">transition</option>
<option value="chase">chase</option>
<option value="circle">circle</option>
<option value="calibration">calibration</option>
<option value="test">test</option>
</select>
</label>
<div class="preset-editor-row">
<label class="preset-editor-field">
<span>Delay (d)</span>
<input id="be-delay" type="number" inputmode="numeric" />
</label>
<label class="preset-editor-field">
<span>Brightness (b)</span>
<input id="be-brightness" type="number" inputmode="numeric" min="0" max="255" />
</label>
</div>
<label class="preset-editor-field">
<span>Colors (c)</span>
<input id="be-colors" type="text" placeholder="r,g,b; r,g,b (0255)" />
</label>
<div class="preset-editor-row">
<label class="preset-editor-field small">
<span>n1</span>
<input id="be-n1" type="number" inputmode="numeric" />
</label>
<label class="preset-editor-field small">
<span>n2</span>
<input id="be-n2" type="number" inputmode="numeric" />
</label>
</div>
<div class="preset-editor-row">
<label class="preset-editor-field small">
<span>n3</span>
<input id="be-n3" type="number" inputmode="numeric" />
</label>
<label class="preset-editor-field small">
<span>n4</span>
<input id="be-n4" type="number" inputmode="numeric" />
</label>
</div>
<div class="preset-editor-row">
<label class="preset-editor-field small">
<span>n5</span>
<input id="be-n5" type="number" inputmode="numeric" />
</label>
<label class="preset-editor-field small">
<span>n6</span>
<input id="be-n6" type="number" inputmode="numeric" />
</label>
</div>
<div class="preset-editor-row">
<label class="preset-editor-field small">
<span>n7</span>
<input id="be-n7" type="number" inputmode="numeric" />
</label>
<label class="preset-editor-field small">
<span>n8</span>
<input id="be-n8" type="number" inputmode="numeric" />
</label>
</div>
<div class="preset-editor-actions">
<button type="button" class="preset-editor-btn primary" onclick="saveButtonFromEditor()">Save</button>
<button type="button" class="preset-editor-btn" onclick="closeButtonEditor()">Cancel</button>
</div>
</div>
</div>
<button class="next-btn" type="button" onclick="nextPreset()">Next</button>
<script src="static/main.js"></script>
</body>
</html>