From 3bf110bc85c2a3b99c05fd5159b7d09ea259f593 Mon Sep 17 00:00:00 2001 From: Stefan Bethke Date: Tue, 3 Jun 2025 22:20:32 +0200 Subject: [PATCH] Improve countdown --- buba/animations/dbf.py | 21 ++++++++------- buba/animations/icalevents.py | 24 +++++++---------- buba/animations/time.py | 2 +- buba/bubaanimator.py | 49 ++++++++++++++++++++++++++++++++--- 4 files changed, 67 insertions(+), 29 deletions(-) diff --git a/buba/animations/dbf.py b/buba/animations/dbf.py index caccb3c..693afc1 100644 --- a/buba/animations/dbf.py +++ b/buba/animations/dbf.py @@ -37,12 +37,12 @@ class DBFAnimation(BubaAnimation): sleep(60) @staticmethod - def countdown(train): + def countdown(dt:datetime): now = datetime.datetime.now() try: - dep_time = datetime.datetime.strptime(train['actualDeparture'], "%H:%M").time() + dep_time = datetime.datetime.strptime(dt, "%H:%M").time() except TypeError as e: - return -1 + return "--" # First, assume all departure times are on the current day dep_date = now.date() @@ -63,8 +63,9 @@ class DBFAnimation(BubaAnimation): dep_date -= datetime.timedelta(days=1) # Recalculate the timedelta - dep_td = datetime.datetime.combine(dep_date, dep_time) - now - return round(dep_td.total_seconds() / 60) + return BubaAnimation.countdown(datetime.datetime.combine(dep_date, dep_time)) +# dep_td = datetime.datetime.combine(dep_date, dep_time) - now +# return round(dep_td.total_seconds() / 60) @staticmethod def short_station(station: str) -> str: @@ -83,6 +84,10 @@ class DBFAnimation(BubaAnimation): def short_train(train: str) -> str: if train.startswith("ICE"): train = "ICE" + if train.startswith("EC"): + train = "EC" + if train.startswith("EN"): + train = "EN" if train.startswith("ME"): train = "ME" if train.startswith("NJ"): @@ -111,13 +116,11 @@ class DBFAnimation(BubaAnimation): if train['isCancelled']: when = "--" else: - when = self.countdown(train) - if when < 0: - when = 0 + when = self.countdown(train['actualDeparture']) 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=105, col_end=119, - text=str(when), align=BubaCmd.ALIGN_RIGHT) + text=when, align=BubaCmd.ALIGN_RIGHT) self.buba.set_page(0) 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) diff --git a/buba/animations/icalevents.py b/buba/animations/icalevents.py index ec0ba20..e605b38 100644 --- a/buba/animations/icalevents.py +++ b/buba/animations/icalevents.py @@ -23,31 +23,25 @@ class IcalEvents(BubaAnimation): def update(self): 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: event.start = event.start.astimezone(tz) self.events = events sleep(600) - @staticmethod - def ellipsis(text, max=28): - if len(text) > max: - text = text[:max - 3] + "..." - return text - def run(self): for (page, events) in enumerate(self.chunk(self.events, 3)): if len(self.events) > 3: 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: 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): - if event.start - datetime.now(event.start.tzinfo) < timedelta(hours=24): - when = event.start.strftime("%H:%M") + for i in range(3): + if i >= len(events): + self.buba.text(page=0, row=i + 1, col_start=0, col_end=119, text="") else: - when = event.start.strftime("%d.%m.") - 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=104, col_end=119, - text=when, align=BubaCmd.ALIGN_RIGHT) + event = events[i] + 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, + text=self.countdown(event.start), align=BubaCmd.ALIGN_RIGHT) sleep(10) diff --git a/buba/animations/time.py b/buba/animations/time.py index 5affeef..ab4af58 100644 --- a/buba/animations/time.py +++ b/buba/animations/time.py @@ -17,5 +17,5 @@ class BubaTime(BubaAnimation): self.buba.set_page(0) 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) diff --git a/buba/bubaanimator.py b/buba/bubaanimator.py index 17ba786..20226a7 100644 --- a/buba/bubaanimator.py +++ b/buba/bubaanimator.py @@ -1,4 +1,5 @@ import logging +from datetime import datetime, timedelta from itertools import islice from threading import Thread from time import sleep @@ -12,16 +13,56 @@ class BubaAnimation: self.buba = buba pass + def __repr__(self): + return f"<{type(self).__name__}>" + + def run(self): + raise Exception("Your class must implement a run() method") + @staticmethod def chunk(it, size): + """ + Return list in groups of size. + :param it: list + :param size: chunk size + :return: list of chunks + """ it = iter(it) return iter(lambda: tuple(islice(it, size)), ()) - def run(self): - pass + @staticmethod + 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: