epaper/modules/ratp.py

208 lines
6.8 KiB
Python
Raw Normal View History

2022-08-14 13:19:51 +00:00
from datetime import datetime, timedelta, timezone
import json
import os
import urllib.parse
import urllib.request
from PIL import Image, ImageDraw, ImageFont
2022-08-14 16:24:46 +00:00
class IDFMAPI:
2022-08-14 13:19:51 +00:00
fnt_R_path = "./fonts/Parisine-Regular.ttf"
fnt_RB_path = "./fonts/Parisine-Bold.ttf"
2022-08-14 16:24:46 +00:00
lines = {
"metros": {
"1": "C01371",
"2": "C01372",
"3": "C01373",
"4": "C01374",
"5": "C01375",
"6": "C01376",
"7": "C01377",
"8": "C01378",
"9": "C01379",
"10": "C01380",
"11": "C01381",
"12": "C01382",
"13": "C01383",
"14": "C01384",
"3B": "C01386",
"7B": "C01387",
},
"buses": {
"57": "C01094",
"125": "C01154",
#"131": "C01159",
"184": "C01205",
},
"rers": {
"A": "C01742",
"B": "C01743",
"C": "C01727",
"D": "C01728",
"E": "C01729"
},
"tramways": {
"T2": "C01390",
"T3A": "C01391",
"T3B": "C01679",
"T7": "C01774",
"T9": "C02317",
},
}
def __init__(self, apikey=None):
self.baseurl = "https://prim.iledefrance-mobilites.fr/marketplace"
self.apikey = apikey or os.environ["TOKEN_IDFM"]
2022-08-14 13:19:51 +00:00
self._cached_file = ".ratp-%s.cache"
2022-08-14 16:24:46 +00:00
self.cache_time = 5
2022-08-14 13:19:51 +00:00
def get_weather(self):
2022-08-14 16:24:46 +00:00
ret = {}
for mode in IDFMAPI.lines:
ret[mode] = {}
for line in IDFMAPI.lines[mode]:
ret[mode][line] = self.get_line_weather(mode, line)
return ret
def get_line_weather(self, mode, line):
cache_file = self._cached_file % ("traffic-" + IDFMAPI.lines[mode][line])
2022-08-14 13:19:51 +00:00
# Read the mod time
statinfo = None
try:
2022-08-14 16:24:46 +00:00
statinfo = os.stat(cache_file)
2022-08-14 13:19:51 +00:00
except:
pass
2022-08-14 16:24:46 +00:00
if statinfo is None or datetime.fromtimestamp(statinfo.st_mtime, tz=timezone.utc) + timedelta(minutes=self.cache_time) < datetime.now(tz=timezone.utc):
2022-08-14 13:19:51 +00:00
# Do the request and save it
2022-08-14 16:24:46 +00:00
req = urllib.request.Request(self.baseurl + "/general-message?LineRef=STIF:Line::" + IDFMAPI.lines[mode][line] + ":", headers={'apikey': self.apikey})
try:
with urllib.request.urlopen(req) as f:
with open(cache_file, 'wb') as fd:
fd.write(f.read())
except ConnectionResetError:
pass
2022-08-14 13:19:51 +00:00
# Retrieve cached data
res = {}
2022-08-14 16:24:46 +00:00
with open(cache_file) as f:
2022-08-14 13:19:51 +00:00
res = json.load(f)
2022-08-14 16:24:46 +00:00
return res["Siri"]["ServiceDelivery"]["GeneralMessageDelivery"][0]["InfoMessage"]
def get_line_icon(mode, line, size, fill="gray"):
width = int(size * 1.38) if mode == "buses" or mode == "tramways" else size
image = Image.new('RGBA', (width, size), '#fff0')
draw = ImageDraw.Draw(image)
fnt_icon = ImageFont.truetype(IDFMAPI.fnt_RB_path, size-3)
if mode == "metros":
draw.ellipse((0, 0, width, size), fill=fill)
elif mode == "tramways":
2022-08-14 16:51:03 +00:00
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")
2022-08-19 14:57:59 +00:00
elif mode == "rers":
draw.rounded_rectangle((0, 0, width, size), radius=4, fill=fill)
2022-08-14 16:24:46 +00:00
else:
draw.rectangle((0, 0, width, size), fill=fill)
draw.text((int(width / 2), int(size / 2)), line, fill="white" if (fill == "black" and mode == "tramways") or (fill != "white" and mode != "tramways") else "black", anchor="mm", font=fnt_icon)
return image
2022-08-14 13:19:51 +00:00
class RATPWeatherModule:
2022-08-14 16:24:46 +00:00
def __init__(self):
self.major_lines = ["M7", "M5", "M14", "RB", "T3A"]
def gen_alerts(self):
alerts = []
weather = IDFMAPI().get_weather()
for mode in weather:
for line in weather[mode]:
if mode[0].upper() + line not in self.major_lines:
continue
def alert_icon(mode, line):
def icon(size=64):
image = Image.new('RGB', (size, size), '#000')
2022-08-14 16:24:46 +00:00
white = Image.new('RGB', (int(size / 2), int(size / 2)), '#fff')
mode_icon = Image.open("icons/" + mode + ".png").resize((int(size/2), int(size/2)))
image.paste(white, (-5,0), mode_icon)
line_icon = IDFMAPI.get_line_icon(mode, line, int(size/2), fill="white")
image.paste(line_icon, (int(size/2) - 5,0), line_icon)
return image
return icon
for info in weather[mode][line]:
if info["InfoChannelRef"]["value"] != "Perturbation":
continue
2022-08-17 08:22:48 +00:00
if "Message" not in info["Content"]:
continue
2022-08-14 16:24:46 +00:00
for msg in info["Content"]["Message"]:
if msg["MessageType"] != "TEXT_ONLY":
continue
yield {
2022-08-14 16:51:03 +00:00
"description": msg["MessageText"]["value"].replace("", ""),
2022-08-14 16:24:46 +00:00
"icon": alert_icon(mode, line),
}
2022-08-14 13:19:51 +00:00
def draw_module(self, config, width, height, line_height=19):
image = Image.new('RGB', (width, height), '#fff')
draw = ImageDraw.Draw(image)
2022-08-14 16:24:46 +00:00
weather = IDFMAPI().get_weather()
2022-08-14 13:19:51 +00:00
align_x = 0
align_y = 0
2022-08-14 16:24:46 +00:00
for mode in ["metros", "rers", "tramways", "buses"]:
if mode != "rers" and mode != "buses":
2022-08-14 13:19:51 +00:00
align_x = 0
# display mode icon
icon = Image.open("icons/" + mode + ".png").resize((line_height, line_height))
image.paste(icon, (align_x,align_y), icon)
align_x += line_height + 10
for line in weather[mode]:
if align_x + line_height >= width:
align_x = line_height + 10
align_y += line_height + 6
2022-08-14 16:24:46 +00:00
states = []
for info in weather[mode][line]:
states.append(info["InfoChannelRef"]["value"])
fill = "gray" if "Perturbation" not in states else "black"
2022-08-14 13:19:51 +00:00
2022-08-14 16:24:46 +00:00
icon = IDFMAPI.get_line_icon(mode, line, line_height, fill=fill)
image.paste(icon, (align_x, align_y), icon)
2022-08-14 13:19:51 +00:00
2022-08-14 16:24:46 +00:00
align_x += icon.width + 5
2022-08-14 13:19:51 +00:00
2022-08-14 16:24:46 +00:00
if mode != "metros" and mode != "tramways":
2022-08-14 13:19:51 +00:00
align_y += line_height + 10
else:
align_y += 10
return image