Last active
February 18, 2026 14:52
-
-
Save spacegauch0/2effda8be6131af9a59cccc78a84c3ad to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """ | |
| BVG Departures Display for Waveshare 2.13" e-Paper HAT | |
| Shows U6 departures from Kaiserin-Augusta-Str station | |
| """ | |
| import sys | |
| import os | |
| import json | |
| from datetime import datetime, timezone, timedelta | |
| try: | |
| from waveshare_epd import epd2in13_V2 | |
| from PIL import Image, ImageDraw, ImageFont | |
| HAS_EPAPER = True | |
| except Exception as e: | |
| HAS_EPAPER = False | |
| print(f"e-Paper not available: {e}") | |
| sys.exit(1) | |
| # Display dimensions | |
| EPD_WIDTH = 250 | |
| EPD_HEIGHT = 122 | |
| # Fonts | |
| try: | |
| FONT_UI = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Bold.ttf", 10) | |
| except: | |
| FONT_UI = ImageFont.load_default() | |
| def display_bvg(): | |
| """Fetch and display BVG departures.""" | |
| import urllib.request | |
| # Station ID for U6 Kaiserin-Augusta-Str | |
| STATION_ID = "900068302" | |
| try: | |
| url = f"https://v6.bvg.transport.rest/stops/{STATION_ID}/departures?limit=5" | |
| req = urllib.request.Request(url, headers={'User-Agent': 'OpenClawGotchi'}) | |
| with urllib.request.urlopen(req, timeout=15) as response: | |
| data = json.loads(response.read()) | |
| except Exception as e: | |
| print(f"BVG API error: {e}") | |
| # Try to show error on display | |
| try: | |
| epd = epd2in13_V2.EPD() | |
| epd.init(epd.FULL_UPDATE) | |
| image = Image.new('1', (EPD_WIDTH, EPD_HEIGHT), 255) | |
| draw = ImageDraw.Draw(image) | |
| draw.text((10, 50), "BVG Error", font=FONT_UI, fill=0) | |
| draw.text((10, 70), str(e)[:30], font=FONT_UI, fill=0) | |
| epd.display(epd.getbuffer(image)) | |
| epd.sleep() | |
| except: | |
| pass | |
| return | |
| try: | |
| epd = epd2in13_V2.EPD() | |
| epd.init(epd.FULL_UPDATE) | |
| image = Image.new('1', (EPD_WIDTH, EPD_HEIGHT), 255) | |
| draw = ImageDraw.Draw(image) | |
| # Header | |
| now = datetime.now() | |
| draw.text((2, 1), "U6 Kaiserin-Augusta-Str.", font=FONT_UI, fill=0) | |
| time_str = now.strftime('%H:%M') | |
| time_w = draw.textbbox((0, 0), time_str, font=FONT_UI)[2] | |
| draw.text((EPD_WIDTH - time_w - 2, 1), time_str, font=FONT_UI, fill=0) | |
| draw.line((0, 14, EPD_WIDTH, 14), fill=0) | |
| # Departures | |
| y = 20 | |
| for dep in data.get('departures', [])[:4]: | |
| line = dep.get('line', {}).get('name', 'U?') | |
| direction = dep.get('direction', '') | |
| when = dep.get('when') or dep.get('plannedWhen', '') | |
| if when: | |
| try: | |
| when = when.replace('+01:00', '+0100').replace('Z', '+0000') | |
| dt = datetime.fromisoformat(when) | |
| now_tz = now.replace(tzinfo=timezone(timedelta(hours=1))) | |
| diff = dt - now_tz | |
| mins = int(diff.total_seconds() / 60) | |
| if mins < 0: | |
| mins = 0 | |
| if mins == 0: | |
| time_display = "now" | |
| elif mins == 1: | |
| time_display = "1 min" | |
| else: | |
| time_display = f"{mins} mins" | |
| except: | |
| time_display = "--" | |
| else: | |
| time_display = "--" | |
| direction = direction[:16] | |
| draw.text((5, y), line, font=FONT_UI, fill=0) | |
| draw.text((28, y), direction[:14], font=FONT_UI, fill=0) | |
| time_w = draw.textbbox((0, 0), time_display, font=FONT_UI)[2] | |
| draw.text((EPD_WIDTH - time_w - 5, y), time_display, font=FONT_UI, fill=0) | |
| y += 22 | |
| draw.line((0, EPD_HEIGHT - 10, EPD_WIDTH, EPD_HEIGHT - 10), fill=0) | |
| epd.display(epd.getbuffer(image)) | |
| epd.sleep() | |
| print("BVG departures shown") | |
| except Exception as e: | |
| print(f"Display error: {e}") | |
| if __name__ == "__main__": | |
| display_bvg() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment