2 Punkte von GN⁺ 2024-10-25 | 1 Kommentare | Auf WhatsApp teilen
  • Wenn man sich in San Francisco ohne Auto fortbewegt, ist ein Gerät, das vor dem Verlassen des Hauses einfach die nächste Ankunftszeit an der nahegelegenen Haltestelle anzeigt, praktischer
  • Ein altes Kindle wurde per Jailbreak in ein wandmontiertes E-Ink-Infodisplay verwandelt; anfangs zeigte es periodisch Screenshots der BART-Webseite
  • Als MUNI hinzukam, machten mehrere Browser-Tabs, die Speicherlast auf dem Raspberry Pi und HTTP 429 die screenshotbasierte Architektur unzuverlässig
  • Die in Rust neu geschriebene Version holt Daten aus der Stop Monitoring API von 511.org und erzeugt mit skia-safe direkt PNGs passend zur Kindle-Auflösung und 8-Bit-Farbtiefe
  • Durch das Entfernen der Browser-Engine sowie Caching, Fehleranzeige und Layout-Anpassungen entstand ein im Alltag nutzbares Kindle-Display für den öffentlichen Nahverkehr

Die Information, die man beim Verlassen des Hauses braucht, ist einfach

  • Für Wege ohne Auto in San Francisco werden häufig öffentliche Verkehrsmittel wie Busse, Züge, Trams, Oberleitungsbusse, Cable Cars und Fähren genutzt
  • Apps wie CityMapper sind nützlich für die Zielführung, aber direkt vor der Haustür ist wichtiger als „Wohin will ich?“ vor allem die nächste Ankunftszeit jeder Linie
  • Wenn Haltestelle und Linie bereits feststehen, ist das Modell aus aktuellem Standort und Ziel generischer Navigations-Apps unnötig komplex
  • Die Idee kam aus Matt Healys Beitrag über ein altes Kindle als Smart-Home-Display und Ben Borgers Beitrag über ein altes Nook als iCloud-Fotorahmen

Bis Bilder auf dem Kindle angezeigt wurden

  • Der erste Schritt war, das Kindle per Jailbreak aufzuschließen, USBNet zu aktivieren und SSH-Zugriff zu ermöglichen
    • Danach wurde das Display regelmäßig per Cron-Job aktualisiert
    • Als Referenz dienten die Kindle-Hacking-Materialien aus Wiki und Forum von mobileread
  • Die Real Time Departure-Seite von BART bot die nächsten Züge für die gewünschte Station und automatisches Neuladen und war damit als erste Datenquelle geeignet
  • Zunächst sollte eips aus der Kindle-CLI PNGs anzeigen, doch die Bilder waren anfangs stark verzerrt oder abgeschnitten und so unbrauchbar
  • Die Ursache war, dass das Kindle-Anzeigetool 24-Bit-RGB-PNGs nicht korrekt verarbeitete und sie wie 8-Bit-Bilder einlas
    • Mit -colorspace gray -depth 8 in ImageMagick convert wurden sie korrekt dargestellt
    • Die entscheidenden Bedingungen waren Bilder in Kindle-Bildschirmgröße und 8-Bit-Farbtiefe

Die erste Implementierung: Node.js, Puppeteer, Raspberry Pi

  • Der erste Server nutzte Node.js und Puppeteer, um den benötigten Bereich der BART-Webseite als Screenshot zu erfassen
  • Das aufgenommene Bild wurde auf Kindle-Bildschirmgröße und 8-Bit-Graustufen umgewandelt und dann über einen HTTP-Endpunkt zurückgegeben
  • Der Server lief auf einem Raspberry Pi, und das Kindle holte das Bild per Cron jede Minute ab und zeigte es mit eips an
  • Da die BART-Seite selbst automatisches Refresh unterstützte, wurde die Seite beim Serverstart einmal geöffnet und nicht bei jeder Anfrage neu geladen
  • Diese Version funktionierte eine Zeit lang gut, aber das Refresh blieb gelegentlich hängen oder der Server musste wegen Speichermangels neu gestartet werden

