Files
led-controller/run_web.py

153 lines
4.6 KiB
Python

#!/usr/bin/env python3
"""
Local development web server - imports and runs main.py with port 5000
"""
import sys
import os
import asyncio
# Add src and lib to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'lib'))
# Import the main module
from src import main as main_module
# Override the port in the main function
async def run_local():
"""Run main with port 5000 for local development."""
from settings import Settings
import gc
# Mock MicroPython modules for local development
class MockMachine:
class WDT:
def __init__(self, timeout):
pass
def feed(self):
pass
import sys as sys_module
sys_module.modules['machine'] = MockMachine()
class MockESPNow:
def __init__(self):
self.active_value = False
self.peers = []
def active(self, value):
self.active_value = value
print(f"[MOCK] ESPNow active: {value}")
def add_peer(self, peer):
self.peers.append(peer)
print(f"[MOCK] Added peer: {peer.hex() if hasattr(peer, 'hex') else peer}")
async def asend(self, peer, data):
print(f"[MOCK] Would send to {peer.hex() if hasattr(peer, 'hex') else peer}: {data}")
class MockAIOESPNow:
def __init__(self):
pass
def active(self, value):
return MockESPNow()
def add_peer(self, peer):
pass
class MockNetwork:
class WLAN:
def __init__(self, interface):
self.interface = interface
def active(self, value):
print(f"[MOCK] WLAN({self.interface}) active: {value}")
STA_IF = 0
# Replace MicroPython modules with mocks
sys_module.modules['aioespnow'] = type('module', (), {'AIOESPNow': MockESPNow})()
sys_module.modules['network'] = MockNetwork()
# Mock gc if needed
if not hasattr(gc, 'collect'):
class MockGC:
def collect(self):
pass
gc = MockGC()
settings = Settings()
print("Starting LED Controller Web Server (Local Development)")
print("=" * 60)
# Mock network
import network
network.WLAN(network.STA_IF).active(True)
# Mock ESPNow
import aioespnow
e = aioespnow.AIOESPNow()
e.active(True)
e.add_peer(b"\xbb\xbb\xbb\xbb\xbb\xbb")
from microdot import Microdot, send_file
from microdot.websocket import with_websocket
import controllers.preset as preset
import controllers.profile as profile
import controllers.group as group
import controllers.sequence as sequence
import controllers.tab as tab
import controllers.palette as palette
import controllers.scene as scene
app = Microdot()
# Mount model controllers as subroutes
app.mount(preset.controller, '/presets')
app.mount(profile.controller, '/profiles')
app.mount(group.controller, '/groups')
app.mount(sequence.controller, '/sequences')
app.mount(tab.controller, '/tabs')
app.mount(palette.controller, '/palettes')
app.mount(scene.controller, '/scenes')
# Serve index.html at root
@app.route('/')
def index(request):
"""Serve the main web UI."""
return send_file('src/templates/index.html')
# Static file route
@app.route("/static/<path:path>")
def static_handler(request, path):
"""Serve static files."""
if '..' in path:
return 'Not found', 404
return send_file('src/static/' + path)
@app.route('/ws')
@with_websocket
async def ws(request, ws):
while True:
data = await ws.receive()
if data:
await e.asend(b"\xbb\xbb\xbb\xbb\xbb\xbb", data)
print(data)
else:
break
# Use port 5000 for local development
port = 5000
print(f"Starting server on http://0.0.0.0:{port}")
print(f"Open http://localhost:{port} in your browser")
print("=" * 60)
try:
await app.start_server(host="0.0.0.0", port=port, debug=True)
except KeyboardInterrupt:
print("\nShutting down server...")
if __name__ == '__main__':
# Change to project root
os.chdir(os.path.dirname(os.path.abspath(__file__)))
# Override settings path for local development
import settings as settings_module
settings_module.Settings.SETTINGS_FILE = os.path.join(os.getcwd(), 'settings.json')
asyncio.run(run_local())