167 lines
6.7 KiB
Python
167 lines
6.7 KiB
Python
import curses
|
|
import os
|
|
from datetime import datetime
|
|
from big_digits import BIG_DIGITS
|
|
|
|
class NcursesUI:
|
|
def __init__(self, stdscr):
|
|
# Initialize curses
|
|
curses.start_color()
|
|
curses.init_pair(1, curses.COLOR_GREEN, curses.COLOR_BLACK)
|
|
curses.init_pair(2, curses.COLOR_YELLOW, curses.COLOR_BLACK)
|
|
curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK)
|
|
curses.curs_set(0)
|
|
self.stdscr.keypad(1)
|
|
self.stdscr.timeout(100)
|
|
|
|
def show_error(self, message):
|
|
height, width = self.stdscr.getmaxyx()
|
|
self.stdscr.attron(curses.color_pair(3))
|
|
self.stdscr.addstr(height-1, 0, f"ERROR: {message}"[:width-1])
|
|
self.stdscr.attroff(curses.color_pair(3))
|
|
self.stdscr.refresh()
|
|
time.sleep(2)
|
|
|
|
def draw_big_digit(self, y, x, digit):
|
|
patterns = BIG_DIGITS[digit]
|
|
for i, line in enumerate(patterns):
|
|
self.stdscr.addstr(y + i, x, line)
|
|
|
|
def draw_big_time(self, current_time):
|
|
height, width = self.stdscr.getmaxyx()
|
|
time_str = current_time.strftime("%H:%M:%S")
|
|
|
|
# Calculate starting position to center the big clock
|
|
digit_width = 14 # Width of each digit pattern including spacing
|
|
total_width = digit_width * len(time_str)
|
|
start_x = (width - total_width) // 2
|
|
start_y = (height - 7) // 2 - 4 # Move up a bit to make room for date
|
|
|
|
self.stdscr.attron(curses.color_pair(1))
|
|
for i, digit in enumerate(time_str):
|
|
self.draw_big_digit(start_y, start_x + i * digit_width, digit)
|
|
self.stdscr.attroff(curses.color_pair(1))
|
|
|
|
def draw_main_clock(self):
|
|
current_time = datetime.now()
|
|
time_str = current_time.strftime("%H:%M:%S")
|
|
date_str = current_time.strftime("%Y-%m-%d")
|
|
|
|
# Get terminal dimensions
|
|
height, width = self.stdscr.getmaxyx()
|
|
|
|
# Draw big time
|
|
self.draw_big_time(current_time)
|
|
|
|
# Draw date
|
|
date_x = width // 2 - len(date_str) // 2
|
|
date_y = height // 2 + 4 # Below the big clock
|
|
|
|
self.stdscr.attron(curses.color_pair(2))
|
|
self.stdscr.addstr(date_y, date_x, date_str)
|
|
self.stdscr.attroff(curses.color_pair(2))
|
|
|
|
# Draw menu options
|
|
menu_str = "A: Add Alarm L: List Alarms S: Stop Z: Snooze Q: Quit"
|
|
menu_x = width // 2 - len(menu_str) // 2
|
|
self.stdscr.addstr(height - 2, menu_x, menu_str)
|
|
|
|
# Show alarm/snooze status
|
|
if self.current_alarm_process:
|
|
status_str = "⏰ ALARM ACTIVE - Press 'S' to stop or 'Z' to snooze"
|
|
status_x = width // 2 - len(status_str) // 2
|
|
self.stdscr.attron(curses.color_pair(3))
|
|
self.stdscr.addstr(height - 4, status_x, status_str)
|
|
self.stdscr.attroff(curses.color_pair(3))
|
|
elif self.snooze_until:
|
|
snooze_str = f"💤 Snoozed until {self.snooze_until.strftime('%H:%M')}"
|
|
snooze_x = width // 2 - len(snooze_str) // 2
|
|
self.stdscr.attron(curses.color_pair(2))
|
|
self.stdscr.addstr(height - 4, snooze_x, snooze_str)
|
|
self.stdscr.attroff(curses.color_pair(2))
|
|
|
|
def draw_add_alarm(self):
|
|
height, width = self.stdscr.getmaxyx()
|
|
|
|
form_y = height // 2 - 3
|
|
self.stdscr.addstr(form_y, width // 2 - 10, "New Alarm")
|
|
|
|
# Time selection
|
|
hour_str = f"{self.new_alarm_hour:02d}"
|
|
minute_str = f"{self.new_alarm_minute:02d}"
|
|
|
|
# Highlight selected field
|
|
if self.new_alarm_selected == 0:
|
|
self.stdscr.attron(curses.color_pair(2))
|
|
self.stdscr.addstr(form_y + 1, width // 2 - 2, hour_str)
|
|
if self.new_alarm_selected == 0:
|
|
self.stdscr.attroff(curses.color_pair(2))
|
|
|
|
self.stdscr.addstr(form_y + 1, width // 2, ":")
|
|
|
|
if self.new_alarm_selected == 1:
|
|
self.stdscr.attron(curses.color_pair(2))
|
|
self.stdscr.addstr(form_y + 1, width // 2 + 1, minute_str)
|
|
if self.new_alarm_selected == 1:
|
|
self.stdscr.attroff(curses.color_pair(2))
|
|
|
|
# Date selection
|
|
date_str = "No specific date" if not self.new_alarm_date else self.new_alarm_date.strftime("%Y-%m-%d")
|
|
if self.new_alarm_selected == 2:
|
|
self.stdscr.attron(curses.color_pair(2))
|
|
self.stdscr.addstr(form_y + 3, width // 2 - len(date_str) // 2, date_str)
|
|
if self.new_alarm_selected == 2:
|
|
self.stdscr.attroff(curses.color_pair(2))
|
|
|
|
# Weekday selection
|
|
weekday_str = "Repeat: " + " ".join(
|
|
self.weekday_names[i] if i in self.new_alarm_weekdays else "___"
|
|
for i in range(7)
|
|
)
|
|
if self.new_alarm_selected == 3:
|
|
self.stdscr.attron(curses.color_pair(2))
|
|
self.stdscr.addstr(form_y + 4, width // 2 - len(weekday_str) // 2, weekday_str)
|
|
if self.new_alarm_selected == 3:
|
|
self.stdscr.attroff(curses.color_pair(2))
|
|
|
|
# Instructions
|
|
if self.new_alarm_selected < 2:
|
|
self.stdscr.addstr(height - 2, 2, "↑↓: Change value ←→: Switch field Enter: Save Esc: Cancel")
|
|
elif self.new_alarm_selected == 2:
|
|
self.stdscr.addstr(height - 2, 2, "↑↓: Change date Space: Clear date Enter: Save Esc: Cancel")
|
|
else: # weekday selection
|
|
self.stdscr.addstr(height - 2, 2, "←→: Select day Space: Toggle Enter: Save Esc: Cancel")
|
|
|
|
def draw_list_alarms(self):
|
|
height, width = self.stdscr.getmaxyx()
|
|
|
|
self.stdscr.addstr(2, width // 2 - 5, "Alarms")
|
|
|
|
try:
|
|
self.alarms = self.api_client.get_alarms()
|
|
for i, alarm in enumerate(self.alarms):
|
|
# Parse time from the time string
|
|
time_parts = alarm['time'].split(':')
|
|
time_str = f"{time_parts[0]}:{time_parts[1]}"
|
|
|
|
# Add repeat rule information
|
|
if 'repeat_rule' in alarm:
|
|
if alarm['repeat_rule']['type'] == 'once' and 'date' in alarm['repeat_rule']:
|
|
time_str += f" on {alarm['repeat_rule']['date']}"
|
|
elif alarm['repeat_rule']['type'] == 'weekly' and 'days' in alarm['repeat_rule']:
|
|
weekdays = [self.weekday_names[d] for d in alarm['repeat_rule']['days']]
|
|
time_str += f" every {', '.join(weekdays)}"
|
|
|
|
status = "✓" if alarm['enabled'] else "✗"
|
|
display_str = f"{status} {time_str}"
|
|
|
|
self.stdscr.addstr(4 + i, width // 2 - len(display_str) // 2, display_str)
|
|
|
|
if not self.alarms:
|
|
self.stdscr.addstr(4, width // 2 - 7, "No alarms set")
|
|
|
|
except Exception as e:
|
|
self.show_error(f"Failed to display alarms: {str(e)}")
|
|
|
|
self.stdscr.addstr(height - 2, 2, "D: Delete alarm Esc: Back")
|