127 lines
3.6 KiB
Python
127 lines
3.6 KiB
Python
"""UI main entry point."""
|
|
|
|
import logging
|
|
from pathlib import Path
|
|
|
|
from fastapi import FastAPI, Request
|
|
from fastapi.responses import HTMLResponse
|
|
from fastapi.staticfiles import StaticFiles
|
|
from fastapi.templating import Jinja2Templates
|
|
|
|
from apps.ui.api_client import fetch_devices, fetch_layout
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Initialize FastAPI app
|
|
app = FastAPI(
|
|
title="Home Automation UI",
|
|
description="User interface for home automation system",
|
|
version="0.1.0"
|
|
)
|
|
|
|
# Setup Jinja2 templates
|
|
templates_dir = Path(__file__).parent / "templates"
|
|
templates = Jinja2Templates(directory=str(templates_dir))
|
|
|
|
# Setup static files
|
|
static_dir = Path(__file__).parent / "static"
|
|
static_dir.mkdir(exist_ok=True)
|
|
app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")
|
|
|
|
|
|
@app.get("/", response_class=HTMLResponse)
|
|
async def index(request: Request) -> HTMLResponse:
|
|
"""Redirect to dashboard.
|
|
|
|
Args:
|
|
request: The FastAPI request object
|
|
|
|
Returns:
|
|
HTMLResponse: Rendered dashboard
|
|
"""
|
|
return await dashboard(request)
|
|
|
|
|
|
@app.get("/dashboard", response_class=HTMLResponse)
|
|
async def dashboard(request: Request) -> HTMLResponse:
|
|
"""Render the dashboard with rooms and devices.
|
|
|
|
Args:
|
|
request: The FastAPI request object
|
|
|
|
Returns:
|
|
HTMLResponse: Rendered dashboard template
|
|
"""
|
|
try:
|
|
# Load layout from API
|
|
layout_data = fetch_layout()
|
|
|
|
# Fetch devices from API (now includes features)
|
|
api_devices = fetch_devices()
|
|
|
|
# Create device lookup by device_id
|
|
device_map = {d["device_id"]: d for d in api_devices}
|
|
|
|
# Build rooms with merged device data
|
|
rooms = []
|
|
for room in layout_data.get("rooms", []):
|
|
devices = []
|
|
for tile in room.get("devices", []):
|
|
# Merge tile data with API device data
|
|
device_data = {
|
|
"device_id": tile["device_id"],
|
|
"title": tile["title"],
|
|
"icon": tile["icon"],
|
|
"rank": tile["rank"],
|
|
}
|
|
|
|
# Add type, name, and features from API if available
|
|
if tile["device_id"] in device_map:
|
|
api_device = device_map[tile["device_id"]]
|
|
device_data["type"] = api_device.get("type")
|
|
device_data["name"] = api_device.get("name")
|
|
device_data["features"] = api_device.get("features", {})
|
|
else:
|
|
device_data["features"] = {}
|
|
|
|
devices.append(device_data)
|
|
|
|
# Sort devices by rank (ascending)
|
|
devices.sort(key=lambda d: d["rank"])
|
|
|
|
rooms.append({
|
|
"name": room["name"],
|
|
"devices": devices
|
|
})
|
|
|
|
logger.info(f"Rendering dashboard with {len(rooms)} rooms")
|
|
|
|
return templates.TemplateResponse("dashboard.html", {
|
|
"request": request,
|
|
"rooms": rooms
|
|
})
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error rendering dashboard: {e}", exc_info=True)
|
|
# Fallback to empty dashboard
|
|
return templates.TemplateResponse("dashboard.html", {
|
|
"request": request,
|
|
"rooms": []
|
|
})
|
|
|
|
|
|
def main() -> None:
|
|
"""Run the UI application with uvicorn."""
|
|
import uvicorn
|
|
|
|
uvicorn.run(
|
|
"apps.ui.main:app",
|
|
host="0.0.0.0",
|
|
port=8002,
|
|
reload=True
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|