Grenzen, die bei der Erweiterung auf MUNI sichtbar wurden

  • In der Bay Area von San Francisco gibt es 27 Nahverkehrsbetreiber, und die erste Implementierung behandelte nur BART, das regionale Schnellbahnsystem
  • Später sollte auch die Ankunftszeit von MUNI-Bussen und Light-Rail-Haltestellen angezeigt werden
  • Auch die MUNI-Webseite lieferte haltestellenweise Ankunftszeiten; zunächst wurde versucht, mehrere Haltestellenseiten mit Puppeteer zu erfassen und anschließend mit ImageMagick zusammenzusetzen
  • Die 7 Abschnitte auf dem Bildschirm entsprachen jeweils einem Browser-Tab, den Puppeteer offen halten musste
    • Der Raspberry Pi hatte nicht genug Arbeitsspeicher, und Chrome ist nicht leichtgewichtig
    • Die MUNI-Seite gab bei zu häufigen Anfragen HTTP 429 zurück
  • Ein Bildschirm mit BART und MUNI zusammen war deutlich instabiler als die reine BART-Anzeige, und für ein Display auf einen Blick war eine verlässlichere Architektur nötig

Neu entworfene Architektur in Rust

  • In der neu geschriebenen Version wurde Puppeteer entfernt und durch einen Rust-HTTP-Server ersetzt, der PNGs direkt erzeugt
  • Die öffentliche Schnittstelle blieb ähnlich wie beim Node.js-Server, aber die interne Implementierung änderte sich vollständig
  • Die Kernbausteine waren drei
    • Nächste Ankunftszeiten von 511.org holen
    • Ankunftszeiten als 8-Bit-PNG rendern
    • Das erzeugte PNG über einen HTTP-Server zurückgeben
  • Für den HTTP-Server wurde Axum verwendet
  • Für das PNG-Rendering wurde rust-skia verwendet
    • Skia ist auch die zugrunde liegende Grafikbibliothek von Chrome, sodass sich Rendering-Technik mit weniger Overhead als bei Puppeteer nutzen ließ
  • Der finale Servercode umfasste etwa 1.200 Zeilen, von denen nur die wesentlichen Teile zusammengefasst wurden

Daten aus der 511.org Stop Monitoring API holen

  • Die Stop Monitoring API von 511.org liefert prognostizierte Ankunfts- und Abfahrtszeiten für Haltestellen in der Bay Area als XML und JSON
  • Der verwendete Endpunkt hatte die Form http://api.511.org/transit/StopMonitoring/…]
  • agency bestimmt einen der 27 Nahverkehrsbetreiber der Bay Area; der Code für SF MUNI ist SF
  • Die API kann immer nur Daten eines Betreibers auf einmal liefern
    • Sie gibt entweder Ankunftszeiten für eine bestimmte Haltestelle oder Daten für alle Haltestellen dieses Betreibers zurück
    • Eine Möglichkeit, mehrere Haltestellen als Teilmenge auf einmal abzurufen, wurde nicht gefunden
  • Das API-Limit von 511.org liegt bei 60 Anfragen pro Stunde, daher eignet sich ein Ansatz mit mehreren Anfragen pro Haltestelle nicht für eine Echtzeitanzeige
  • Beim Abruf aller SF-MUNI-Daten dauerte die HTTP-Anfrage etwa 5 Sekunden, lieferte rund 27 MiB Daten zurück, und das formatierte JSON umfasste mehr als eine Million Zeilen
  • Die vollständige Antwort enthielt etwa 26k MonitoredStopVisit-Einträge, aber auf dem eigentlichen Display wurden nur LineRef, DirectionRef, DestinationName, StopPointRef, ExpectedArrivalTime und DestinationDisplay für bestimmte Haltestellen und Linien benötigt

Detailprobleme bei der Datenverarbeitung in Rust

  • Mit reqwest, serde, serde_json, chrono, tokio und eyre wurden HTTP-Anfragen, JSON-Parsing, Zeitberechnung, asynchrone Ausführung und Fehlerbehandlung umgesetzt
  • MUNI-Haltestellen-IDs lassen sich auf den Haltestellenseiten der SF-MUNI-Webseite nachsehen
    • Zum Beispiel gibt es für Persia Ave & Paris St getrennt 15918 für inbound und 15919 für outbound
    • MUNI-Haltestellen haben je Richtung eigene IDs
  • Die Antwort von 511.org enthielt vor dem JSON eine UTF-8 Byte Order Mark, und direktes Parsen mit .json() führte zum Fehler expected value at line 1 column 1
  • Wurde die Antwort mit .text() als UTF-8-String eingelesen, wurde das BOM entfernt und das Parsen mit serde_json::from_str funktionierte
  • Nach dem Filtern blieben nur noch etwa 20 Ankunftsinformationen für die interessierenden Haltestellen übrig, deutlich handlicher als die ursprünglichen 26k Einträge
  • Anschließend wurde nach LineRef und DirectionRef gruppiert, nach ExpectedArrivalTime sortiert und UTC-Zeitstempel in das Format „in X Minuten“ umgewandelt

