Improve countdown
All checks were successful
docker-image / docker (push) Successful in 9m58s

This commit is contained in:
Stefan Bethke 2025-06-03 22:20:32 +02:00
commit 3bf110bc85
4 changed files with 67 additions and 29 deletions

View file

@ -37,12 +37,12 @@ class DBFAnimation(BubaAnimation):
sleep(60) sleep(60)
@staticmethod @staticmethod
def countdown(train): def countdown(dt:datetime):
now = datetime.datetime.now() now = datetime.datetime.now()
try: try:
dep_time = datetime.datetime.strptime(train['actualDeparture'], "%H:%M").time() dep_time = datetime.datetime.strptime(dt, "%H:%M").time()
except TypeError as e: except TypeError as e:
return -1 return "--"
# First, assume all departure times are on the current day # First, assume all departure times are on the current day
dep_date = now.date() dep_date = now.date()
@ -63,8 +63,9 @@ class DBFAnimation(BubaAnimation):
dep_date -= datetime.timedelta(days=1) dep_date -= datetime.timedelta(days=1)
# Recalculate the timedelta # Recalculate the timedelta
dep_td = datetime.datetime.combine(dep_date, dep_time) - now return BubaAnimation.countdown(datetime.datetime.combine(dep_date, dep_time))
return round(dep_td.total_seconds() / 60) # dep_td = datetime.datetime.combine(dep_date, dep_time) - now
# return round(dep_td.total_seconds() / 60)
@staticmethod @staticmethod
def short_station(station: str) -> str: def short_station(station: str) -> str:
@ -83,6 +84,10 @@ class DBFAnimation(BubaAnimation):
def short_train(train: str) -> str: def short_train(train: str) -> str:
if train.startswith("ICE"): if train.startswith("ICE"):
train = "ICE" train = "ICE"
if train.startswith("EC"):
train = "EC"
if train.startswith("EN"):
train = "EN"
if train.startswith("ME"): if train.startswith("ME"):
train = "ME" train = "ME"
if train.startswith("NJ"): if train.startswith("NJ"):
@ -111,13 +116,11 @@ class DBFAnimation(BubaAnimation):
if train['isCancelled']: if train['isCancelled']:
when = "--" when = "--"
else: else:
when = self.countdown(train) when = self.countdown(train['actualDeparture'])
if when < 0:
when = 0
self.buba.text(page=0, row=i + 1, col_start=0, col_end=11, text=self.short_train(train['train'])) self.buba.text(page=0, row=i + 1, col_start=0, col_end=11, text=self.short_train(train['train']))
self.buba.text(page=0, row=i + 1, col_start=12, col_end=104, text=self.short_station(train['destination'])) self.buba.text(page=0, row=i + 1, col_start=12, col_end=104, text=self.short_station(train['destination']))
self.buba.text(page=0, row=i + 1, col_start=105, col_end=119, self.buba.text(page=0, row=i + 1, col_start=105, col_end=119,
text=str(when), align=BubaCmd.ALIGN_RIGHT) text=when, align=BubaCmd.ALIGN_RIGHT)
self.buba.set_page(0) self.buba.set_page(0)
for i in range(5): for i in range(5):
self.buba.text(page=0, row=0, col_start=93, col_end=119, text=datetime.datetime.now().strftime("%H:%M"), align=BubaCmd.ALIGN_RIGHT) self.buba.text(page=0, row=0, col_start=93, col_end=119, text=datetime.datetime.now().strftime("%H:%M"), align=BubaCmd.ALIGN_RIGHT)

View file

