136 lines
3.6 KiB
Python
136 lines
3.6 KiB
Python
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)) |