2 points par GN⁺ 2024-10-25 | 1 commentaires | Partager sur WhatsApp
  • À San Francisco, quand on se déplace sans voiture, il est plus pratique d’avoir un dispositif qui affiche directement la prochaine heure d’arrivée à l’arrêt le plus proche avant de quitter la maison
  • Une ancienne Kindle a été jailbreakée puis transformée en panneau e-ink mural, affichant au départ à intervalles réguliers des captures de la page web de BART
  • En y ajoutant MUNI, l’accumulation d’onglets de navigateur, la charge mémoire sur le Raspberry Pi et les erreurs HTTP 429 ont rendu l’architecture basée sur des captures d’écran moins fiable
  • La réécriture en Rust récupère les données depuis l’API Stop Monitoring de 511.org et génère directement des PNG adaptés à la résolution de la Kindle et à sa profondeur de couleur 8 bits avec skia-safe
  • En supprimant le moteur de navigateur et en ajoutant cache, affichage des erreurs et ajustements de mise en page, le projet aboutit à un afficheur de transports en commun sur Kindle réellement utilisable

Les informations nécessaires avant de sortir sont simples

  • À San Francisco, l’auteur se déplace sans voiture et utilise souvent les transports en commun comme le bus, le train, le tramway, le trolleybus, le cable car et le ferry
  • Des apps comme CityMapper sont utiles pour aller à destination, mais devant chez soi, il est plus important de connaître la prochaine heure d’arrivée de chaque ligne que de décider « où aller »
  • Si l’on connaît déjà l’arrêt et les lignes à prendre, le modèle position actuelle/destination des apps de navigation généralistes est plus complexe que nécessaire
  • L’idée vient d’un article de Matt Healy sur l’usage d’une ancienne Kindle comme écran domotique, et d’un article de Ben Borgers sur l’usage d’une ancienne Nook comme cadre photo iCloud

Jusqu’à l’affichage d’images sur la Kindle

  • La première étape a consisté à jailbreaker la Kindle pour activer USBNet et permettre une connexion SSH
    • Ensuite, une tâche cron rafraîchissait régulièrement l’écran
    • Les ressources de hacking Kindle du wiki et du forum mobileread ont servi de référence
  • La page Real Time Departure de BART fournit les prochains trains pour une station donnée et se rafraîchit automatiquement, ce qui en faisait une bonne source de données initiale
  • L’auteur a tenté d’afficher un PNG avec eips, l’outil CLI de la Kindle, mais au départ l’image était fortement étirée ou tronquée, donc inutilisable
  • Le problème venait du fait que l’outil d’affichage de la Kindle ne gérait pas correctement les PNG RGB 24 bits et les lisait comme des images 8 bits
    • En appliquant -colorspace gray -depth 8 avec convert d’ImageMagick, l’affichage est devenu correct
    • Une image à la taille de l’écran Kindle et une profondeur de couleur 8 bits sont devenues les conditions essentielles

Première implémentation : Node.js, Puppeteer, Raspberry Pi

  • Le serveur initial utilisait Node.js et Puppeteer pour capturer la zone nécessaire de la page web BART
  • L’image capturée était convertie au format adapté à la taille d’écran de la Kindle et au niveau de gris 8 bits, puis renvoyée via un endpoint HTTP
  • Le serveur tournait sur un Raspberry Pi, et la Kindle récupérait l’image chaque minute via cron pour l’afficher avec eips
  • Comme la page BART prenait déjà en charge l’actualisation automatique, le serveur ouvrait la page une seule fois au démarrage au lieu de la recharger à chaque requête
  • Cette version a bien fonctionné pendant un temps, mais le rafraîchissement cessait parfois et le serveur devait parfois redémarrer faute de mémoire

