refactor(led-driver): simplify websocket runtime and test layout
This commit is contained in:
136
src/patterns/main.py
Normal file
136
src/patterns/main.py
Normal file
@@ -0,0 +1,136 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
from settings import Settings
|
||||
from machine import WDT
|
||||
import network
|
||||
import utime
|
||||
import asyncio
|
||||
from microdot import Microdot
|
||||
from microdot.websocket import WebSocketError, with_websocket
|
||||
from presets import Presets
|
||||
from controller_messages import process_data
|
||||
from hello import broadcast_hello_udp
|
||||
|
||||
settings = Settings()
|
||||
print(settings)
|
||||
|
||||
presets = Presets(settings["led_pin"], settings["num_leds"])
|
||||
presets.load(settings)
|
||||
presets.b = settings.get("brightness", 255)
|
||||
default_preset = settings.get("default", "")
|
||||
if default_preset and default_preset in presets.presets:
|
||||
if presets.select(default_preset):
|
||||
print(f"Selected startup preset: {default_preset}")
|
||||
else:
|
||||
print("Startup preset failed (invalid pattern?):", default_preset)
|
||||
|
||||
wdt = WDT(timeout=10000)
|
||||
wdt.feed()
|
||||
|
||||
sta_if = network.WLAN(network.STA_IF)
|
||||
sta_if.active(True)
|
||||
sta_if.config(pm=network.WLAN.PM_NONE)
|
||||
sta_if.connect(settings["ssid"], settings["password"])
|
||||
while not sta_if.isconnected():
|
||||
utime.sleep(1)
|
||||
wdt.feed()
|
||||
|
||||
app = Microdot()
|
||||
|
||||
|
||||
def _simulator_register_microdot_app():
|
||||
"""led-simulator sets LED_SIM_ROOT so Stop can shutdown() the same Microdot instance."""
|
||||
root = os.environ.get("LED_SIM_ROOT")
|
||||
if not root:
|
||||
return
|
||||
sys.path.insert(0, root)
|
||||
try:
|
||||
import led_driver_sim_hook as _sim_hook
|
||||
except ImportError:
|
||||
return
|
||||
_sim_hook.register_app(app)
|
||||
|
||||
|
||||
_simulator_register_microdot_app()
|
||||
|
||||
|
||||
@app.route("/ws")
|
||||
@with_websocket
|
||||
async def ws_handler(request, ws):
|
||||
print("WS client connected")
|
||||
try:
|
||||
while True:
|
||||
data = await ws.receive()
|
||||
if not data:
|
||||
print("WS client disconnected (closed)")
|
||||
break
|
||||
print(data)
|
||||
process_data(data, settings, presets)
|
||||
except WebSocketError as e:
|
||||
print("WS client disconnected:", e)
|
||||
except OSError as e:
|
||||
print("WS client dropped (OSError):", e)
|
||||
|
||||
|
||||
async def presets_loop():
|
||||
while True:
|
||||
await presets.tick()
|
||||
wdt.feed()
|
||||
# tick() does not await; yield so UDP hello and HTTP/WebSocket can run.
|
||||
await asyncio.sleep(0)
|
||||
|
||||
|
||||
async def _udp_hello_after_http_ready():
|
||||
"""Hello must run after the HTTP server binds, or discovery clients time out on /ws."""
|
||||
await asyncio.sleep(1)
|
||||
print("UDP hello: broadcasting…")
|
||||
try:
|
||||
broadcast_hello_udp(
|
||||
sta_if,
|
||||
settings.get("name", ""),
|
||||
wait_reply=False,
|
||||
wdt=wdt,
|
||||
dual_destinations=True,
|
||||
)
|
||||
except Exception as ex:
|
||||
print("UDP hello broadcast failed:", ex)
|
||||
|
||||
|
||||
async def main(port=80):
|
||||
t_presets = asyncio.create_task(presets_loop())
|
||||
t_hello = asyncio.create_task(_udp_hello_after_http_ready())
|
||||
try:
|
||||
await app.start_server(host="0.0.0.0", port=port)
|
||||
finally:
|
||||
for t in (t_presets, t_hello):
|
||||
t.cancel()
|
||||
try:
|
||||
await t
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
|
||||
def _simulator_apply_pattern_from_env():
|
||||
"""led-simulator sets LED_SIM_PATTERN to a patterns/ module name (no .py)."""
|
||||
mod = os.environ.get("LED_SIM_PATTERN", "").strip()
|
||||
if not mod:
|
||||
return
|
||||
presets.reload_patterns()
|
||||
presets.edit(
|
||||
"_sim",
|
||||
{
|
||||
"p": mod,
|
||||
"d": 200,
|
||||
"b": 255,
|
||||
"c": [(255, 0, 0), (0, 255, 0), (0, 0, 255)],
|
||||
},
|
||||
)
|
||||
if not presets.select("_sim"):
|
||||
print("LED_SIM_PATTERN: could not select pattern:", mod)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
_simulator_apply_pattern_from_env()
|
||||
_port = int(os.environ.get("LED_SIM_PORT", "80"))
|
||||
asyncio.run(main(port=_port))
|
||||
Reference in New Issue
Block a user