PNGs für das Kindle mit Skia erzeugen

  • Das PNG-Rendering wurde mit skia-safe umgesetzt
  • Es wurde eine Gray8-Bitmap in der Kindle-Auflösung 1024x758 angelegt, der Hintergrund weiß gefüllt und dann Text und Linien gezeichnet
  • Zunächst wurde ein PNG8-Bild mit „Hello World“ erzeugt, um die Rendering-Pipeline zu prüfen
  • Danach wurde mit simulierten Ankunftsdaten ein tabellenartiges Bild mit zwei Spalten für inbound und outbound erstellt
  • In Kombination mit den echten API-Daten zeigte sich mehr Vielfalt bei Zielen und Ankunftszeiten als in den Testdaten, wodurch es in einem festen Layout zu überlappenden Texten kam
  • Für schnelle Iteration und zum Umgehen der API-Limits wurde vorübergehend Code hinzugefügt, der Antworten in data.json zwischenspeichert

Das Anzeige-Layout verfeinern

  • Die erste Ansicht mit echten Daten war schwer lesbar, weil sich die angezeigte Datenmenge pro Zeile unterschied
  • Verbessert wurde Folgendes
    • Ankunftszeiten wurden rechtsbündig gesetzt, um Platz am Ende der Tabelle zu sparen
    • Das min hinter jeder Zeit wurde entfernt und nur noch einmal am Ende der Liste angezeigt
    • Es werden nur die nächsten 3 Fahrzeuge angezeigt
    • Der Zielname wurde weiter nach rechts verschoben, um längere Liniennamen zu berücksichtigen
  • Danach wurden die Textgrenzen mit measure_str berechnet, um Linien-IDs als abgerundete graue Sprechblasen darzustellen
  • Zieltexte wurden 15 Pixel rechts vom rechten Rand der Linien-ID-Sprechblase platziert
  • Oben kam ein hellgrauer Header hinzu, und Muni Inbound sowie Muni Outbound wurden zentriert dargestellt

PNGs mit Axum bereitstellen

  • Der HTTP-Server wurde mit Axum 0.7 gebaut und gibt auf der Route /stops.png ein PNG zurück
  • Der Response-Header wurde auf Content-Type: image/png gesetzt
  • Zuerst wurde mit leerem Body geprüft, ob HTTP 200 zurückkommt; danach wurde die Bildgenerierungsfunktion angeschlossen, sodass PNG-Bytes direkt aus dem Speicher zurückgegeben werden
  • Wenn das Kindle das Bild per wget holte und mit eips anzeigte, trat zunächst das Problem auf, dass das Bild gedreht und abgeschnitten war
  • Da das Kindle standardmäßig im Hochformat genutzt wird, musste für eine Anzeige im Querformat vor dem Rendering eine Rotationsmatrix angewendet werden
    • Die Bildgröße wurde auf (758, 1024) vertauscht
    • Die Canvas wurde um 90.0 Grad gedreht

Funktionen, die in die produktive Version kamen

  • Die tatsächlich genutzte Version bekam zusätzlich folgende Funktionen
    • Ein Caching-System, das API-Daten im Hintergrund fortlaufend abruft, damit der HTTP-Server schnell antworten kann
    • Zeitanzeige unten links und Status des Datencaches unten rechts
    • Antialiasing
    • Kürzen von Zielnamen, damit sie nicht in den Bereich der Ankunftszeiten hineinragen
    • Unterschiedliche Grautöne pro Linie, damit sie schneller unterscheidbar sind
    • Konfiguration über eine Konfigurationsdatei
    • Bei Fehlern werden der Fehler und die .source()-Kette als PNG gerendert, sodass sie direkt auf dem Kindle sichtbar sind
  • Der gemeinsam erstellte vereinfachte Servercode steht unter transit-kindle-simplified
  • Der Servercode für das tatsächliche wandmontierte Kindle steht unter transit-kindle
  • Die screenshotbasierte BART-Version funktionierte ungefähr im Februar 2023, und das letzte Feintuning der UI des Rust-Rewrites wurde im Januar 2024 abgeschlossen

1 Kommentare

 
GN⁺ 2024-10-25
Meinungen auf Hacker News
  • Ich arbeite bei der Non-Profit-Organisation hinter OneBusAway. OneBusAway ist ein Open-Source-Projekt, das täglich von Millionen Menschen genutzt wird, um die Positionen und Ankunftszeiten von Bussen, Zügen, Straßenbahnen und Standseilbahnen zu prüfen.
    Wenn es in eurer Stadt bereits einen OneBusAway-Server gibt, könnt ihr mit dem neuen SDK eigene Apps erstellen: https://github.com/onebusAway/?q=sdk&type=all&language=&sort...
    Wenn eure Stadt keinen Server hat, könnt ihr mit den Docker-Images und der OpenTofu-Konfiguration, die wir dieses Jahr erstellt haben, GTFS/GTFS-RT-Feeds in eine REST API umwandeln.
    Auch BART stellt GTFS und GTFS-RT bereit: https://mobilitydatabase.org/feeds/mdb-53
    Andere ÖPNV-Betreiber in den USA müssen wegen neuer bundesweiter Regeln inzwischen zumindest statische Fahrplandaten als GTFS veröffentlichen.
    Wir brauchen jederzeit Entwickler, die zu der entsprechenden Software beitragen, und besonders dringend iOS-Entwickler, die Verbesserungen für 250.000 Nutzer pro Tag unterstützen. Kontakt: aaron@onebusaway.org
    Docker: https://github.com/OneBusAway/onebusaway-docker / OpenTofu: https://github.com/OneBusAway/onebusaway-deployment

    • Aktuell werden weltweit nur 9 Städte unterstützt, davon 2 im Beta-Status. Es müsste sich wohl stärker verbreiten, damit Open-Source-Beiträge leichter werden.
      Ich würde meine Stadt auch gern hinzufügen, wenn der Ablauf einfach ist. Es wirkt so, als wäre zuletzt viel Engineering-Zeit in Bibliotheken und SDK-Support geflossen; besser wäre es vermutlich, in einen einfachen Einstiegspfad für die Developer Experience zu investieren, damit neue Beitragende sinnvoll Zeit investieren können.
      Dieses Dokument ist zum Beispiel ziemlich komplex: https://github.com/OneBusAway/onebusaway/wiki/Multi-Region
    • Ich frage mich, wo man die Liste der Städte findet, in denen OneBusAway im Einsatz ist. Wenn man nach „onebusaway cities“ sucht, erscheint https://onebusaway.org/onebusaway-deployments/, aber dort war eine der erwähnten Städte wie Adelaide nicht aufgeführt.
    • Wenn es Open Source ist, frage ich mich, warum es nur im Play Store angeboten und nicht über F-Droid verteilt wird.
    • Ich frage mich nicht nur bei OneBusAway, sondern auch bei Google Maps und anderen Bus-Tracking-Apps, warum sie so ungenau sind.
      Da steht „Ankunft in 4 Minuten“, während der Bus direkt vor einem abfährt, oder „5 Minuten Verspätung“, und dann kommt er 2 Minuten zu früh. Manchmal taucht der Bus gar nicht auf.
      Vermutlich liegt es an den Quelldaten, aber ich würde gern die Hintergründe kennen, warum die Zuverlässigkeit so gering ist. Als Referenz: Seattle.
  • BART und MUNI unterstützen beide die General Transit Feed Specification, und es gibt einen standardisierten Weg, diese Daten zu erhalten.
    https://gtfs.org/documentation/overview/#gtfs-realtime

    • Ich frage mich, wie verlässlich die Echtzeitinformationen sind. Google Maps sagt „Bus kommt in 4 Minuten“, während ich sehe, wie er abfährt, oder es heißt „5 Minuten Verspätung“, tatsächlich kommt er aber oft 2 Minuten zu früh.
  • Gefällt mir wirklich sehr. Als ich früher in Boston ohne Auto gelebt habe, habe ich mir selbst etwas deutlich Groberes zusammengeschustert, um Fälle abzudecken, in denen es mehrere Wege von A nach B gibt und das „Beste“ von der Zeit oder Zwischenstopps abhängt.
    Zum Beispiel habe ich vor der Arbeit meinen Sohn zur Schule gebracht und manchmal danach gefrühstückt. Mit einer Anzeige der nächsten Abfahrtszeit hätte ich in nichtdeterministischen Situationen wie einem Spaziergang mit einem Vierjährigen an einem interessanten Ort viel flexibler entscheiden können – oder ob ich mich beeilen muss, um einen Zug zu erwischen, dessen Verpassen eine größere Taktlücke bedeutet.

  • Ich bin überrascht, dass https://oeffi.schildbach.de/index.html noch nicht erwähnt wurde. Es macht genau das, was hier gewünscht ist.

  • In Großbritannien kann man ein Abfahrtstafel-Gerät kaufen, das die Abfahrtsanzeigen an Bahnhöfen nachahmt
    https://ukdepartureboards.co.uk/store/product/desktop-depart...

  • Ich lebe ebenfalls in SF und habe eine iOS-App namens A Better Ride gebaut, um dasselbe Problem zu lösen
    Sie zeigt nur die Abfahrtszeiten des öffentlichen Nahverkehrs an Haltestellen in der Nähe. Ziel ist es, den ÖPNV vorhersehbar und leichter erkundbar zu machen und dadurch Stress zu reduzieren. Es ist ein persönliches Projekt, das ich in meiner Freizeit ohne Monetarisierung entwickle

    • Diese App ist wirklich großartig. Sie unterscheidet sich von anderen ÖPNV-Apps dadurch, dass sie nur Linien in der Nähe zeigt und wohin diese Linien fahren
      So kann man Orte erkunden, die man ohne Nachdenken über Umstiege erreichen kann
    • Die App ist wirklich toll. Die Echtzeitkarte gefällt mir besonders
      Ich baue ebenfalls Hardware in diesem Bereich und habe mich zuletzt viel mit GTFS beschäftigt; man sieht sofort, wie viel Arbeit in das Parsen und Darstellen der Verkehrsdaten geflossen ist
      Falls es dir recht ist, würde ich gern über die UX-Entscheidungen sprechen, die möglichen Fahrten an jeder Haltestelle zusammenzufassen und zu gruppieren, sowie über das Backend
    • Wirklich gut. Es gibt viele sorgfältige Details, etwa dass angezeigt wird, auf welcher Straßenseite man ist und in welche grobe Richtung die Fahrt geht
  • Man muss den Kindle nicht jailbreaken oder Bilder rendern
    Es reicht, eine selbst gebaute Webseite im Kindle-Browser zu öffnen und per Debug-Befehl die „Bildschirmschoner“-Werbung bzw. den Energiesparbildschirm des Kindle abzuschalten
    Die Browserleiste bleibt zwar an einer Kante des Kindle sichtbar, aber man kann das Gerät drehen und sie unten oder rechts platzieren. Dafür kann man die Wetter-, Verkehrs- und Nachrichtenanzeige mit einfachem HTML/CSS/JS und einer Backend-Sprache nach Wahl bauen und z. B. auf einer günstigen DigitalOcean-Instanz für 4 Dollar betreiben
    https://blog.notfaqs.com/2018/06/kindle-e-reader-disable-scr...
    https://www.mobileread.com/forums/showthread.php?t=198334

    • Es wäre noch cooler, wenn Amazon für solche Display-Hacks einen Kioskmodus im Kindle-Browser fördern und bereitstellen würde
    • Ich habe so etwas mit einem inkBOOK gemacht, auf dem Android läuft. Ich habe das Chrome-APK installiert und meine eigene Webseite geöffnet
      Mit document.documentElement.requestFullscreen() kommt man in echtes Vollbild, sodass auch die Statusleiste nicht sichtbar ist. Da die lokale ÖPNV-API direkt von der Webseite aus erreichbar war, brauchte ich nicht einmal ein Backend
    • Ich habe es mit einem Kindle der 4. Generation untersucht, aber offenbar lassen sich wegen ungültiger Zertifikate kaum HTTPS-Seiten nutzen
      Wenn man ihn jedoch mit einem Server im lokalen Netzwerk kommunizieren lässt, sollte es wohl funktionieren
    • Bei späteren Kindles wurde die Möglichkeit, den Bildschirmschoner zu deaktivieren, mit ziemlicher Sicherheit entfernt
  • Ich glaube, dieser Use Case wird nicht ganz verstanden. Man will nicht „einen Zug nehmen“, sondern von A nach B kommen
    Wenn der Zug kaputt ist, gestreikt wird, die Strecke zwischen 13 und 16 Uhr blockiert ist oder gerade keine Betriebszeit ist, will man Alternativen. Deshalb brauche ich das, was Google Maps liefert oder zu liefern versucht
    Wenn ich vom Ferry Building nach Oakland will und BART ausgefallen ist, ist die Fähre vielleicht besser; spätabends bleiben vielleicht nur Taxi oder Uber
    Wenn man in Tokyo von Shibuya nach Azabujuban fährt, gibt es Optionen wie Ginza Line→Namboku Line, Hanzomon Line→Oedo Line, Yamanote Line→Namboku Line, Bus 6 oder Taxi
    Zwischen den Bahnhöfen und Bushaltestellen der einzelnen Linien läuft man jeweils 3 bis 8 Minuten, sodass auch beeinflusst, wovon man gerade näher ist. Ob man spät dran ist oder großes bzw. schweres Gepäck hat, ist ebenfalls wichtig

    • Google Maps scheint stark davon auszugehen, dass Gehen etwas sehr Schlechtes ist, das niemand will
      Wenn ich zum Beispiel irgendwohin will, wo ich mit dem Bus 39 fahren und dann 15 Minuten laufen kann, schlägt Google mir eine Route mit zwei Umstiegen vor. Mit Glück dauert sie ungefähr so lang wie einmal Bus plus Fußweg
      Wenn ich also wissen will, wann der Bus 39 kommt, muss ich absichtlich ein anderes Ziel angeben
      Außerdem sind die Echtzeitdaten von Google dort, wo ich bin, fragwürdig; die Daten des Betreibers oder einiger Drittanbieter sind zuverlässiger
    • Der Use Case ist alltägliche Mobilität. Viele Menschen wollen die meiste Zeit tatsächlich einfach „einen Zug nehmen“
      Es gibt Situationen, in denen man in Echtzeit umfangreich planen muss, und es ist gut, solche Tools zu haben, aber alltägliche Wege lassen sich mit deutlich einfacheren Werkzeugen gut abdecken
      Das Tokyo-Beispiel ist interessant, aber laut Google liegen die meisten ÖPNV-Optionen bei 30–40 Minuten und sind für eine grobe Planung praktisch gleichwertig. Das Taxi ist schneller, und mit dem Fahrrad ist es fast ähnlich
    • Beim täglichen Pendeln will ich wirklich einfach „den Zug nehmen“
      Wenn der Zug kaputt ist oder gestreikt wird, zeigt sich das in der Echtzeit-Zugliste als unerwartete Lücke
      Wenn ich in der Stadt unterwegs bin, öffne ich Google Maps und gebe mein Ziel ein; aber wenn ich von zu Hause zu einem Ort aufbreche, an dem ich schon 500-mal war, mache ich das nicht extra
    • Die ÖPNV-Erfahrung im Zentrum von Tokyo lässt sich nicht auf die ganze Welt verallgemeinern
      Das ist ungefähr so sinnvoll, als würde man Einkauf oder Essen nur anhand der Erfahrung innerhalb von Disneyland beurteilen. Schon das Antippen mit Suica ist um ein Vielfaches schneller als anderswo, und an manchen Orten wird es überhaupt nicht unterstützt
    • https://citymapper.com könnte dir gefallen
      Es hat viele Funktionen, aber ich weiß nicht, wie gut Tokyo unterstützt wird. In London zeigt es, wenn man nach Hause möchte, mehrere Wege, optimiert nach Kosten, Fußweg, Geschwindigkeit, Umstiegen, Barrierefreiheit usw.
  • Ich nutze etwas Ähnliches mit einem wiederverwendeten Lenovo ThinkSmart View-Tablet/Konferenzraumgerät, das mit Home Assistant und der lokalen Verkehrsgesellschaft integriert ist.
    Der Vorteil ist, dass es deutlich schneller einsatzbereit ist. Klar, ich habe auf dem Lenovo ein Custom-Android-ROM installiert, aber man kann darauf alles anzeigen, was man möchte, und auch Dinge wie Beleuchtung oder einen Saugroboter steuern.

  • Es gibt auch ein schönes Produkt, das die BART-Karte als physisches LED-Schild umsetzt: https://www.designrules.co/