Files
pi-pm3/app/backend/sse/events.py
michael 1da6730735 Add Dangerous Pi MVP implementation - complete backend and system integration
This commit adds the complete Dangerous Pi web management interface with all MVP features implemented and tested locally.

## New Features

### Backend (Python + FastAPI)
- Complete FastAPI backend with async support
- 40+ API endpoints (Health, PM3, WiFi, Updates, UPS, BLE, Plugins)
- 6 managers: Session, WiFi, Update, UPS, BLE, Plugin
- SQLite database with sessions, config, history, crash reports
- Server-Sent Events (SSE) for real-time notifications
- Mock PM3 worker for development without hardware

### WiFi Manager
- Interface detection (USB vs built-in)
- Network scanning with signal strength
- Mode switching (AP/Client/Dual/Auto/Off)
- Network connection with password support
- Hidden SSID and saved networks support
- Static IP and DHCP configuration
- 10 WiFi API endpoints

### Update Manager
- GitHub releases API integration
- Automatic periodic update checks
- Semantic version comparison
- Update download with progress tracking
- SHA256 checksum verification
- Automatic installation with backup and rollback
- PM3 client rebuild after updates
- 6 Update API endpoints

### UPS Manager
- I2C battery monitoring (MAX17040-compatible)
- Battery percentage, voltage, current tracking
- Power source detection (AC/Battery)
- Safe shutdown triggers at configurable thresholds
- Event callbacks for battery warnings
- SSE and BLE notification integration
- 3 UPS API endpoints

### BLE Manager
- Bluetooth Low Energy notification support
- Auto-detects BLE capability
- Multiple notification types (updates, battery, shutdown, etc.)
- BLE advertising management
- Device connection tracking
- 4 BLE API endpoints

### Plugin Framework
- Dynamic plugin loading/unloading
- Plugin lifecycle management (load, enable, disable, unload)
- Hook system for extensibility
- JSON-based metadata
- Example "Hello World" plugin included
- 7 Plugin API endpoints

### Frontend (Remix.js + React)
- Cyberpunk-themed responsive UI
- Dashboard with system status
- PM3 command interface with history
- Settings page with WiFi and Update management
- Command logs viewer
- Theme toggle (Dark/Light/Auto)
- Server-side rendering (SSR)
- Mobile-first responsive design

### System Integration
- Systemd service with security hardening
- Automated install/uninstall scripts
- Environment configuration template
- Hardware access groups (i2c, bluetooth, gpio, dialout)
- Pi-gen stage 04 integration for OS image building
- Port conflict resolution with ttyd-bash
- I2C interface auto-enable for UPS HAT

### Testing
- test_backend.py - Backend API tests
- test_ups.py - UPS manager tests
- test_ble.py - BLE manager tests
- test_plugins.py - Plugin manager tests
- All tests passing locally

### Documentation
- 12 comprehensive documentation files
- claude.md - AI development guide
- WIFI_MANAGER.md - WiFi management guide
- UPDATE_MANAGER.md - Update system guide
- PORT_CONFLICT.md - Port conflict resolution guide
- MVP_COMPLETE.md - MVP implementation summary
- PROJECT_STATUS.md - Project status and roadmap
- systemd/README.md - Service management docs
- pi-gen integration documentation

## Technical Details
- ~5,000+ lines of backend code
- 11 Python dependencies (smbus2 added for UPS)
- FastAPI with async/await throughout
- Type hints and docstrings on all functions
- RESTful API design with SSE for notifications
- Security hardening (non-root, protected dirs, resource limits)

## Next Steps
- Deploy to Raspberry Pi Zero 2 W hardware
- Test with real Proxmark3 device
- Test UPS HAT integration
- Test BLE on Pi hardware
- Build custom OS image with pi-gen
- Performance optimization for Pi Zero 2 W

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-26 08:11:36 -08:00

166 lines
4.9 KiB
Python

