Display next stop just before an event

This commit is contained in:
nemunaire 2022-08-21 04:03:33 +02:00
parent c85fff1dd9
commit e380d544d9
3 changed files with 133 additions and 11 deletions

22
main.py
View File

@ -60,20 +60,30 @@ def main():
moon = WeatherMoonPhaseModule().draw_module(config, 65, 65)
image.paste(moon, (0, 113), moon)
# 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))
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))

View File

@ -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)

View File

@ -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