Compare commits
3 Commits
8127789b08
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 00592b5e3a | |||
| 8b8d30da27 | |||
| 4f5473e0e1 |
@@ -1,7 +1,7 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from datetime import datetime
|
from datetime import datetime, time
|
||||||
import logging
|
import logging
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
@@ -58,14 +58,24 @@ def draw_clock_digits(x, y, size ,hours, minutes, blink_second, color=(0, 255, 0
|
|||||||
|
|
||||||
draw_digits(x, y, size, current_time, color)
|
draw_digits(x, y, size, current_time, color)
|
||||||
|
|
||||||
def draw_digits(x, y, size, digits, color=(0, 255, 0)):
|
def draw_digits(x, y, size, digits, color=(0, 255, 0), anchor='la'):
|
||||||
"""
|
"""
|
||||||
Draws clock digits at the specified position.
|
Draws clock digits at the specified position.
|
||||||
"""
|
"""
|
||||||
font_path = "/usr/share/fonts/truetype/dseg/DSEG14Classic-BoldItalic.ttf"
|
font_path = "/usr/share/fonts/truetype/dseg/DSEG14Classic-BoldItalic.ttf"
|
||||||
font = ImageFont.truetype(font_path, int(CLOCK_HEIGHT_PIXELS * size))
|
font = ImageFont.truetype(font_path, int(CLOCK_HEIGHT_PIXELS * size))
|
||||||
|
|
||||||
draw.text((x, y), digits, font=font, fill=color)
|
draw.text((x, y), digits, font=font, fill=color, anchor=anchor)
|
||||||
|
|
||||||
|
def status_clock_position(order):
|
||||||
|
"""
|
||||||
|
Computes the x, y position of the status clock at the given stacking order (0-based).
|
||||||
|
"""
|
||||||
|
x = SCREEN_WIDTH_PIXELS - CLOCK_WIDTH_PIXELS * STATUS_SIZE_MULTIPLIER - MARGIN_PIXELS
|
||||||
|
slot_height = CLOCK_HEIGHT_PIXELS * STATUS_SIZE_MULTIPLIER + 2 * MARGIN_PIXELS
|
||||||
|
y = (MARGIN_PIXELS + CLOCK_HEIGHT_PIXELS * CLOCK_SIZE_MULTIPLIER + 2 * MARGIN_PIXELS
|
||||||
|
+ order * slot_height)
|
||||||
|
return x, y
|
||||||
|
|
||||||
def draw_clock():
|
def draw_clock():
|
||||||
x = SCREEN_WIDTH_PIXELS - CLOCK_WIDTH_PIXELS * CLOCK_SIZE_MULTIPLIER - MARGIN_PIXELS
|
x = SCREEN_WIDTH_PIXELS - CLOCK_WIDTH_PIXELS * CLOCK_SIZE_MULTIPLIER - MARGIN_PIXELS
|
||||||
@@ -78,22 +88,81 @@ def draw_clock():
|
|||||||
#draw_label('Time:', x - MARGIN_PIXELS, y + CLOCK_HEIGHT_PIXELS)
|
#draw_label('Time:', x - MARGIN_PIXELS, y + CLOCK_HEIGHT_PIXELS)
|
||||||
|
|
||||||
|
|
||||||
def draw_death_clock():
|
def draw_countdown(x, y, target_dt, size=STATUS_SIZE_MULTIPLIER):
|
||||||
x = SCREEN_WIDTH_PIXELS - CLOCK_WIDTH_PIXELS * STATUS_SIZE_MULTIPLIER - MARGIN_PIXELS
|
"""
|
||||||
y = MARGIN_PIXELS + CLOCK_HEIGHT_PIXELS * CLOCK_SIZE_MULTIPLIER + 2 * MARGIN_PIXELS
|
Draws a countdown/countup to target_dt: green if more than 15 minutes away,
|
||||||
|
yellow if less than 15 minutes away, red if target_dt is in the past.
|
||||||
death_date = datetime.strptime("08/23/2026", "%m/%d/%Y").date()
|
"""
|
||||||
today = datetime.today().date()
|
now = datetime.now()
|
||||||
days_until_death = (death_date - today).days
|
delta_seconds = (target_dt - now).total_seconds()
|
||||||
|
if delta_seconds >= 0:
|
||||||
color = (0, 255, 0)
|
color = (255, 255, 0) if delta_seconds < 15 * 60 else (0, 255, 0)
|
||||||
|
else:
|
||||||
draw_digits(x, y, STATUS_SIZE_MULTIPLIER, f'{days_until_death:4}', color)
|
color = (255, 0, 0)
|
||||||
draw_label('death:', x - MARGIN_PIXELS, y + CLOCK_HEIGHT_PIXELS * STATUS_SIZE_MULTIPLIER)
|
|
||||||
|
|
||||||
def draw_journal_minutes(status):
|
total = abs(int(delta_seconds))
|
||||||
x = SCREEN_WIDTH_PIXELS - CLOCK_WIDTH_PIXELS * STATUS_SIZE_MULTIPLIER - MARGIN_PIXELS
|
hours, remainder = divmod(total, 3600)
|
||||||
y = MARGIN_PIXELS + CLOCK_HEIGHT_PIXELS * CLOCK_SIZE_MULTIPLIER + CLOCK_HEIGHT_PIXELS * STATUS_SIZE_MULTIPLIER + 4 * MARGIN_PIXELS
|
minutes = remainder // 60
|
||||||
|
|
||||||
|
draw_clock_digits(x, y, size, f'{hours:02}', f'{minutes:02}', 1, color)
|
||||||
|
|
||||||
|
|
||||||
|
WORKDAY_EVENTS = [
|
||||||
|
(7, 0, 'Wake up'),
|
||||||
|
(8, 0, 'Start working'),
|
||||||
|
(11, 0, 'Scrum meeting'),
|
||||||
|
(14, 0, 'Physiotherapy'),
|
||||||
|
(15, 0, 'Lunch'),
|
||||||
|
(18, 0, 'Close work day'),
|
||||||
|
(18, 30, 'Evening walk'),
|
||||||
|
(22, 0, 'Close the day'),
|
||||||
|
(23, 0, 'Go to sleep'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def next_workday_event(now=None):
|
||||||
|
"""
|
||||||
|
Returns (target_dt, label) for the next event in WORKDAY_EVENTS, or None on weekends.
|
||||||
|
Once past the last event of the day, keeps returning it (now overdue) until midnight.
|
||||||
|
"""
|
||||||
|
now = now or datetime.now()
|
||||||
|
if now.weekday() >= 5:
|
||||||
|
return None
|
||||||
|
|
||||||
|
today = now.date()
|
||||||
|
for hour, minute, label in WORKDAY_EVENTS:
|
||||||
|
dt = datetime.combine(today, time(hour, minute))
|
||||||
|
if dt > now:
|
||||||
|
return dt, label
|
||||||
|
|
||||||
|
last_hour, last_minute, last_label = WORKDAY_EVENTS[-1]
|
||||||
|
return datetime.combine(today, time(last_hour, last_minute)), last_label
|
||||||
|
|
||||||
|
|
||||||
|
def draw_next_event(order):
|
||||||
|
result = next_workday_event()
|
||||||
|
if result is None:
|
||||||
|
return
|
||||||
|
target, label = result
|
||||||
|
x, y = status_clock_position(order)
|
||||||
|
draw_countdown(x, y, target, STATUS_SIZE_MULTIPLIER)
|
||||||
|
draw_label(f'{label}:', x - MARGIN_PIXELS, y + CLOCK_HEIGHT_PIXELS * STATUS_SIZE_MULTIPLIER)
|
||||||
|
|
||||||
|
|
||||||
|
def milestone_countdown(order, target_date, label):
|
||||||
|
x, y = status_clock_position(order)
|
||||||
|
right_x = x + CLOCK_WIDTH_PIXELS * STATUS_SIZE_MULTIPLIER
|
||||||
|
|
||||||
|
today = datetime.today().date()
|
||||||
|
days_until = (target_date - today).days
|
||||||
|
|
||||||
|
color = (0, 255, 0)
|
||||||
|
|
||||||
|
draw_digits(right_x, y, STATUS_SIZE_MULTIPLIER, f'{days_until}', color, anchor='ra')
|
||||||
|
draw_label(label, x - MARGIN_PIXELS, y + CLOCK_HEIGHT_PIXELS * STATUS_SIZE_MULTIPLIER)
|
||||||
|
|
||||||
|
def draw_journal_minutes(status, order):
|
||||||
|
x, y = status_clock_position(order)
|
||||||
|
|
||||||
elapsed_minutes = status['journal_updated_minutes']
|
elapsed_minutes = status['journal_updated_minutes']
|
||||||
|
|
||||||
@@ -111,31 +180,17 @@ def draw_journal_minutes(status):
|
|||||||
draw_label('Journal:', x - MARGIN_PIXELS, y + CLOCK_HEIGHT_PIXELS * STATUS_SIZE_MULTIPLIER)
|
draw_label('Journal:', x - MARGIN_PIXELS, y + CLOCK_HEIGHT_PIXELS * STATUS_SIZE_MULTIPLIER)
|
||||||
|
|
||||||
|
|
||||||
def draw_fasting(status):
|
def draw_fasting(status, order):
|
||||||
x = SCREEN_WIDTH_PIXELS - CLOCK_WIDTH_PIXELS * STATUS_SIZE_MULTIPLIER - MARGIN_PIXELS
|
x, y = status_clock_position(order)
|
||||||
y = MARGIN_PIXELS + CLOCK_HEIGHT_PIXELS * CLOCK_SIZE_MULTIPLIER + CLOCK_HEIGHT_PIXELS * STATUS_SIZE_MULTIPLIER * 2 + 6 * MARGIN_PIXELS
|
|
||||||
|
|
||||||
if 'fasting' in status and status['fasting']:
|
if 'fasting' in status and status['fasting']:
|
||||||
label = 'Breakfast'
|
label = 'Breakfast'
|
||||||
date = datetime.strptime(status['fast']['projected_end'], "%Y-%m-%d %H:%M:%S")
|
target = datetime.strptime(status['fast']['projected_end'], "%Y-%m-%d %H:%M:%S")
|
||||||
delta = date - datetime.now()
|
|
||||||
hours = delta.seconds // 3600
|
|
||||||
minutes = (delta.seconds // 60) % 60
|
|
||||||
color = (0, 255, 0)
|
|
||||||
else:
|
else:
|
||||||
label = 'Fasting'
|
label = 'Fasting'
|
||||||
date = datetime.now().replace(hour=16, minute=0, second=0, microsecond=0)
|
target = datetime.now().replace(hour=16, minute=0, second=0, microsecond=0)
|
||||||
if date > datetime.now():
|
|
||||||
delta = date - datetime.now()
|
draw_countdown(x, y, target, STATUS_SIZE_MULTIPLIER)
|
||||||
color = (0, 255, 0)
|
|
||||||
else:
|
|
||||||
delta = datetime.now() - date
|
|
||||||
color = (255, 0, 0)
|
|
||||||
|
|
||||||
hours = delta.seconds // 3600
|
|
||||||
minutes = (delta.seconds // 60) % 60
|
|
||||||
|
|
||||||
draw_clock_digits(x, y, STATUS_SIZE_MULTIPLIER, f'{hours:02}', f'{minutes:02}', 1, color)
|
|
||||||
draw_label(f'{label}:', x - MARGIN_PIXELS, y + CLOCK_HEIGHT_PIXELS * STATUS_SIZE_MULTIPLIER)
|
draw_label(f'{label}:', x - MARGIN_PIXELS, y + CLOCK_HEIGHT_PIXELS * STATUS_SIZE_MULTIPLIER)
|
||||||
|
|
||||||
def clear_status_area():
|
def clear_status_area():
|
||||||
@@ -146,12 +201,14 @@ def clear_status_area():
|
|||||||
|
|
||||||
while True:
|
while True:
|
||||||
draw_clock()
|
draw_clock()
|
||||||
draw_death_clock()
|
draw_next_event(order=2.5)
|
||||||
try:
|
try:
|
||||||
status = get_status_data()
|
status = get_status_data()
|
||||||
|
|
||||||
draw_journal_minutes(status)
|
draw_journal_minutes(status, order=0)
|
||||||
# draw_fasting(status)
|
draw_fasting(status, order=1)
|
||||||
|
milestone_countdown(order=5, target_date=datetime.strptime("09/01/2026", "%m/%d/%Y").date(), label='below 108kg:')
|
||||||
|
milestone_countdown(order=4, target_date=datetime.strptime("12/25/2026", "%m/%d/%Y").date(), label='below 95kg:')
|
||||||
|
|
||||||
except RequestException as e:
|
except RequestException as e:
|
||||||
logging.error(f'Error fetching status data: {e}')
|
logging.error(f'Error fetching status data: {e}')
|
||||||
|
|||||||
Reference in New Issue
Block a user