feat: add fire prevention management platform

This commit is contained in:
2026-08-12 17:35:03 +08:00
parent bb395e3d9a
commit 9a1788fe8f
6 changed files with 806 additions and 229 deletions

View File

@@ -12,9 +12,11 @@ from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
from PIL import Image, UnidentifiedImageError
from dotenv import load_dotenv
from pydantic import BaseModel
from ultralytics import YOLO
from .alerting import AlertManager
from .events import EVENT_STATUSES, EventStore
PROJECT_ROOT = Path(__file__).resolve().parents[1]
FRONTEND_DIR = PROJECT_ROOT / "frontend"
@@ -32,6 +34,11 @@ ALERT_MANAGER = AlertManager(
confirm_frames=int(os.getenv("ALERT_CONFIRM_FRAMES", "3")),
cooldown_seconds=float(os.getenv("ALERT_COOLDOWN_SECONDS", "60")),
)
EVENT_STORE = EventStore()
class EventStatusUpdate(BaseModel):
status: str
app = FastAPI(title="Smoke Fire Detector API", version="0.1.0")
app.add_middleware(
@@ -132,6 +139,16 @@ async def detect(
"notification_channels": ALERT_MANAGER.channels,
}
)
if result["alert"]["triggered"]:
result["event"] = EVENT_STORE.create(
session_id=session_id or "single-image",
classes=result["alert"]["classes"],
detections=result["detections"],
notification_channels=result["alert"].get(
"notification_channels",
ALERT_MANAGER.channels,
),
)
return result
@@ -141,6 +158,49 @@ def reset_detection_session(session_id: str) -> dict[str, str]:
return {"status": "reset"}
@app.get("/api/events")
def list_events(
limit: int = 100,
status: str | None = None,
) -> dict[str, Any]:
if status and status not in EVENT_STATUSES:
raise HTTPException(status_code=400, detail="Invalid event status")
return {
"events": EVENT_STORE.list(limit=limit, status=status),
"summary": EVENT_STORE.summary(),
}
@app.patch("/api/events/{event_id}")
def update_event(
event_id: str,
update: EventStatusUpdate,
) -> dict[str, Any]:
try:
event = EVENT_STORE.update(event_id, update.status)
except ValueError as error:
raise HTTPException(status_code=400, detail=str(error)) from error
if event is None:
raise HTTPException(status_code=404, detail="Event not found")
return event
@app.get("/api/dashboard")
def dashboard() -> dict[str, Any]:
return {
"summary": EVENT_STORE.summary(),
"recent_events": EVENT_STORE.list(limit=6),
"system": health(),
"detection": {
"confidence": CONFIDENCE,
"iou": IOU,
"image_size": IMAGE_SIZE,
"confirm_frames": ALERT_MANAGER.confirm_frames,
"cooldown_seconds": ALERT_MANAGER.cooldown_seconds,
},
}
@app.get("/")
def frontend() -> FileResponse:
return FileResponse(FRONTEND_DIR / "index.html")