Les limites révélées par l’extension à MUNI

  • Dans la baie de San Francisco, il existe 27 opérateurs de transports publics, et la première implémentation ne gérait que BART, le réseau ferroviaire régional
  • Ensuite, l’auteur a voulu voir aussi les heures d’arrivée des arrêts de bus et de métro léger de MUNI
  • Le site web de MUNI fournissant lui aussi les heures d’arrivée par arrêt, la première tentative a consisté à capturer plusieurs pages d’arrêt avec Puppeteer puis à les assembler avec ImageMagick
  • Les 7 sections de l’écran correspondaient chacune à un onglet de navigateur que Puppeteer devait maintenir
    • Le Raspberry Pi n’avait pas assez de mémoire, et Chrome n’est pas léger
    • Le site de MUNI renvoyait des HTTP 429 en cas de requêtes trop fréquentes
  • L’écran combinant BART et MUNI s’est révélé bien plus instable que l’écran BART seul, et un tableau de bord visible d’un coup d’œil demandait une architecture plus fiable

Une architecture repensée en Rust

  • La version réécrite supprime Puppeteer et adopte un serveur HTTP Rust qui génère directement les PNG
  • L’interface publique est restée proche de celle du serveur Node.js, mais l’implémentation interne a complètement changé
  • L’architecture repose sur trois composants principaux
    • récupérer les prochaines heures d’arrivée depuis 511.org
    • rendre ces heures d’arrivée en image PNG 8 bits
    • renvoyer le PNG généré depuis un serveur HTTP
  • Le serveur HTTP utilise Axum
  • Le rendu PNG utilise rust-skia
    • Skia est aussi la bibliothèque graphique de base de Chrome, ce qui permet de réutiliser une technologie de rendu avec moins de surcharge que Puppeteer
  • Le code final du serveur faisait environ 1 200 lignes, et seuls les points essentiels sont présentés

Récupérer les données depuis l’API Stop Monitoring de 511.org

  • L’API Stop Monitoring de 511.org fournit en XML et JSON les heures prévues d’arrivée et de départ pour les arrêts de la baie
  • L’endpoint utilisé avait la forme http://api.511.org/transit/StopMonitoring/…]
  • agency désigne l’un des 27 opérateurs de transport de la baie, et le code de SF MUNI est SF
  • L’API ne permet de récupérer les données que pour un seul opérateur à la fois
    • Elle renvoie soit les heures d’arrivée d’un arrêt précis, soit les données de tous les arrêts de cet opérateur
    • L’auteur n’a pas trouvé de moyen de récupérer en une seule fois un sous-ensemble de plusieurs arrêts
  • La limite de l’API 511.org est de 60 requêtes par heure, ce qui rend peu adaptée une approche par multiples requêtes par arrêt pour un affichage en temps réel
  • En demandant l’ensemble des données de SF MUNI, la requête HTTP prenait environ 5 secondes, renvoyait environ 27 MiB de données, et le JSON formaté dépassait le million de lignes
  • La réponse complète contenait environ 26k entrées MonitoredStopVisit, mais l’écran n’avait réellement besoin que de LineRef, DirectionRef, DestinationName, StopPointRef, ExpectedArrivalTime et DestinationDisplay pour certains arrêts et lignes précis

Les détails rencontrés dans le traitement des données en Rust

  • reqwest, serde, serde_json, chrono, tokio et eyre ont été utilisés pour les requêtes HTTP, le parsing JSON, le calcul du temps, l’exécution asynchrone et la gestion des erreurs
  • Les identifiants d’arrêt MUNI peuvent être consultés sur les pages d’arrêt du site web de SF MUNI
    • Par exemple, Persia Ave & Paris St a 15918 pour l’inbound et 15919 pour l’outbound
    • Les arrêts MUNI ont des identifiants distincts selon la direction
  • La réponse de 511.org ajoutait un Byte Order Mark UTF-8 au début du JSON, ce qui provoquait l’erreur expected value at line 1 column 1 en essayant de parser directement avec .json()
  • En lisant la réponse avec .text(), le BOM était supprimé et le contenu pouvait ensuite être parsé avec serde_json::from_str
  • Après filtrage, il ne restait qu’une vingtaine d’informations d’arrivée pour les arrêts d’intérêt, bien plus faciles à manipuler que les 26k entrées initiales
  • Les données ont été regroupées par LineRef et DirectionRef, triées par ExpectedArrivalTime, puis les horodatages UTC ont été convertis au format « dans X min »

Générer des PNG pour Kindle avec Skia

  • Le rendu PNG a été construit avec skia-safe
  • Un bitmap Gray8 correspondant à la résolution de l’écran Kindle, 1024x758, est créé, rempli d’un fond blanc, puis le texte et les lignes y sont dessinés
  • L’auteur a d’abord généré une image PNG8 « Hello World » pour valider le pipeline de rendu
  • Ensuite, une image en forme de tableau avec deux colonnes, inbound et outbound, a été produite à partir de fausses données d’horaires
  • Une fois les vraies données d’API branchées, la variété plus grande des destinations et des temps d’arrivée a provoqué des chevauchements de texte dans la mise en page fixe
  • Pour itérer rapidement et éviter les limites de l’API, un code temporaire de mise en cache de la réponse dans data.json a été ajouté

Affiner la mise en page de l’affichage

  • Le premier écran basé sur les données réelles était peu lisible, car la quantité d’informations affichées variait selon les lignes
  • Les améliorations apportées ont été les suivantes
    • aligner à droite les heures d’arrivée pour réduire l’espace perdu en fin de tableau
    • supprimer min après chaque heure et ne l’ajouter qu’à la fin de la liste
    • n’afficher que les 3 prochains véhicules
    • déplacer les noms de destination vers la droite pour tenir compte des noms de ligne plus longs
  • Ensuite, les limites du texte ont été calculées avec measure_str afin d’afficher l’identifiant de ligne dans une bulle grise arrondie
  • La chaîne de destination est placée à 15 pixels à droite du bord droit de cette bulle d’identifiant
  • En haut, un en-tête gris clair a été ajouté avec Muni Inbound et Muni Outbound centrés

Servir le PNG avec Axum

  • Le serveur HTTP a été construit avec Axum 0.7 et renvoie le PNG sur la route /stops.png
  • L’en-tête de réponse est défini sur Content-Type: image/png
  • L’auteur a d’abord vérifié un HTTP 200 avec un corps vide, puis a connecté la fonction de génération d’image pour renvoyer directement les octets PNG depuis la mémoire
  • Lors de la récupération de l’image sur la Kindle avec wget puis de son affichage avec eips, l’image était d’abord tournée et tronquée
  • Comme la Kindle est pensée pour un usage en orientation portrait, il a fallu appliquer une matrice de rotation avant le rendu pour l’afficher en paysage
    • les dimensions de l’image ont été inversées en (758, 1024)
    • le canvas a été tourné de 90.0 degrés

Les fonctions ajoutées à la version réellement utilisée

  • La version utilisée au quotidien ajoute les fonctions suivantes
    • un système de cache qui continue de récupérer les données API en arrière-plan afin que le serveur HTTP réponde rapidement
    • l’affichage de l’heure en bas à gauche et de l’état du cache de données en bas à droite
    • l’anti-aliasing
    • une logique de troncature des noms de destination pour éviter qu’ils ne chevauchent la zone des heures d’arrivée
    • une différenciation rapide des lignes via des niveaux de gris distincts
    • une configuration basée sur un fichier de paramètres
    • un rendu des erreurs et de la chaîne .source() directement en PNG pour pouvoir les voir immédiatement sur la Kindle
  • Le code simplifié du serveur créé pour l’occasion est disponible dans transit-kindle-simplified
  • Le code du serveur qui alimente la Kindle murale réelle est disponible dans transit-kindle
  • La version basée sur les captures d’écran BART fonctionnait vers février 2023, et la finalisation de l’interface de la réécriture Rust s’est achevée en janvier 2024

