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))