213 lines
9.0 KiB
Python
213 lines
9.0 KiB
Python
![]() |
import curses
|
||
|
from datetime import datetime, date, timedelta
|
||
|
import time
|
||
|
from threading import Thread
|
||
|
import sys
|
||
|
import os
|
||
|
from pathlib import Path
|
||
|
|
||
|
from alert_clients.alarm_api_client.alarm_api_client import AlarmApiClient
|
||
|
#from alarm_api_client.alarm_api_client import AlarmApiClient
|
||
|
from ncurses_ui import NcursesUI
|
||
|
from alert_logic import AlarmLogic
|
||
|
from ncurses_threads import NcursesThreads
|
||
|
from alarm_logger import AlarmLogger
|
||
|
|
||
|
class AlarmClock:
|
||
|
def __init__(self, stdscr):
|
||
|
# Initialize logging first
|
||
|
self.logger = AlarmLogger()
|
||
|
|
||
|
try:
|
||
|
self.stdscr = stdscr
|
||
|
self.api_client = AlarmApiClient("http://localhost:8000")
|
||
|
self.alarm_logic = AlarmLogic(self.api_client)
|
||
|
self.ncurses_ui = NcursesUI(stdscr)
|
||
|
self.ncurses_threads = NcursesThreads(self.alarm_logic)
|
||
|
|
||
|
self.alarms = []
|
||
|
self.running = True
|
||
|
self.selected_menu = 0
|
||
|
self.new_alarm_hour = 0
|
||
|
self.new_alarm_minute = 0
|
||
|
self.new_alarm_selected = 0
|
||
|
self.new_alarm_date = None
|
||
|
self.new_alarm_weekdays = []
|
||
|
self.weekday_names = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
||
|
self.current_alarm = None
|
||
|
self.current_alarm_process = False
|
||
|
self.snooze_minutes = 5
|
||
|
self.snooze_until = None
|
||
|
|
||
|
# Initialize curses
|
||
|
self.stdscr.keypad(1)
|
||
|
self.stdscr.timeout(100)
|
||
|
|
||
|
self.logger.log_system_error("Alarm Clock Initialized Successfully")
|
||
|
except Exception as e:
|
||
|
self.logger.log_system_error(f"Initialization Error: {str(e)}", exc_info=True)
|
||
|
raise
|
||
|
|
||
|
def show_error(self, message):
|
||
|
self.logger.log_system_error(message)
|
||
|
self.ncurses_ui.show_error(message)
|
||
|
|
||
|
def handle_add_alarm_input(self, key):
|
||
|
try:
|
||
|
if key == 27: # Escape
|
||
|
self.selected_menu = 0
|
||
|
self.logger.log_system_error("Add Alarm menu cancelled")
|
||
|
return
|
||
|
|
||
|
if key == 10: # Enter
|
||
|
try:
|
||
|
alarm_details = {
|
||
|
"hour": self.new_alarm_hour,
|
||
|
"minute": self.new_alarm_minute,
|
||
|
"date": self.new_alarm_date,
|
||
|
"weekdays": self.new_alarm_weekdays if self.new_alarm_weekdays else None
|
||
|
}
|
||
|
self.alarm_logic.create_alarm(
|
||
|
self.new_alarm_hour,
|
||
|
self.new_alarm_minute,
|
||
|
self.new_alarm_date,
|
||
|
self.new_alarm_weekdays if self.new_alarm_weekdays else None
|
||
|
)
|
||
|
self.logger.log_alarm_created(alarm_details)
|
||
|
self.alarms = self.api_client.get_alarms()
|
||
|
self.selected_menu = 0
|
||
|
except Exception as e:
|
||
|
self.show_error(f"Failed to create alarm: {str(e)}")
|
||
|
return
|
||
|
|
||
|
if key == curses.KEY_LEFT:
|
||
|
self.new_alarm_selected = (self.new_alarm_selected - 1) % 4
|
||
|
self.logger.log_system_error(f"Selected field changed to {self.new_alarm_selected}")
|
||
|
elif key == curses.KEY_RIGHT:
|
||
|
self.new_alarm_selected = (self.new_alarm_selected + 1) % 4
|
||
|
self.logger.log_system_error(f"Selected field changed to {self.new_alarm_selected}")
|
||
|
elif key == 32: # Space
|
||
|
if self.new_alarm_selected == 2: # Date
|
||
|
self.new_alarm_date = None
|
||
|
self.logger.log_system_error(f"Selected field changed to DATE")
|
||
|
elif self.new_alarm_selected == 3: # Weekdays
|
||
|
current_day = len(self.new_alarm_weekdays)
|
||
|
if current_day < 7:
|
||
|
if current_day in self.new_alarm_weekdays:
|
||
|
self.new_alarm_weekdays.remove(current_day)
|
||
|
else:
|
||
|
self.new_alarm_weekdays.append(current_day)
|
||
|
self.new_alarm_weekdays.sort()
|
||
|
|
||
|
elif key == curses.KEY_UP:
|
||
|
if self.new_alarm_selected == 0:
|
||
|
self.new_alarm_hour = (self.new_alarm_hour + 1) % 24
|
||
|
elif self.new_alarm_selected == 1:
|
||
|
self.new_alarm_minute = (self.new_alarm_minute + 1) % 60
|
||
|
elif self.new_alarm_selected == 2:
|
||
|
if not self.new_alarm_date:
|
||
|
self.new_alarm_date = date.today()
|
||
|
else:
|
||
|
self.new_alarm_date += timedelta(days=1)
|
||
|
|
||
|
elif key == curses.KEY_DOWN:
|
||
|
if self.new_alarm_selected == 0:
|
||
|
self.new_alarm_hour = (self.new_alarm_hour - 1) % 24
|
||
|
elif self.new_alarm_selected == 1:
|
||
|
self.new_alarm_minute = (self.new_alarm_minute - 1) % 60
|
||
|
elif self.new_alarm_selected == 2 and self.new_alarm_date:
|
||
|
self.new_alarm_date -= timedelta(days=1)
|
||
|
|
||
|
except Exception as e:
|
||
|
self.logger.log_system_error(f"Error in add alarm input: {str(e)}", exc_info=True)
|
||
|
|
||
|
def delete_selected_alarm(self):
|
||
|
try:
|
||
|
if self.alarms:
|
||
|
alarm = self.alarms[-1] # Get the last alarm
|
||
|
if self.api_client.delete_alarm(alarm["id"]):
|
||
|
self.logger.log_system_error(f"Deleted Alarm ID: {alarm['id']}")
|
||
|
self.alarm_logic.refresh_alarms()
|
||
|
else:
|
||
|
self.show_error("Failed to delete alarm")
|
||
|
except Exception as e:
|
||
|
self.logger.log_system_error(f"Delete alarm error: {str(e)}", exc_info=True)
|
||
|
self.show_error(f"Delete error: {str(e)}")
|
||
|
|
||
|
def handle_list_alarms_input(self, key):
|
||
|
try:
|
||
|
if key == 27: # Escape
|
||
|
self.selected_menu = 0
|
||
|
self.logger.log_system_error("List Alarms menu cancelled")
|
||
|
elif key == ord('d'):
|
||
|
self.logger.log_system_error("Attempting to delete last alarm")
|
||
|
self.delete_selected_alarm()
|
||
|
except Exception as e:
|
||
|
self.logger.log_system_error(f"Error in list alarms input: {str(e)}", exc_info=True)
|
||
|
|
||
|
def draw_main_clock(self):
|
||
|
self.ncurses_ui.draw_main_clock()
|
||
|
|
||
|
def draw_add_alarm(self):
|
||
|
self.ncurses_ui.draw_add_alarm()
|
||
|
|
||
|
def draw_list_alarms(self):
|
||
|
self.ncurses_ui.draw_list_alarms()
|
||
|
|
||
|
def run(self):
|
||
|
try:
|
||
|
# Start alarm checking thread
|
||
|
self.ncurses_threads.start_threads()
|
||
|
self.logger.log_system_error("Alarm checking threads started")
|
||
|
|
||
|
while self.running:
|
||
|
try:
|
||
|
# Clear and redraw the screen
|
||
|
self.stdscr.erase()
|
||
|
|
||
|
if self.selected_menu == 0:
|
||
|
self.draw_main_clock()
|
||
|
elif self.selected_menu == 1:
|
||
|
self.draw_add_alarm()
|
||
|
elif self.selected_menu == 2:
|
||
|
self.draw_list_alarms()
|
||
|
|
||
|
self.stdscr.refresh()
|
||
|
|
||
|
# Handle input with timeout
|
||
|
key = self.stdscr.getch()
|
||
|
if key != -1: # Key was pressed
|
||
|
if self.selected_menu == 0:
|
||
|
if key == ord('q'):
|
||
|
self.logger.log_system_error("Application quit requested")
|
||
|
self.alarm_logic.stop_alarm()
|
||
|
break
|
||
|
elif key == ord('a'):
|
||
|
self.selected_menu = 1
|
||
|
self.logger.log_system_error("Switched to Add Alarm menu")
|
||
|
elif key == ord('l'):
|
||
|
self.selected_menu = 2
|
||
|
self.logger.log_system_error("Switched to List Alarms menu")
|
||
|
elif key == ord('s'):
|
||
|
self.logger.log_system_error("Alarm stopped")
|
||
|
self.alarm_logic.stop_alarm()
|
||
|
elif key == ord('z'):
|
||
|
self.logger.log_alarm_snoozed(
|
||
|
"current_alarm",
|
||
|
self.snooze_minutes
|
||
|
)
|
||
|
self.alarm_logic.snooze_alarm()
|
||
|
elif self.selected_menu == 1:
|
||
|
self.handle_add_alarm_input(key)
|
||
|
elif self.selected_menu == 2:
|
||
|
self.handle_list_alarms_input(key)
|
||
|
|
||
|
except curses.error as e:
|
||
|
self.logger.log_system_error(f"Curses error: {str(e)}", exc_info=True)
|
||
|
self.running = False
|
||
|
raise Exception(f"Curses error: {str(e)}")
|
||
|
|
||
|
except Exception as e:
|
||
|
self.logger.log_system_error(f"Unhandled error in main run loop: {str(e)}", exc_info=True)
|
||
|
raise
|