@ -23,31 +23,25 @@ class IcalEvents(BubaAnimation):
def update(self): def update(self):
tz = timezone(os.getenv("TZ", "Europe/Berlin")) tz = timezone(os.getenv("TZ", "Europe/Berlin"))
events = icalevents.icalevents.events(self.url, sort=True) events = icalevents.icalevents.events(self.url, tzinfo=tz, sort=True, end=datetime.now(tz)+timedelta(days=14))
for event in events: for event in events:
event.start = event.start.astimezone(tz) event.start = event.start.astimezone(tz)
self.events = events self.events = events
sleep(600) sleep(600)
@staticmethod
def ellipsis(text, max=28):
if len(text) > max:
text = text[:max - 3] + "..."
return text
def run(self): def run(self):
for (page, events) in enumerate(self.chunk(self.events, 3)): for (page, events) in enumerate(self.chunk(self.events, 3)):
if len(self.events) > 3: if len(self.events) > 3:
self.buba.text(page=0, row=0, col_start=0, col_end=119, self.buba.text(page=0, row=0, col_start=0, col_end=119,
text=f"{self.title} ({page + 1}/{int(len(self.events) / 3)})", align=BubaCmd.ALIGN_LEFT) text=f"{self.title} ({page + 1}/{int((len(self.events)+2) / 3)})", align=BubaCmd.ALIGN_LEFT)
else: else:
self.buba.text(page=0, row=0, col_start=0, col_end=119, text=self.title, align=BubaCmd.ALIGN_LEFT) self.buba.text(page=0, row=0, col_start=0, col_end=119, text=self.title, align=BubaCmd.ALIGN_LEFT)
for (i, event) in enumerate(events): for i in range(3):
if event.start - datetime.now(event.start.tzinfo) < timedelta(hours=24): if i >= len(events):
when = event.start.strftime("%H:%M") self.buba.text(page=0, row=i + 1, col_start=0, col_end=119, text="")
else: else:
when = event.start.strftime("%d.%m.") event = events[i]
self.buba.text(page=0, row=i + 1, col_start=0, col_end=103, text=self.ellipsis(event.summary)) self.buba.text(page=0, row=i + 1, col_start=0, col_end=103, text=self.ellipsis(event.summary, 25))
self.buba.text(page=0, row=i + 1, col_start=104, col_end=119, self.buba.text(page=0, row=i + 1, col_start=104, col_end=119,
text=when, align=BubaCmd.ALIGN_RIGHT) text=self.countdown(event.start), align=BubaCmd.ALIGN_RIGHT)
sleep(10) sleep(10)

View file

@ -17,5 +17,5 @@ class BubaTime(BubaAnimation):
self.buba.set_page(0) self.buba.set_page(0)
for i in range(3): for i in range(3):
self.buba.text(page=0, row=0, col_start=93, col_end=119, text=datetime.now().strftime("%H:%M:"), align=BubaCmd.ALIGN_RIGHT) self.buba.text(page=0, row=0, col_start=93, col_end=119, text=datetime.now().strftime("%H:%M"), align=BubaCmd.ALIGN_RIGHT)
sleep(2) sleep(2)

View file

@ -1,4 +1,5 @@
import logging import logging
from datetime import datetime, timedelta
from itertools import islice from itertools import islice
from threading import Thread from threading import Thread
from time import sleep from time import sleep
@ -12,16 +13,56 @@ class BubaAnimation:
self.buba = buba self.buba = buba
pass pass
def __repr__(self):
return f"<{type(self).__name__}>"
def run(self):
raise Exception("Your class must implement a run() method")
@staticmethod @staticmethod
def chunk(it, size): def chunk(it, size):
"""
Return list in groups of size.
:param it: list
:param size: chunk size
:return: list of chunks
"""
it = iter(it) it = iter(it)
return iter(lambda: tuple(islice(it, size)), ()) return iter(lambda: tuple(islice(it, size)), ())
def run(self): @staticmethod
pass def countdown(dt:datetime):
"""
Compute a human-readable time specification until the target event starts. The day starts at 04:00.
:param dt: datetime timezone-aware datetime
:return:
"""
now = datetime.now(dt.tzinfo)
from_day_start = now.replace(hour=4, minute=0, second=0, microsecond=0)
now_delta = dt - now
day_delta = dt - from_day_start
if now_delta < timedelta(seconds=0):
return "now"
if now_delta < timedelta(minutes=30):
return f"{int(now_delta.seconds/60)}m"
if day_delta < timedelta(hours=24):
return f"{int((now_delta.seconds+3599)/3600)}h"
if day_delta < timedelta(days=7):
return dt.strftime("%a") # weekday
return dt.strftime("%d.%m.")
def __repr__(self):
return f"<{type(self).__name__}>" @staticmethod
def ellipsis(text, max=28):
"""
If the text is longer that max, shorten it and add ellipsis.
:param text: to be shortened
:param max: max length
:return: shortened text
"""
if len(text) > max:
text = text[:max - 2] + "..." # we can get away with just 2, since the periods are very narrow
return text
class BubaAnimator: class BubaAnimator: