Added event clearing and source tracking
- Added clear events endpoint that preserves latest gate status - Added source tracking (api/mqtt) for gate triggers - Added clear events button to frontend - Updated MQTT handler to use mqtt source
This commit is contained in:
parent
b372618cf7
commit
9ff21046d7
|
|
@ -251,7 +251,7 @@ app.add_middleware(
|
|||
)
|
||||
|
||||
# Gate control
|
||||
async def trigger_gate():
|
||||
async def trigger_gate(source: str = "api"):
|
||||
"""Trigger the gate relay"""
|
||||
try:
|
||||
settings = app.state.current_settings
|
||||
|
|
@ -268,7 +268,7 @@ async def trigger_gate():
|
|||
|
||||
# Log event
|
||||
timestamp = datetime.now().isoformat()
|
||||
await add_event(timestamp, "gate triggered", "api")
|
||||
await add_event(timestamp, "gate triggered", source)
|
||||
|
||||
# Wait for specified duration
|
||||
await asyncio.sleep(trigger_duration)
|
||||
|
|
@ -282,7 +282,7 @@ async def trigger_gate():
|
|||
logger.error(f"Failed to trigger gate: {e}", exc_info=True)
|
||||
# Log failure
|
||||
timestamp = datetime.now().isoformat()
|
||||
await add_event(timestamp, "gate trigger failed", "api", False)
|
||||
await add_event(timestamp, "gate trigger failed", source, False)
|
||||
return False
|
||||
|
||||
last_open_time = None
|
||||
|
|
@ -437,6 +437,10 @@ async def cleanup_old_events():
|
|||
"DELETE FROM events WHERE timestamp < ?",
|
||||
(cutoff_date,)
|
||||
)
|
||||
await db.execute(
|
||||
"DELETE FROM gate_status WHERE timestamp < ?",
|
||||
(cutoff_date,)
|
||||
)
|
||||
await db.commit()
|
||||
logger.info(f"Cleaned up {count} events older than {cutoff_date}")
|
||||
|
||||
|
|
@ -462,7 +466,7 @@ async def handle_mqtt_command(should_open: bool):
|
|||
|
||||
status_pin = settings.gpio.statusPin
|
||||
if should_open != (GPIO.input(status_pin) == GPIO.HIGH):
|
||||
await trigger_gate()
|
||||
await trigger_gate("mqtt")
|
||||
except Exception as e:
|
||||
logger.error(f"Error handling MQTT command: {e}")
|
||||
|
||||
|
|
@ -471,17 +475,40 @@ async def handle_mqtt_command(should_open: bool):
|
|||
async def trigger():
|
||||
"""Trigger the gate"""
|
||||
try:
|
||||
success = await trigger_gate()
|
||||
timestamp = datetime.now().isoformat()
|
||||
|
||||
# Get current status after trigger
|
||||
settings = app.state.current_settings or Settings()
|
||||
current_status = GPIO.input(settings.gpio.statusPin) == GPIO.HIGH
|
||||
|
||||
return {"success": success, "timestamp": timestamp, "isOpen": current_status}
|
||||
result = await trigger_gate("api")
|
||||
if result:
|
||||
status = await get_status()
|
||||
return {"success": True, "isOpen": status["isOpen"]}
|
||||
return {"success": False}
|
||||
except Exception as e:
|
||||
logger.error("Error triggering gate", exc_info=True)
|
||||
raise HTTPException(status_code=500, detail="Failed to trigger gate")
|
||||
logger.error(f"Failed to trigger gate: {e}", exc_info=True)
|
||||
return {"success": False}
|
||||
|
||||
@app.post("/api/events/clear")
|
||||
async def clear_events():
|
||||
"""Clear all events from the database except the latest gate status"""
|
||||
try:
|
||||
async with DBConnection() as db:
|
||||
# Delete all events
|
||||
await db.execute("DELETE FROM events")
|
||||
|
||||
# Keep only the most recent gate status
|
||||
await db.execute("""
|
||||
DELETE FROM gate_status
|
||||
WHERE timestamp NOT IN (
|
||||
SELECT timestamp
|
||||
FROM gate_status
|
||||
ORDER BY timestamp DESC
|
||||
LIMIT 1
|
||||
)
|
||||
""")
|
||||
await db.commit()
|
||||
|
||||
logger.info("Event logs cleared (preserved latest gate status)")
|
||||
return {"success": True}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to clear events: {e}", exc_info=True)
|
||||
return {"success": False}
|
||||
|
||||
@app.get("/api/status")
|
||||
async def get_status():
|
||||
|
|
@ -491,6 +518,8 @@ async def get_status():
|
|||
# HIGH (3.3V) means gate is open (receiving voltage)
|
||||
# LOW (0V) means gate is closed (no voltage)
|
||||
is_open = GPIO.input(settings.gpio.statusPin) == GPIO.HIGH
|
||||
|
||||
# Update state machine
|
||||
gate_status.update(is_open)
|
||||
|
||||
return {"isOpen": gate_status.is_open, "lastChanged": gate_status.last_changed}
|
||||
|
|
|
|||
|
|
@ -198,7 +198,29 @@ function App() {
|
|||
|
||||
{/* Recent Events */}
|
||||
<div className="mt-6">
|
||||
<h2 className="text-lg font-semibold mb-2">Recent Events</h2>
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<h2 className="text-lg font-semibold">Recent Events</h2>
|
||||
<button
|
||||
onClick={async () => {
|
||||
try {
|
||||
if (window.confirm('Are you sure you want to clear all events?')) {
|
||||
await api.clearEvents();
|
||||
// Reload events
|
||||
const eventsData = await api.getEvents();
|
||||
setEvents(eventsData.events);
|
||||
setHasMoreEvents(eventsData.hasMore);
|
||||
setTotalEvents(eventsData.total);
|
||||
}
|
||||
} catch (err) {
|
||||
setError('Failed to clear events');
|
||||
console.error(err);
|
||||
}
|
||||
}}
|
||||
className="text-sm text-red-600 hover:text-red-800"
|
||||
>
|
||||
Clear Events
|
||||
</button>
|
||||
</div>
|
||||
<div className="border rounded-lg divide-y divide-gray-200 max-h-96 overflow-y-auto">
|
||||
{events.map((event, index) => (
|
||||
<div key={index} className="px-3 py-2 hover:bg-gray-50 flex items-center justify-between text-sm">
|
||||
|
|
|
|||
|
|
@ -49,3 +49,13 @@ export async function updateSettings(settings: Settings): Promise<{ success: boo
|
|||
}
|
||||
return response.json();
|
||||
}
|
||||
|
||||
export async function clearEvents(): Promise<{ success: boolean }> {
|
||||
const response = await fetch(`${API_BASE}/events/clear`, {
|
||||
method: 'POST',
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to clear events');
|
||||
}
|
||||
return response.json();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue