anthodev/lapin-futepublic Fork
⑂ develop
Commits
⬇ Clone ▾
git clone https://git.rickub.com/anthodev/lapin-fute.git
git clone ssh://git@rickub.com/anthodev/lapin-fute.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP
README.md

About

Lapin Futé puts Île-de-France public-transport arrivals on a Pebble watch. The watch shows a list of favorite journeys with their next arrivals, an arrival board for each, and the current service messages for its line. A companion on your phone fetches the data from PRIM, Île-de-France Mobilités' open-data platform, and prepares everything the watch displays; the watch itself never goes online.

Features

  • Six favorite journeys, five transport modes. A favorite is a departure stop, a line, and a reachable arrival stop, including intermediate stops that no service terminates at. Journeys stay on one line, without connections, across Métro, RER, Transilien, Tram, and Bus. Favorites are renamed and reordered on the phone page, which shows official line badges and colors.
  • Arrivals at a glance. The favorites list shows the next arrival for each journey; Select opens its arrival board. Confirmed full-line and short-turn services are combined chronologically. Departures known not to serve the journey are excluded; uncertain departures follow the confirmed ones, marked with ? and retaining their real times. Conflicting terminal hints can still confirm a journey when every candidate route serves its arrival. Countdowns keep ticking on the watch between refreshes.
  • Traffic view. Select again on an arrival board to read the most recently updated active disruption for that line, using PRIM's lastUpdate rather than severity. Traffic normalization accepts up to 256 application periods per disruption; payloads exceeding this limit remain rejected. Active works participate in the same selection and can report stopped or delayed service; future, expired, and information-only notices remain excluded. Periods are reevaluated on existing request triggers, not by a new timer. If the selected English message lacks a usable title or body, the app uses the complete French message for that same incident. This may require one additional French request when no fresh French response is already available.
  • Refresh when needed. Open the app, switch favorite, choose Refresh all, or hold Select to request arrivals. Recent results are reused for 60 seconds; after a failed refresh, the last complete result remains visibly stale for at most 15 minutes. There is no periodic network polling.
  • Clear data states. The app distinguishes real-time arrivals, mixed real-time and scheduled results, updates in progress, missing cached data, and trips whose route to your arrival is unconfirmed (?).
  • English and French. In settings, choose Automatic to follow the watch system language, or force Français or English for the watch app. Save applies the choice even when favorites are unchanged. The phone remembers it across restarts; reopening settings uses the effective language.
  • Settings that fit your phone. Light and dark themes follow your system preference. Collapsible sections keep favorite editing, PRIM access, and watch synchronization easy to reach.

Compatibility

Requirement Details
Watches Pebble Time 2 (emery profile, primary target); the Pebble Round 2 profile is experimental
Firmware 4.32 or newer
Phone Pebble mobile app running the companion; internet access needed for fresh data
Building from source Node.js 24.18.0, Bun, Pebble SDK 4.33.1, pebble-tool 5.0.40

Getting started

  1. Install Lapin Futé from RePebble.
  2. In the Pebble mobile app, open Lapin Futé's settings. Under PRIM access, paste a personal access token generated on the PRIM portal.
  3. Add up to six favorite journeys: search the departure stop, pick the line and boarding point, then pick a reachable arrival stop, including intermediate stops. Searching and adding work without a token. Save, and your settings sync to the watch. Favorites saved with an earlier version keep their name, order, and labels; their arrival is filled in automatically from the updated catalog, and until that succeeds they show as unresolved. A settings page opened by an older companion stays read-only until the watch app is updated.
  4. Open Lapin Futé on the watch. Arrivals refresh whenever the app opens, you change favorite, or you hold Select; the phone needs to be connected for fresh data.

Developers can also build and install the app locally using the instructions in Build from source.

Watch controls

Screen Control Action
Favorites Up / Down Move through favorites and the Refresh all row
Favorites Select Open the highlighted favorite
Favorites · Refresh all Select Refresh every favorite
Arrivals Up / Down Switch favorite
Arrivals Select Open the traffic view
Arrivals Hold Select Force a refresh
Traffic Up / Down Page through messages
Any screen Back Return to the previous screen

Countdowns and the clock keep updating on the watch every minute; only the actions above trigger network requests.

Privacy

Your PRIM token stays on your phone. It is stored locally and sent only as the request header of the companion's direct HTTPS calls to PRIM, which is operated by Île-de-France Mobilités. It never reaches the watch, your favorites, cached data, URLs, or logs. Browsing the settings page and its station catalog sends no credentials. The app has no telemetry, no analytics, and no background or periodic network activity.

Build from source

Install the pinned prerequisites from the table above; no repository script installs a toolchain for you.

npm test
npm run build

npm run build runs the test suite, generates the bilingual display copy, bundles the watchapp for emery and gabbro, and verifies the result. The watch build needs no catalog and no network. Its one optional input is the settings-page URL:

LAPIN_FUTE_CONFIG_URL=https://example.org/lapin-fute/ npm run build

The station catalog is generated ahead of time from open IDFM datasets. Format 4 embeds each line's journey patterns — stop sequences with boarding and alighting restrictions, plus the terminal aliases the phone matches against announced destinations — and there is no in-place upgrade: an existing catalog must be rebuilt and republished in full. Until that catalog is live, the settings page cannot list arrival stops or resolve migrated favorites, and on the watch a favorite whose arrival never resolved shows no departures while one whose line patterns are missing shows its departures as uncertain. With IDFM_DATASET_TOKEN in .env:

node --env-file=.env scripts/refresh-catalog.mjs   # download, build, and publish the catalog
npm run build:config-site                          # assemble var/config-site; fails without a published catalog

node --env-file=.env scripts/probe-catalog.mjs runs a small live check against PRIM, one request per transport mode, and needs a PRIM_API_KEY. Operator tokens live only in .env and are never printed or embedded in build output.

To rebuild the hosted catalog independently, run Rebuild and publish IDFM catalog from Rickub Actions, using .github/workflows/rebuild-catalog.yml. It uses the existing production environment's IDFM and Bunny credentials and publishes only catalog files, with up to 75 concurrent curl uploads. The manifest is uploaded last. It does not build the PBW, deploy the settings page, or create a Rickub release.

Uploads are skipped when the hosted IDFM source revision is unchanged. Enable force_catalog_upload when republishing a catalog format change from the same source data. The workflow retains the previous manifest for rollback and shares the production publication lock with page deployments and rollbacks. Release tags continue through deploy-bunny.yml, without rebuilding the catalog. Release deployments install the pinned pebble-tool on a uv-managed Python 3.11, because the runner's system Python has no ensurepip, and require the production environment's BUNNY_STORAGE_ENDPOINT variable and BUNNY_STORAGE_PASSWORD secret; when either is unset, the deployment fails at the backup gate with an error naming the missing one. The backup step feeds its file loop through a plain pipe rather than process substitution, which hardened runners may not provide. After tests, packaging, and deployment pass, the publish job creates a Rickub release through the runner's GITHUB_API_URL with the tagged changelog notes and attaches the PBW and SHA256SUMS. The Store job reads those published notes through the same API.

Contributing

Bug reports are welcome on the issue tracker. A useful report includes the watch model and firmware, the phone setup, the steps to reproduce, and whether the data shown was live or cached. Never paste your PRIM token or other personal data in an issue.

Code changes should keep the frozen message contracts and the phone-only credential boundary intact.