1 commentaires

 
GN⁺ 2024-10-25
Avis Hacker News
  • Je travaille pour l’organisation à but non lucratif derrière OneBusAway. OneBusAway est un projet open source utilisé chaque jour par des millions de personnes pour consulter la position et les horaires d’arrivée des bus, trains, trams et funiculaires.
    Si votre ville dispose déjà d’un serveur OneBusAway, vous pouvez créer une app personnalisée avec le nouveau SDK : https://github.com/onebusAway/?q=sdk&type=all&language=&sort...
    Si votre ville n’a pas de serveur, les images Docker et la configuration OpenTofu que nous avons créées cette année permettent de transformer des flux GTFS/GTFS-RT en API REST.
    BART fournit aussi du GTFS et du GTFS-RT : https://mobilitydatabase.org/feeds/mdb-53
    Aux États-Unis, d’autres opérateurs de transport public doivent également publier au moins leurs données d’horaires statiques en GTFS, en raison d’une nouvelle réglementation fédérale.
    Nous avons toujours besoin de développeurs pour contribuer aux logiciels associés, et nous recherchons en particulier urgemment des développeurs iOS pour aider à améliorer le service utilisé par 250 000 utilisateurs par jour. Contact : aaron@onebusaway.org
    Docker : https://github.com/OneBusAway/onebusaway-docker / OpenTofu : https://github.com/OneBusAway/onebusaway-deployment

    • Pour l’instant, seules 9 villes sont prises en charge dans le monde, dont 2 en bêta ; il faudrait donc une diffusion plus large pour que les contributions open source deviennent plus faciles.
      J’aimerais aussi ajouter ma ville si la procédure était simple. Il semble que beaucoup de temps d’ingénierie ait récemment été consacré au support des bibliothèques et SDK, mais il vaudrait mieux, à mon avis, investir dans un parcours simple d’expérience développeur afin que les nouveaux contributeurs puissent y consacrer du temps plus facilement.
      Par exemple, cette documentation est assez complexe : https://github.com/OneBusAway/onebusaway/wiki/Multi-Region
    • Je me demande où trouver la liste des villes où OneBusAway est déployé. En cherchant “onebusaway cities”, on tombe sur https://onebusaway.org/onebusaway-deployments/, mais des villes mentionnées ici, comme Adelaide, n’y figuraient pas.
    • Si c’est open source, je me demande pourquoi l’app n’est publiée que sur le Play Store et pas sur F-Droid.
    • Je me demande pourquoi les apps de suivi des bus, pas seulement OneBusAway mais aussi Google Maps, sont aussi imprécises.
      Elles affichent “arrivée dans 4 minutes” alors que le bus part sous mes yeux, ou bien “5 minutes de retard” alors qu’il arrive en réalité 2 minutes en avance. Il arrive aussi que le bus n’apparaisse pas du tout.
      Je suppose que le problème vient des données sources, mais j’aimerais comprendre ce qui, en interne, rend la fiabilité aussi faible. Pour référence, je parle de Seattle.
  • BART et MUNI prennent tous deux en charge la General Transit Feed Specification, et il existe une méthode standard pour obtenir ces données.
    https://gtfs.org/documentation/overview/#gtfs-realtime

    • Je me demande à quel point les informations en temps réel sont fiables. Il m’arrive souvent que Google Maps indique “bus dans 4 minutes” alors que je le vois partir, ou “5 minutes de retard” alors qu’il arrive en fait 2 minutes en avance.
  • J’aime beaucoup. À l’époque où je vivais sans voiture à Boston, j’avais bricolé quelque chose de beaucoup plus rudimentaire pour gérer les cas où il y avait plusieurs façons d’aller de A à B, et où la “meilleure” dépendait de l’heure ou des endroits où l’on s’arrêtait en chemin.
    Par exemple, avant d’aller travailler, j’emmenais mon fils à l’école, et parfois nous prenions le petit-déjeuner après l’avoir déposé. Avec une fonctionnalité voir les prochains départs, il aurait été beaucoup plus facile de décider souplement quoi faire dans des situations non déterministes, comme marcher avec un enfant de 4 ans dans un lieu intéressant, ou savoir s’il fallait se dépêcher pour attraper un train après lequel il y aurait un gros trou dans la fréquence.

  • Je suis surpris que https://oeffi.schildbach.de/index.html n’ait pas encore été mentionné. Il fait exactement ce que l’on veut ici.

  • Au Royaume-Uni, on peut acheter un panneau d’affichage des départs qui imite ceux des gares
    https://ukdepartureboards.co.uk/store/product/desktop-depart...

  • Moi aussi, j’habite à SF, et j’ai créé une app iOS appelée A Better Ride pour résoudre le même problème
    Elle affiche uniquement les horaires de départ des transports en commun aux arrêts à proximité. L’objectif est de réduire le stress en rendant les transports en commun plus prévisibles et plus faciles à explorer. C’est un projet personnel que je développe sur mon temps libre, sans monétisation

    • Cette app est vraiment excellente. Elle se distingue des autres apps de transport par le fait qu’elle montre seulement les lignes autour de soi et où elles vont
      Elle permet d’explorer les endroits où l’on peut aller sans se soucier des correspondances
    • L’app est vraiment superbe. La carte en temps réel est particulièrement réussie
      Je travaille moi aussi sur du matériel dans ce domaine et j’ai beaucoup manipulé GTFS récemment ; on voit tout de suite le travail énorme qu’il a fallu pour parser et afficher les données de transport
      Si ça te va, j’aimerais discuter des choix UX pour résumer et regrouper les trajets possibles depuis chaque arrêt, ainsi que du backend
    • Vraiment très bien. Il y a beaucoup de détails soignés, comme l’indication du côté de la rue et de la direction générale du trajet
  • Il n’est pas nécessaire de jailbreaker le Kindle ni de générer des images
    Il suffit d’ouvrir une page web que l’on a créée soi-même dans le navigateur du Kindle, puis de désactiver les publicités de « veille » ou l’écran d’économie d’énergie du Kindle avec une commande de debug
    La barre du navigateur reste sur un bord du Kindle, mais on peut orienter l’appareil pour la placer en bas ou à droite. En échange, on peut créer un écran météo/transports/actualités en HTML/CSS/JS simple, avec le langage backend de son choix, et le faire tourner sur quelque chose comme une instance DigitalOcean bon marché à 4 dollars
    https://blog.notfaqs.com/2018/06/kindle-e-reader-disable-scr...
    https://www.mobileread.com/forums/showthread.php?t=198334

    • Ce serait encore plus cool si Amazon encourageait ce genre de hack d’affichage et ajoutait un mode kiosque au navigateur du Kindle
    • J’ai fait ça avec un inkBOOK sous Android. J’ai installé l’APK de Chrome et ouvert ma propre page web
      Avec document.documentElement.requestFullscreen(), on passe en vrai plein écran et même la barre d’état disparaît. L’API de transports locale étant accessible directement depuis la page web, je n’avais même pas besoin de backend
    • J’ai regardé avec un Kindle de 4e génération, et il semble presque impossible d’utiliser des sites HTTPS, car les certificats ne sont plus valides
      Cela dit, ça devrait aller si on le fait communiquer avec un serveur sur le réseau local
    • Il est presque certain que la possibilité de désactiver l’écran de veille a été supprimée sur les Kindle plus récents
  • Je crois que je ne comprends pas bien ce cas d’usage. Ce que je veux, ce n’est pas « prendre le train », c’est aller de A à B
    Si le train est en panne, en grève, si la ligne est bloquée entre 13 h et 16 h, ou si ce n’est pas dans les horaires de service, je veux une alternative. Donc ce dont j’ai besoin, c’est ce que Google Maps fournit, ou essaie de fournir
    Si je veux aller du Ferry Building à Oakland et que le BART est en panne, le ferry peut être préférable ; s’il est tard, il ne reste peut-être que le taxi ou Uber
    À Tokyo, pour aller de Shibuya à Azabujuban, les options incluent Ginza Line→Namboku Line, Hanzomon Line→Oedo Line, Yamanote Line→Namboku Line, le bus 6, un taxi, etc.
    Il faut aussi marcher 3 à 8 minutes entre les gares et les arrêts de bus de chaque ligne, donc l’endroit précis où l’on se trouve influe sur le choix. Le fait d’être en retard, ou d’avoir de gros bagages lourds, compte aussi

    • Google Maps semble partir très fortement du principe que marcher est quelque chose de très mauvais, dont personne ne veut
      Par exemple, pour aller quelque part, je peux prendre le bus 39 puis marcher 15 minutes, mais si je demande à Google, il me propose un itinéraire avec 2 correspondances. Avec un peu de chance, c’est à peu près aussi long qu’un trajet bus + marche
      Du coup, si je veux savoir quand arrive le bus 39, je dois volontairement indiquer une autre destination
      Et là où je vis, les données temps réel de Google sont suspectes, tandis que celles de l’opérateur ou de certains tiers sont plus fiables
    • Le cas d’usage, ce sont les déplacements quotidiens. Beaucoup de gens, la plupart du temps, veulent vraiment « prendre le train »
      Il y a des situations où il faut faire une planification sophistiquée en temps réel, et ce serait bien d’avoir ce genre d’outil, mais les déplacements quotidiens sont largement couverts par des outils beaucoup plus simples
      L’exemple de Tokyo que tu donnes est intéressant, mais d’après Google, la plupart des options de transports en commun prennent 30 à 40 minutes, donc elles sont presque équivalentes pour une planification approximative. Le taxi est plus rapide, et le vélo presque comparable
    • Pour mon trajet domicile-travail quotidien, je veux vraiment simplement « prendre le train »
      Si le train est en panne ou en grève, cela se voit dans la liste des trains en temps réel par un vide inattendu
      Quand je me déplace en ville, j’ouvre Google Maps et j’entre ma destination, mais quand je pars de chez moi pour aller à un endroit où je suis déjà allé 500 fois, je ne le fais pas
    • L’expérience des transports en commun dans le centre de Tokyo ne peut pas être généralisée au monde entier
      Ce serait aussi peu pertinent que de parler de shopping ou de restauration en se basant uniquement sur l’expérience à l’intérieur de Disneyland. Rien que le fait de valider avec Suica est plusieurs fois plus rapide qu’ailleurs, et ce n’est même pas pris en charge dans certains endroits
    • https://citymapper.com pourrait te plaire
      Il a beaucoup de fonctionnalités, mais je ne sais pas à quel point Tokyo est bien pris en charge. À Londres, si on lui demande de nous ramener chez nous, il montre plusieurs options optimisées selon le coût, la distance à pied, la vitesse, les correspondances, l’accessibilité, etc.
  • J’utilise quelque chose de similaire avec Home Assistant et une intégration aux transports locaux sur une tablette / appareil de salle de réunion Lenovo ThinkSmart View recyclé.
    L’avantage, c’est que c’est beaucoup plus prêt à l’emploi. Bien sûr, j’ai quand même installé une ROM Android personnalisée sur le Lenovo, mais on peut afficher à peu près tout ce qu’on veut, et aussi contrôler des choses comme l’éclairage ou un robot aspirateur.

  • Il existe aussi un superbe produit qui transforme la carte du BART en panneau LED physique : https://www.designrules.co/