A highly configurable iPhone and iPad Home Screen widget, which uses the Scriptable app to show upcoming Transperth train services from and to the stations of your choosing, and caches information in case of connectivity loss or API failure.
Warning
Use at your own risk
Whilst all reasonable efforts have been made to ensure that this delivers reliable and accurate information, Live transport information may be delayed, incomplete, unavailable, or changed at short notice. Always follow official station signage, announcements, staff directions, and current Transperth information when making travel decisions.
Important
Unofficial community project
This is an unofficial community project. It is not affiliated with, endorsed by, or supported by the Public Transport Authority of Western Australia, Transperth, Apple, or Scriptable.
- Contents
- Features
- How it works
- Requirements
- Installation
- How does it do what it does?
- Quick configuration examples
- A note on previewing a widget in Scriptable
- Complete configuration reference
- Journey settings
- Visible departure limits
- Cache depth and age
- Cancellation settings
- Refresh scheduling
- Fresh-cache reuse
- Cache compatibility
- Network and 429 protection
- Multiple-instance identity
- Display controls
- Expected countdown and sorting
- Accessibility settings
- Small-widget controls
- Theme colours
- Diagnostics controls
- Cancellation handling
- Delays and departure sorting
- Widget layouts
- Accessibility
- Refresh and rate-limit protection
- Caching and multiple widget instances
- Diagnostics
- Common errors and troubleshooting
- Frequently asked questions
- Can the widget handle more than one train line?
- Must every possible terminal be configured?
- Can more than one destination be accepted?
- Can several copies run at once?
- Why does one copy have a cache while another does not?
- Why does the widget show stale data?
- Why is a train shown as
Due? - Why did train order change?
- Does the widget know every disruption type?
- Does the widget refresh exactly every 10 or 60 minutes?
- Can logging be disabled?
- Upgrading
- Known limitations
- Credits and acknowledgements
- Change log
- Disclaimer
- Journey-aware results across every train line serving the configured stations.
- Combined
Allstation-board requests. - Exact
TripIdcorrelation for through-running trains. - Terminal-destination fallback for trains ending at an accepted destination.
- One origin and one or more acceptable destination stations.
- Real-time, delay-aware departure countdowns.
- Scheduled or expected departure sorting.
- Four delay severity bands.
- First-class cancellation detection and display policies.
- Cancelled services excluded from usable cache coverage.
- Separate small, medium, and large layouts.
- High-contrast mode and three font profiles.
- Standard and countdown-focused layouts.
- Long-horizon cache containing up to 180 minutes of available future information.
- Render-time removal of departed cached services.
- Isolated caches for different scripts and journey configurations.
- Shared short-lived station-board data to reduce duplicate API traffic.
- Shared HTTP 429 backoff across all instances.
- Cache provenance, migration, coverage, and exhaustion diagnostics.
- No credentials, API keys, accounts, packages, or third-party JavaScript libraries required.
- No username, password, API key, or token is required.
- No device location permission is requested.
- Configured station names are sent to Transperth in live-board requests.
- Cache, station-board, and backoff data are stored locally in Scriptable's documents directory.
- The script itself includes no analytics, advertising, telemetry, or third-party tracking; however this does not prevent or suggest that Transperth or the Public Transport Authority doesn't use such services to analyise requests made to its servers.
The widget answers these passenger-focused questions.
- Which upcoming trains from my departure station are confirmed to call at an acceptable destination?
- When does each train depart?
- What platform does each train depart from?
- Are any of the trains delayed, and if so by how long?
- How many cars long is the train?
- What type/series of train is it?
The answers to these questions are all obtained using Transperth's live data, and rendered to the user in a friendly format that can be cached locally to reduce network requests, and also be updated on demand if the user needs to.
- iPhone or iPad with Scriptable installed.
- Internet access for live Transperth information.
- A valid origin station name.
- At least one valid destination station name.
- A small, medium, or large Scriptable Home Screen widget.
- Install the Scriptable app on your Apple iOS/iPadOS device.
- Download the complete
code.jsfile from the repository. - Open Scriptable.
- Import the JavaScript file, or create a new script and paste the complete source.
- Keep the Scriptable metadata comments at the very beginning of the file.
- Edit the values inside
CONFIGfor the required journey and preferences. NOTE: Follow the steps at Finding correct station names to get ther correct values for your config. - Run the script directly in Scriptable to validate the configuration, and view the results.
- Add a Scriptable widget to the Home Screen.
- Long-press the widget and select Edit Widget.
- Select this script.
- Repeat for any additional sizes or journey combinations.
Use Transperth's official Live Train Times page:
https://www.transperth.wa.gov.au/Timetables/Live-Train-Times
Recommended process:
- Open the Live Train Times page.
- Select All as the line.
- Open the station selector.
- Copy the station label exactly.
- Use the exact value in
originStationordestinationStations.
Common examples include:
Joondalup Stn
Mt Lawley Stn
Perth Stn
Perth Underground Stn
Airport Central Stn
Perth Stn and Perth Underground Stn are different station boards. Configure the station actually served by the required train. Add both only when either station is genuinely acceptable for the journey.
Keep the API selector as:
apiLine: "All"Values such as All Lines or [All Lines] are not valid substitutes for the verified route value.
For each live update, the widget:
- Requests the combined
Allboard for the origin station. - Requests the combined
Allboard for each configured destination station. - Runs the required requests concurrently.
- Indexes destination-board entries by
TripId. - Looks for the same
TripIdat a destination station later than the origin departure. - Rejects implausible matches beyond
maximumJourneyHours. - Accepts a train when a valid later destination call exists.
- Uses a normalised terminal fallback when the train terminates at an accepted destination.
- Classifies cancellations separately from usable services.
- Sorts and renders usable services according to the configured widget size.
This allows a train displaying a terminal beyond the passenger's destination to be included correctly. For example, a train displaying Claremont can still qualify for a journey to Perth when its TripId appears at Perth later.
Platform assignments differ between stations and may change during disruptions. Platforms remain available for display, but the journey engine uses confirmed train calls rather than assuming that a particular platform always represents one direction.
The supplied script is configured for Mt Lawley to Perth:
journey: {
originStation: "Mt Lawley Stn",
originAlias: "Mt Lawley",
destinationStations: ["Perth Stn"],
destinationLabel: "Perth",
apiLine: "All",
maximumJourneyHours: 4,
allowTerminalDestinationFallback: true
}journey: {
originStation: "Joondalup Stn",
originAlias: "Joondalup",
destinationStations: ["Perth Underground Stn"],
destinationLabel: "Perth",
apiLine: "All",
maximumJourneyHours: 4,
allowTerminalDestinationFallback: true
}Use case: Trains from the departure station may terminate at multiple locations, or you might want to get off at a different station.
journey: {
originStation: "Bayswater Stn",
originAlias: "Bayswater",
destinationStations: [
"McIver Stn",
"Perth Stn"
],
destinationLabel: "Perth CBD",
apiLine: "All",
maximumJourneyHours: 4,
allowTerminalDestinationFallback: true
}The end of the script uses a large preview by default:
if (!config.runsInWidget) {
await widget.presentLarge();
}For testing, change only the preview call to one of:
await widget.presentSmall();await widget.presentMedium();await widget.presentLarge();The preview call does not control the size of an installed Home Screen widget.
Exact Transperth station name where the passenger boards.
originStation: "Mt Lawley Stn"Shorter name used in the widget heading. Leave blank to derive a name by removing a trailing Stn.
originAlias: "Mt Lawley"One or more acceptable destination station names.
destinationStations: ["Perth Stn"]Human-readable heading label.
destinationLabel: "Perth"Keep this set to the combined board selector:
apiLine: "All"Maximum accepted time between the origin departure and destination call.
maximumJourneyHours: 4Increase this only for a legitimately longer train journey.
Includes a train that terminates at an accepted destination even when the destination departure board cannot show a later call.
allowTerminalDestinationFallback: truedeparturesShown: {
small: 2,
medium: 3,
large: 6
}These limits control visible usable services. Cancellation alerts have separate limits and do not consume usable-service positions when the alert policy is selected.
cacheDepartureBuffer: 2,
cacheHorizonMinutes: 180,
maximumCachedDepartures: 40,
cachedDepartureGraceMinutes: 2,
maximumCachedRealtimeAgeMinutes: 180,
staleStatusDisplay: "suppress",
staleStatusReplacement: "Cached schedule"cacheDepartureBuffer: retained compatibility setting for additional cached capacity.cacheHorizonMinutes: maximum forward horizon the script attempts to retain from a successful API response.maximumCachedDepartures: defensive maximum number of stored matched records.cachedDepartureGraceMinutes: brief grace period before a past departure is removed during rendering.maximumCachedRealtimeAgeMinutes: maximum age at which cached real-time estimates may remain usable.staleStatusDisplay: action for stale real-time row status after the configured threshold.staleStatusReplacement: replacement text whenstaleStatusDisplayisreplace.
Supported stale-status policies are:
staleStatusDisplay: "show"staleStatusDisplay: "suppress"staleStatusDisplay: "replace"The 180-minute cache settings are upper limits. Actual coverage depends on how many future services Transperth supplies in a successful response.
cancelledServiceDisplay: "alert",
cancelledAlertsShown: {
small: 1,
medium: 1,
large: 1
},
cancelledLabel: "Cancelled"See Cancellation handling for policy behaviour.
normalRefreshMinutes: 60,
travelRefreshMinutes: 10,
useTravelWindows: true,
travelWindows: [
{ start: "06:00", end: "08:30" },
{ start: "16:00", end: "18:30" }
],
refreshAtTravelWindowStart: truenormalRefreshMinutes: requested refresh interval outside travel windows.travelRefreshMinutes: requested interval during travel windows.useTravelWindows: enables the travel-window policy.travelWindows: local device times inHH:MMformat.refreshAtTravelWindowStart: requests a refresh at the next travel-window start when earlier than the next normal refresh.
Overnight windows are supported:
{ start: "22:00", end: "01:00" }reuseFreshCacheWithoutRequest: false,
freshCacheReuseMinutes: 1When enabled, a sufficiently fresh cache can be rendered without a new request.
staleMinutes: 10,
maximumCacheAgeMinutes: 180,
cacheSchemaVersion: 7staleMinutes: age after which data receives a prominent stale footer.maximumCacheAgeMinutes: age after which the cache is rejected completely.cacheSchemaVersion: internal compatibility version. Do not edit unless developing the cache format.
requestTimeoutSeconds: 15,
retryTransientFailures: true,
maximumRequestAttempts: 2,
retryDelayMilliseconds: 400,
rateLimitBackoffMinutes: 15,
sharedBoardReuseSeconds: 45- Requests have a bounded timeout.
- Only transient failures are retried.
- The implementation permits at most two attempts.
- HTTP 429 is not retried immediately.
- A 429 creates a shared backoff across every widget instance using the same cache prefix.
- Recent station boards can be reused briefly across separate journey scripts.
instanceId: ""Leave blank for automatic isolation based on the Scriptable script name and journey-affecting configuration.
Set a stable explicit value when two copies would otherwise have the same name and configuration:
instanceId: "weekday-commute"Changing the script name, instanceId, journey, sort mode, expected-time policy, or schema may create a new isolated cache.
showBoardScope: true,
showDestination: true,
destinationStyle: "full",
showServiceLine: true,
serviceLineStyle: "short",
showPlatform: true,
showCars: true,
showTrainSeries: true,
showStatus: true,
showUpdatedTime: trueSupported destination styles:
destinationStyle: "full"destinationStyle: "short"Supported service-line styles:
serviceLineStyle: "short"serviceLineStyle: "full"useExpectedCountdowns: true,
sortMode: "scheduled"Supported sort modes:
sortMode: "scheduled"sortMode: "expected"Scheduled sorting is stable and follows timetable order. Expected sorting can reorder trains as delays change.
accessibility: {
highContrastMode: false,
fontProfile: "normal",
layoutProfile: "standard"
}Supported font profiles:
normal
large
extra-large
Supported layout profiles:
standard
countdown
smallWidget: {
showDestination: false,
showServiceLine: true,
showPlatform: false,
showCars: true,
showTrainSeries: true,
showStatus: false
}darkMode: true,
colours: {
dark: {
background: "#0D1117",
primary: "#FFFFFF",
secondary: "#8B949E"
},
light: {
background: "#F8F9FA",
primary: "#111111",
secondary: "#5F6368"
},
live: "#30D158",
delayed: "#FFB000",
moderate: "#FF7A00",
severe: "#FF453A",
cancelled: "#FF453A",
information: "#64D2FF",
cached: "#FFD60A",
unavailable: "#8B949E"
}All colours must use six-digit hexadecimal notation.
cachePrefix: "transperth-journey-widget",
debugShowCounts: false,
debugShowDiagnostics: false,
debugLogging: true,
debugCacheLogging: trueLogging is enabled in the supplied build to support initial validation. Disable both log settings after validation if quieter logs are preferred.
Version 4.6 treats cancellations as an operational state rather than ordinary status text.
Cancellation detection is case-insensitive and recognises both spellings:
Cancelled
Canceled
Longer phrases containing either spelling are also detected.
cancelledServiceDisplay: "alert"A cancelled train is:
- Displayed as a separate red alert.
- Shown with
Cancelledinstead of a countdown. - Excluded from usable-service row limits.
- Prevented from displacing the next usable train.
- Excluded from determining the first or last useful cache coverage.
- Retained in total and cancelled cache counts for diagnostics.
cancelledServiceDisplay: "show"Cancelled services remain in chronological order within the ordinary service list. The countdown position displays Cancelled in red and duplicate status text is suppressed.
A cancelled service consumes a visible row under this policy.
cancelledServiceDisplay: "hide"Cancelled services are omitted from the widget display. They remain identifiable in cached diagnostic counts and do not extend usable coverage.
cancelledAlertsShown: {
small: 1,
medium: 1,
large: 1
}These limits apply only to the separate alert policy.
Coverage metadata distinguishes:
departureCount
usableDepartureCount
cancelledDepartureCount
firstUsableDeparture
lastUsableDeparture
firstCancelledDeparture
lastCancelledDeparture
The general firstDeparture and lastDeparture coverage values represent usable, non-cancelled services.
When useExpectedCountdowns is enabled, the widget uses the live departure clock supplied by Transperth. If that clock is unavailable, the widget falls back to the scheduled station time plus a numeric delay parsed from status text.
The clock-only live value is interpreted near the scheduled date, including services that cross midnight.
| Delay | Default presentation |
|---|---|
| On time | Green |
| 1 to 4 minutes | Amber |
| 5 to 9 minutes | Orange |
| 10 minutes or more | Red |
| Cancelled | Red cancellation state |
| No real-time data | Grey |
Cancellation takes precedence over all delay classifications.
- Two usable services by default.
- Compact two-line service presentation.
- Separate detail controls.
- One cancellation alert by default under the alert policy.
- Three usable services by default.
- Compact one-line service rows.
- One separate cancellation alert by default.
- Six usable services by default.
- Two-line usable service rows.
- First line contains time, countdown, and status.
- Second line contains destination, line, platform, cars, and train series.
- One separate cancellation alert by default.
Enable maximum contrast with:
highContrastMode: trueChoose larger typography with:
fontProfile: "large"or:
fontProfile: "extra-large"Larger profiles reduce lower-priority information density to preserve legibility.
Make the countdown dominant with:
layoutProfile: "countdown"Return to the default presentation with:
layoutProfile: "standard"The script requests a preferred refresh time, but iOS and iPadOS ultimately decide when a Home Screen widget runs.
The canonical anti-429 defaults are:
normalRefreshMinutes: 60,
travelRefreshMinutes: 10,
rateLimitBackoffMinutes: 15,
sharedBoardReuseSeconds: 45When Transperth returns HTTP 429:
- The rejected request is not retried immediately.
- A shared rate-limit backoff is written locally.
- Other Transperth widget instances check the same backoff before making requests.
- Each widget continues using its own isolated journey cache.
- The next preferred refresh is delayed until backoff expiry when required.
Example footer:
⚠ Rate limited • Cached 22:05
If no valid cache exists, the widget reports the rate limit and retry time instead of displaying a generic error.
Each journey cache identity includes:
- Scriptable script name.
- Optional
instanceId. - Origin station.
- Sorted destination station list.
- API line selector.
- Maximum journey duration.
- Terminal fallback policy.
- Expected-countdown policy.
- Sort mode.
- Cache schema.
This prevents one journey from overwriting another.
Raw station-board data may be reused for 45 seconds by another script requiring the same station. Final matched journey caches remain isolated.
Cached departures are re-evaluated whenever the widget renders. Services more than the configured grace period in the past are removed, and later cached services move into the visible positions.
When no useful future service remains, the widget displays:
No future cached services
The last cached service departed at 21:18.
Live updates will retry after 22:04.
Footer:
⚠ Cache exhausted
Cancelled trains do not falsely keep the cache useful after every non-cancelled service has departed.
The cache records whether it is:
native
legacy-migrated
A migrated legacy cache can be structurally valid while having less forward coverage than a native v4.6 cache. One successful v4.6 live update replaces the migrated profile with a native profile and populates the available extended horizon.
With logging enabled, the script reports:
- Instance fingerprint.
- Isolated cache path.
- Cache acceptance or rejection reason.
- Cache age and provenance.
- Cache coverage and usable/cancelled counts.
- Legacy migration coverage.
- Request station and attempt number.
- Shared station-board reuse.
- Failure classification.
- Cache fallback decision.
- Shared backoff time in UTC and local time.
- Refresh-scheduling decision.
Enable:
debugShowDiagnostics: trueThe footer adds the instance fingerprint and cache age.
Enable counts with:
debugShowCounts: trueDisable both after troubleshooting to preserve space.
| Message or symptom | Meaning | First action |
|---|---|---|
Rate limited |
HTTP 429 and no valid cache. | Wait until the displayed retry time. |
Rate limited • Cached |
HTTP 429 with usable cached services. | No immediate action; allow the backoff to expire. |
Cache exhausted |
A valid cache exists but no non-cancelled future service remains. | Wait for the next live update. |
Data Xm old |
Usable information remains, but the source is stale. | Check logs, connectivity, and backoff state. |
Network error |
A connection or timeout prevented complete correlation. | Restore connectivity and run once in Scriptable. |
Configuration error |
A required value or station name is invalid. | Compare values with Transperth Live Train Times. |
API response error |
Transperth returned unusable content. | Check the official service and retain logs. |
No matching services |
Boards loaded but no usable journey matched. | Verify the journey and destination station. |
| Blank widget | JavaScript or rendering failure. | Run the script directly and inspect the first log error. |
Check the configured policy:
cancelledServiceDisplay: "hide"Use alert or show to display cancellations.
This is expected under:
cancelledServiceDisplay: "alert"The alert does not consume one of the usable-service positions.
This is expected under:
cancelledServiceDisplay: "show"Use alert to preserve useful departure capacity.
A new or changed instance may not yet have completed a successful live update. Wait for the backoff to expire, then allow one successful run to create the isolated cache.
Do not repeatedly run the script during an active backoff.
Check:
- Exact station names.
apiLine: "All".- At least one destination.
- Positive numeric intervals and limits.
- Supported cancellation, sort, stale-status, font, and layout values.
- Six-digit hexadecimal colours.
Common log reasons include:
file does not exist
schema mismatch
configuration fingerprint does not match
cache is too old
read or JSON error
A rejected cache is not used silently. Allow a successful live run to create the correct cache after resolving any configuration problem.
- Run the script directly in Scriptable.
- Read the first error and line number.
- Confirm the entire release file was installed.
- Confirm the metadata comments remain at the top.
- Undo manual edits outside
CONFIG. - Replace the complete script with a known-good release rather than combining partial patches.
Yes. The All board includes services across every applicable line, and journey matching selects trains confirmed to serve the destination.
No. Through trains are identified using TripId. Terminal fallback handles trains ending at an accepted destination.
Yes. Add each exact station name to destinationStations.
Yes. Journey caches are isolated. Use different Scriptable names or explicit instanceId values for otherwise identical copies.
Each identity starts with its own cache. A new instance requires a successful live update or compatible migration before fallback data exists.
Stale cached information may still be more useful than no information. The footer clearly reports the age, departed services are removed, and cache exhaustion is shown when no useful service remains.
The expected or scheduled departure has been reached. The service remains briefly during the configured grace period because API and widget refresh timing are not instantaneous.
Expected sorting can reorder services as real-time estimates change. Use scheduled for stable timetable order.
No. Version 4.6 explicitly recognises cancellations and numeric delays. Other disruption wording is displayed but may not receive specialised interpretation.
No. The values are preferred earliest refresh times. iOS or iPadOS controls actual scheduling.
Yes:
debugLogging: false,
debugCacheLogging: false- Save the current complete script as a rollback copy.
- Copy the current
CONFIGblock into a private temporary note. - Record the script name and any explicit
instanceId. - Read the target release notes and cache migration guidance.
- Import the complete new release as a separate Scriptable script.
- Transfer configuration values into the new release's current
CONFIGstructure. - Do not overwrite a new configuration block wholesale if its structure has changed.
- Run the new script directly in Scriptable.
- Confirm the intended journey, cache identity, and log paths.
- Test the required widget sizes.
- Confirm a live result or documented cache migration.
- Assign one Home Screen widget to the new script.
- Upgrade additional journeys one at a time.
- Keep the previous scripts until every upgraded instance has completed a successful live update.
- No JavaScript error occurs.
- The correct journey is shown.
- Through trains are included.
- Opposite-direction trains are excluded.
- Cancellations follow the selected policy.
- Cancelled trains do not extend useful coverage.
- Countdown and sorting policies behave correctly.
- All required layouts are readable.
- Cache and state paths are isolated correctly.
- Shared rate-limit behaviour is intact.
- The Home Screen widget opens the intended Transperth page.
If validation fails, reassign the Home Screen widget to the previous complete script. Preserve the failing log and do not delete the previous cache until rollback is confirmed.
- The Transperth endpoint used by this project is unofficial and undocumented and may change without notice.
- Journey matching depends on stable
TripIdvalues between station boards. - A through service must appear within the destination board's current window.
- Terminal fallback depends on normalised destination naming.
- Numeric delay interpretation depends on status text such as
6 min delay. - Cancellation recognition depends on status text containing
CancelledorCanceled. - Other disruption types are not yet first-class states.
- Car count, train series, platform, and status depend on data supplied by Transperth.
- Actual cache coverage cannot exceed the future information returned by the API.
- iOS and iPadOS control actual widget refresh timing.
- Michael H: project concept, requirements, configuration design, user experience direction, API diagnostics, real-world validation, layout evaluation, cache and rate-limit design requirements, cancellation requirements, and production acceptance.
- Microsoft 365 Copilot: collaborative architecture, implementation, optimisation, debugging support, test harness development, and documentation.
- Transperth and the Public Transport Authority of Western Australia: source of the public transport services and live train information consumed by the widget. Transperth and PTA retain ownership of their names, services, data, and trademarks.
- Scriptable by Simon Støvring: JavaScript automation and widget runtime used by the project.
- Apple: iOS and iPadOS Home Screen widget platform.
Version 4.6 has no package dependencies and does not incorporate copied code from another widget or third-party library. The implementation was developed collaboratively from direct endpoint diagnostics, observed API behaviour, Scriptable APIs, and iterative real-world testing.
- Added first-class cancellation detection.
- Recognised
CancelledandCanceledcase-insensitively. - Added
alert,show, andhidecancellation policies. - Added separate cancellation-alert limits by widget size.
- Added red cancellation styling.
- Replaced misleading countdowns with
Cancelled. - Suppressed duplicate cancellation status text.
- Prevented cancellation alerts from consuming usable-service positions under the default policy.
- Promoted later usable services into normal rows.
- Added usable and cancelled cache counts.
- Added first and last usable and cancelled coverage fields.
- Excluded cancelled trains from useful cache coverage and cache exhaustion decisions.
- Increased canonical cached real-time retention to 180 minutes.
- Preserved the 60-minute normal and 10-minute travel refresh strategy.
- Added detailed legacy-cache migration metadata.
- Added cache provenance and creation/migration version fields.
- Added extended-horizon population state.
- Added local and UTC retry timestamps to logs.
- Improved cache-exhausted passenger wording.
- Distinguished native and migrated limited-coverage caches.
- Separated cache horizon from visible row limits.
- Added a 180-minute forward cache horizon and 40-record safety limit.
- Re-evaluated cached departures at every render.
- Removed departed trains and promoted later cached services.
- Added stale real-time handling and scheduled fallback.
- Added
Cache exhaustedstate and coverage metadata. - Added shared global 429 backoff.
- Added shared short-lived station-board data.
- Added isolated cache identities for multiple scripts and journeys.
- Added optional
instanceId. - Added precise failure classifications.
- Added HTTP 429 backoff and cached fallback messaging.
- Added cache validation and migration diagnostics.
- Added on-widget instance and cache-age diagnostics.
- Added delay-aware countdowns.
- Added scheduled and expected sorting.
- Added green, amber, orange, and red delay bands.
- Added high-contrast mode.
- Added normal, large, and extra-large font profiles.
- Added countdown-focused layout.
- Strengthened cached, stale, and unavailable indicators.
- Replaced terminal-only filtering with journey-aware matching.
- Added combined
Allstation boards. - Added concurrent origin and destination requests.
- Added exact
TripIdcorrelation. - Added terminal fallback and multiple destinations.
- Removed platform assumptions from direction filtering.
- Preserved complete-cache fallback when any required board failed.
- Added a dedicated two-line large-widget service layout.
- Preserved the accepted small and medium layouts.
- Introduced the optimised caching, validation, retry, refresh-scheduling, view-model, and rendering architecture.
- Parsed dates once per execution.
- Cached style objects.
- Stored compact API records.
- Added bounded retries and configuration validation.
- Avoided unnecessary cache writes.
- Preserved the dedicated readable small layout.
- Added the dedicated small-widget renderer.
- Preserved time and countdown prominence.
- Moved compact train details to a second line.
- Established the readable small-widget baseline.
- Consolidated configuration.
- Added dynamic live and tap-through URLs.
- Added destination filtering, countdowns, status, train details, caching, and commute refresh periods.
- Identified the need for a dedicated small-widget layout.
- Established the Scriptable Transperth train-widget concept.
- Added live station-board retrieval.
- Added configurable station and line values.
- Added basic departure, destination, platform, train, status, cache, theme, and refresh presentation.
Special acknowledgement goes to Chris112, author of aiotransperth, whose work on that client helped immensely with devising the JS code for this widget.
aiotransperth is an independent, MIT-licensed asynchronous Python client for Transperth bus and train departure information. The project documents and implements access to the unofficial internal endpoints used by the Transperth website, including train destination, platform, delay status, car count, scheduled time, and real-time estimates.
The project and its associated Transperth API research provided a useful external reference during the investigation and validation of:
- The unofficial Transperth train live-status endpoint
- Required request headers
- Valid train line and station naming
- The train departure response structure
- Scheduled and estimated departure concepts
- Real-time delay and status information
- Rate-limiting considerations
The Transperth Journey-Aware Train Widget is a separate JavaScript implementation written for Scriptable. It does not import, bundle, or depend on the aiotransperth Python package, and no aiotransperth source code is included in this repository.
- Project:
Chris112/aiotransperth - Author: [
Chris112](https://github.com/cence: MIT - Language and runtime: Python 3.12+ with
aiohttp
Use this project at your own risk. Live transport information may be delayed, incomplete, unavailable, or changed at short notice. Always follow official station signage, announcements, staff directions, and current Transperth information when making travel decisions.