"""SSE event endpoints for real-time notifications."""
import asyncio
import json
from typing import AsyncGenerator
from fastapi import APIRouter
from sse_starlette.sse import EventSourceResponse
router = APIRouter()
# Global event queue for broadcasting events
event_queues: list[asyncio.Queue] = []
class EventBroadcaster:
"""Broadcasts events to all connected SSE clients."""
@staticmethod
async def broadcast(event_type: str, data: dict):
"""Broadcast an event to all connected clients.
Args:
event_type: Type of event (e.g., "update_available", "backup_complete")
data: Event data to send
"""
message = {
"type": event_type,
"data": data
}
# Send to all connected clients
dead_queues = []
for queue in event_queues:
try:
await queue.put(message)
except Exception:
# Queue is dead, mark for removal
dead_queues.append(queue)
# Remove dead queues
for dead_queue in dead_queues:
event_queues.remove(dead_queue)
broadcaster = EventBroadcaster()
async def event_generator() -> AsyncGenerator[dict, None]:
"""Generate SSE events for a client connection."""
# Create a new queue for this client
queue = asyncio.Queue()
event_queues.append(queue)
try:
# Send initial connection event
yield {
"event": "connected",
"data": json.dumps({"message": "Connected to Dangerous Pi event stream"})
}
# Keep connection alive and send events
while True:
try:
# Wait for events with timeout to send keep-alive
message = await asyncio.wait_for(queue.get(), timeout=30.0)
yield {
"event": message["type"],
"data": json.dumps(message["data"])
}
except asyncio.TimeoutError:
# Send keep-alive comment
yield {
"comment": "keep-alive"
}
except asyncio.CancelledError:
# Client disconnected
pass
finally:
# Remove queue when client disconnects
if queue in event_queues:
event_queues.remove(queue)
@router.get("/events")
async def stream_events():
"""SSE endpoint for streaming events to clients.
Events include:
- update_available: New version available on GitHub
- update_downloading: Update download in progress
- update_complete: Update downloaded and ready to install
- backup_complete: Backup operation completed
- ups_low_battery: UPS battery below threshold
- pm3_rebuild_required: PM3 client rebuild needed
- command_complete: Long-running command completed
"""
return EventSourceResponse(event_generator())
# Helper functions for sending specific event types
async def notify_update_available(version: str, url: str):
"""Notify clients that an update is available."""
await broadcaster.broadcast("update_available", {
"version": version,
"url": url
})
async def notify_update_progress(progress: float, status: str):
"""Notify clients of update download progress."""
await broadcaster.broadcast("update_downloading", {
"progress": progress,
"status": status
})
async def notify_backup_complete(backup_path: str, size_bytes: int):
"""Notify clients that backup is complete."""
await broadcaster.broadcast("backup_complete", {
"path": backup_path,
"size": size_bytes
})
async def notify_ups_battery(percentage: float, voltage: float):
"""Notify clients of UPS battery status."""
await broadcaster.broadcast("ups_battery", {
"percentage": percentage,
"voltage": voltage
})
async def notify_ups_warning(percentage: float, threshold: float):
"""Notify clients of low battery warning."""
await broadcaster.broadcast("ups_warning", {
"percentage": percentage,
"threshold": threshold,
"message": f"Battery low: {percentage}%"
})
async def notify_ups_critical(percentage: float):
"""Notify clients of critical battery level."""
await broadcaster.broadcast("ups_critical", {
"percentage": percentage,
"message": f"Battery critical: {percentage}%"
})
async def notify_ups_shutdown(delay: int, percentage: float):
"""Notify clients that shutdown has been initiated."""
await broadcaster.broadcast("ups_shutdown", {
"delay": delay,
"percentage": percentage,
"message": f"Shutdown initiated due to low battery ({percentage}%)"
})
async def notify_pm3_status(connected: bool, message: str):
"""Notify clients of PM3 status changes."""
await broadcaster.broadcast("pm3_status", {
"connected": connected,
"message": message
})