From e380d544d9b2b26e89c1edce5d78f81f75d85f2f Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 21 Aug 2022 04:03:33 +0200 Subject: [PATCH] Display next stop just before an event --- main.py | 26 ++++++++---- modules/ical.py | 10 +++++ modules/ratp.py | 108 ++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 133 insertions(+), 11 deletions(-) diff --git a/main.py b/main.py index 5683186..c092271 100644 --- a/main.py +++ b/main.py @@ -60,20 +60,30 @@ def main(): moon = WeatherMoonPhaseModule().draw_module(config, 65, 65) image.paste(moon, (0, 113), moon) - # weekly weather - from modules.weather import WeeklyWeatherModule - image.paste(WeeklyWeatherModule().draw_module(config, int(480/1.6), 275), (480-int(480/1.6), 255)) + # ical + from modules.ical import IcalModule + ical = IcalModule(config) + cal = ical.draw_module(config, 480-int(480/1.6), 255) + image.paste(cal, (0, 250)) + + occuped_space = 0 + evt_coming = ical.event_coming(config) + if evt_coming: + from modules.ratp import RATPNextStopModule + nstops = RATPNextStopModule().draw_module(config, ["RB/cite+universitaire", "M7/porte+d'italie"], int(480/1.6), 275) + image.paste(nstops, (480-int(480/1.6), 255)) + occuped_space = nstops.height + + if occuped_space < 250: + # weekly weather + from modules.weather import WeeklyWeatherModule + image.paste(WeeklyWeatherModule().draw_module(config, int(480/1.6), 275), (480-int(480/1.6), 255 + occuped_space)) # RATP weather from modules.ratp import RATPWeatherModule ratp = RATPWeatherModule().draw_module(config, int(480/1.6), 94) image.paste(ratp, (480-int(480/1.6), 155)) - # ical - from modules.ical import IcalModule - cal = IcalModule(config).draw_module(config, 480-int(480/1.6), 255) - image.paste(cal, (0, 250)) - # Toolbar from modules.weather import WeatherToolbarModule image.paste(WeatherToolbarModule().draw_module(config, 480, 50), (0, 530)) diff --git a/modules/ical.py b/modules/ical.py index ad225f0..5da1d70 100644 --- a/modules/ical.py +++ b/modules/ical.py @@ -155,6 +155,16 @@ class IcalModule: return situations, conjoncturels, start_station, end_station, place + def event_coming(self, config): + now = datetime.now(tz=pytz.timezone('Europe/Paris')) + coming = datetime.now(tz=pytz.timezone('Europe/Paris')) + timedelta(minutes=80) + + for evt in self.get_events(config): + # Looking only the first event + start = evt["start"] + return now < start and start < coming + return False + def draw_module(self, config, width, height, line_height=19): now = datetime.now(tz=pytz.timezone('Europe/Paris')) events = self.get_events(config, now + timedelta(weeks=1), now) diff --git a/modules/ratp.py b/modules/ratp.py index 2676321..5cd1875 100644 --- a/modules/ratp.py +++ b/modules/ratp.py @@ -1,4 +1,5 @@ from datetime import datetime, timedelta, timezone +import hashlib import json import os import urllib.parse @@ -61,6 +62,48 @@ class IDFMAPI: self._cached_file = ".ratp-%s.cache" self.cache_time = 5 + def get_schedules(self, mode, line, station, way="A+R"): + if mode == "M": + mode = "metros" + elif mode == "R": + mode = "rers" + elif mode == "T": + mode = "tramways" + elif mode == "B": + mode = "buses" + elif mode == "N": + mode = "noctiliens" + + cache_file = self._cached_file % ("schedule-" + mode + "-" + line + "-" + hashlib.md5((mode + line + station + way).encode()).hexdigest()) + + req = urllib.request.Request("https://ratp.p0m.fr/api/schedules/%s/%s/%s/%s" % (mode, line, station, way)) + try: + with urllib.request.urlopen(req) as f: + with open(cache_file, 'wb') as fd: + fd.write(f.read()) + except ConnectionResetError: + pass + except urllib.error.URLError: + pass + except urllib.error.HTTPError: + pass + + with open(cache_file) as f: + res = json.load(f) + + # Convert time to hours + if mode != "rers": + now = datetime.fromisoformat(res["_metadata"]["date"]) + + for i in range(len(res["result"]["schedules"])): + if "message" in res["result"]["schedules"][i]: + if res["result"]["schedules"][i]["message"] == "A l'approche" or res["result"]["schedules"][i]["message"] == "A l'arret": + res["result"]["schedules"][i]["message"] = now.strftime("%H:%M") + elif res["result"]["schedules"][i]["message"].endswith(" mn"): + res["result"]["schedules"][i]["message"] = (now + timedelta(minutes=int(res["result"]["schedules"][i]["message"].split(" ")[0]))).strftime("%H:%M") + + return res["result"]["schedules"] + def get_weather(self): ret = {} @@ -104,14 +147,14 @@ class IDFMAPI: fnt_icon = ImageFont.truetype(IDFMAPI.fnt_RB_path, size-3) - if mode == "metros": + if mode == "M" or mode == "metros": draw.ellipse((0, 0, width, size), fill=fill) - elif mode == "tramways": + elif mode == "T" or mode == "tramways": if fill == "black": draw.rectangle((0, 0, width, size), fill=fill) draw.rectangle((0, 0, width, 1), fill=fill if fill != "black" else "gray") draw.rectangle((0, size-2, width, size), fill=fill if fill != "black" else "gray") - elif mode == "rers": + elif mode == "R" or mode == "rers": draw.rounded_rectangle((0, 0, width, size), radius=4, fill=fill) else: draw.rectangle((0, 0, width, size), fill=fill) @@ -205,3 +248,62 @@ class RATPWeatherModule: align_y += 10 return image + + +class RATPNextStopModule: + + def draw_module(self, config, stops, width, height, line_height=17): + image = Image.new('RGB', (width, height), '#fff') + draw = ImageDraw.Draw(image) + + fnt_R = ImageFont.truetype(IDFMAPI.fnt_R_path, line_height) + fnt_B = ImageFont.truetype(IDFMAPI.fnt_RB_path, line_height) + + align = 0 + + api = IDFMAPI() + for stop in stops: + tmp = stop.split("/") + mode = tmp[0][0] + line = tmp[0][1:] + if 1 < len(tmp) < 3: + prep = {} + for s in api.get_schedules(mode, line, *tmp[1:]): + if s["destination"] not in prep: + prep[s["destination"]] = [] + prep[s["destination"]].append(s) + + icon = IDFMAPI.get_line_icon(mode, line, int(line_height*(1.5 if len(prep.keys()) > 1 else 1))) + image.paste(icon, (0, align), icon) + + max_dest = 64 + for dest, msgs in prep.items(): + align_x = line_height * 2 + + sz = fnt_B.getsize(dest)[0] + while sz > max_dest: + dest = dest[:-1] + sz = fnt_B.getsize(dest)[0] + + draw.text( + (align_x, align), + dest, + font=fnt_B, anchor="lt", fill="black" + ) + + align_x += max_dest + int(line_height/2.5) + + for msg in [] + msgs + msgs: + draw.text( + (align_x, align), + msg["message"], + font=fnt_R, anchor="lt", fill="black" + ) + align_x += fnt_R.getsize(msg["message"])[0] + int(line_height/2.5) + + align += line_height + align += int(line_height * 0.33) + + image = image.crop((0,0,width, min(align, height))) + + return image