Know when to step outside and look up. This custom integration predicts when the International Space Station will be visible from your home: above your horizon, lit by the Sun, while your sky is dark. It gives you advance notice, the direction to look, how high it will climb, and how bright it will be, a calendar of the visible passes, and an ISS Tracker page with a 3D Earth that shows the station flying round it at its true altitude.
All prediction runs locally inside Home Assistant using SGP4, the standard model for published satellite orbital elements and the one that pass-prediction sites such as Heavens-Above and N2YO also rely on. Predictions need only the station's published orbit, downloaded a few times a day and cached so they keep working through internet outages. The 3D globe additionally loads map imagery in your browser (see Data sources).
| Built-in ISS integration | Satellite Tracker (N2YO) | This integration | |
|---|---|---|---|
| Current position | Yes (Open Notify) | Yes (N2YO) | Yes, computed locally |
| Visible pass predictions | No (Open Notify retired its pass API) | Yes, computed by N2YO's servers | Yes, computed locally |
| Works offline | No | No | Yes, from cached orbit data (up to 14 days old) |
| Needs an account / API key | No | Yes | No (N2YO key optional) |
| Sends your location to a third party | No | Yes, every poll | No (unless you add an N2YO key)¹ |
| Rate limits | n/a | 100 pass requests/hour, shared by all satellites | Downloads orbit data every 6 h |
| 3D globe | No | No | Yes: day and night, city lights, orbit in 3D, countdown |
¹ The globe's imagery tiles come from public map servers, so like any online map they see which area you are looking at.
CelesTrak (OMM XML) ─┐
wheretheiss.at (TLE) ─┼─► validate ─► cache (.storage) ─► SGP4 ─► passes for your location
N2YO (TLE, optional) ─┘ │
└─► live position every 10–60 s
- Orbit data. Every 6 hours the integration downloads the ISS's latest mean orbital elements, from CelesTrak first and then the backup sources. Each download is checked (right object, plausible orbit, valid checksums), and newer elements are never replaced by older ones from a lagging mirror.
- Propagation. SGP4 moves the station along its orbit. Your location (latitude, longitude, elevation on the WGS-84 ellipsoid) gives azimuth and elevation for every instant.
- Visibility. A pass counts as visible while the station is above your horizon mask, in sunlight (not in Earth's shadow), and your Sun is below the darkness threshold. The station shines only by reflected sunlight, so the best views come in the hour or two after sunset or before sunrise.
- Brightness. Visual magnitude is estimated from range and Sun angle (lower numbers are brighter; -3 to -4 is a brilliant pass).
- Rolling forecast. Passes are recomputed every 30 minutes and whenever new orbit data arrives, so far-off predictions sharpen as they approach.
The engine was cross-checked against Skyfield, using
the JPL DE421 ephemeris, at six locations across 10 days (see
scripts/validate_against_skyfield.py):
| Quantity | Largest difference from Skyfield |
|---|---|
| Where the station is in your sky | 0.004° |
| Sun elevation (darkness test) | 0.006° |
| Rise, peak and set times | 0.4 s |
| Start and end of the visible window | ~1 s (the reference's sampling step) |
| Passes found or missed | identical |
The limit on real-world accuracy is the orbit data, not the maths. In testing, two
element sets published a day apart agreed to within about 2 seconds for every visible pass
over the following 10 days. The larger risk is a reboost or debris-avoidance
manoeuvre: the station's orbit changes, and later passes can shift by tens of seconds per
day until new orbit data is published, usually within hours. The integration picks that up
on its next download. Each pass carries a confidence attribute based on how far it lies
from the orbit data's epoch:
high: within 3 days.medium: 3–7 days.low: beyond a week. Use these for planning and check again closer to the date; the forecast window goes up to 30 days.
These confidence bands are a practical rule of thumb, not a formal error bound.
Brightness estimates are good to about half a magnitude; the real brightness depends on how the station's solar arrays are angled. The Earth's shadow edge is treated as sharp, whereas the real station fades over a few seconds.
This repository is not in the HACS default store, so add it to HACS as a custom repository. The quickest way is this button, which opens it in HACS on your Home Assistant:
Or add it by hand:
- In Home Assistant, open HACS.
- Open the menu (⋮, top right) and choose Custom repositories.
- Enter
https://github.com/kedube/ha-iss-tracker, choose the type Integration, and select Add. - Search HACS for ISS Tracker, open it and select Download.
- Restart Home Assistant.
- Add the integration.
Installed it before 1.02, when it was called ISS Pass Prediction? The rename gave
it a new domain (iss_tracker), so it can't take over the old entries. Delete the
ISS Pass Prediction entries under Settings → Devices & services, delete the
custom_components/iss_pass_prediction folder if it is still there, and restart; then
install ISS Tracker and add your locations again. Give each location the same name as
before (the old default was ISS, now it is Home) and its entity IDs stay the
same, so automations keep working. The exceptions are the four entities that describe
the station rather than your location, whose names now start with ISS: ISS elevation,
ISS position, ISS above horizon and ISS visible now. Check automations that
use them, or change their entity IDs back under Settings → Entities. Re-import the
blueprint, and pick the location in any globe card that names one.
HACS offers each new release as an update and shows its release notes. Install the update, restart Home Assistant, and then reload any dashboards you have open so they load the new version of the globe.
- Download the source code of the latest release.
- Copy its
custom_components/iss_trackerfolder into thecustom_componentsfolder of your Home Assistant configuration directory, creating that folder if needed. - Restart Home Assistant.
Manual installs don't get update notifications. Repeat these steps to update.
Settings → Devices & services → Add integration → ISS Tracker. The location defaults to your Home Assistant home; you can add more locations (a cabin, a dark-sky site) with Add location on the integration's page. To correct a location's coordinates or elevation later, choose Reconfigure on its entry: its entities, automations and dashboard cards keep working.
| Option | Default | Meaning |
|---|---|---|
| Horizon mask | 10° | The station only counts as visible above this elevation. NASA's Spot the Station also uses 10°. |
| Minimum peak elevation | 20° | Skip low passes that never climb higher than this. |
| Darkness threshold | -6° | The Sun must be at least this far below the horizon (end of civil twilight). Try -3° to include brighter twilight passes. |
| Minimum visible time | 60 s | Skip fleeting passes. |
| Forecast window | 10 days | 1–30 days. See Accuracy. |
| Advance notice | 15 min | When Visible pass soon turns on. |
| Show ISS Tracker in the sidebar | on | Adds the ISS Tracker page to the sidebar. Turn off to remove it; the globe card stays available. The page stays open while a location reloads after an options change. If another dashboard already uses the address /iss-tracker, the page is not added (a warning is logged). |
| Show visible passes in the calendar | on | Adds the location's ISS visible passes calendar (see Calendar). Turn off to remove it. |
| Units | Automatic | Distances and speeds on the globe (altitude, speed, distance from you): Automatic follows Home Assistant's unit system, or choose Metric (km, km/h) or Imperial (mi, mph). Entity attributes stay metric (range_km, altitude_km, speed_km_s). |
| N2YO API key | none | Optional: adds N2YO as a backup orbit source, for every location (they share the orbit data), and checks this integration's predictions against N2YO's once a day. |
Shown here for a location named Home, with Home Assistant set to English. The integration is also translated into German, Dutch, French, Spanish, Italian, Polish, Brazilian Portuguese and Swedish; in those languages new entities get their names, and entity IDs, in that language.
| Entity | What it tells you |
|---|---|
sensor.home_next_visible_pass |
Start time of the next visible pass (or the one under way). Attributes give start/peak/end time, direction and elevation, duration_seconds, magnitude, disappears_into_shadow, confidence, and upcoming, a list of the next qualifying passes in the window (up to 20). |
sensor.home_next_visible_pass_peak_elevation |
How high it climbs (90° is straight overhead). |
sensor.home_next_visible_pass_brightness |
Estimated magnitude (lower = brighter). |
sensor.home_next_visible_pass_duration |
How long it is visible. |
sensor.home_next_pass |
Next pass above the horizon mask, visible or not (useful for radio). |
sensor.home_iss_elevation |
The station's elevation in your sky right now, with azimuth, direction, range and whether it is sunlit. |
binary_sensor.home_visible_pass_soon |
On from the advance-notice time until the visible pass ends. With an advance notice longer than the gap between two passes (about 90 minutes), it stays on from one pass to the next; the blueprint still announces each one. |
binary_sensor.home_iss_visible_now |
On while a visible pass is under way. |
binary_sensor.home_iss_above_horizon |
On while the station is above your horizon mask, lit or not. |
calendar.home_iss_visible_passes |
Every visible pass in the forecast window as an event, with where to look; on while one is under way. See Calendar. |
device_tracker.home_iss_position |
Ground point beneath the station, for map cards. |
sensor.home_orbital_elements_epoch |
Diagnostic: age and source of the orbit data, with age_days, stale and last_download_error. When predictions pause, it stays available to say why until Home Assistant restarts or the location reloads (an options change reloads it); then the repair issue and the location's entry under Devices & services say why. |
sensor.home_n2yo_prediction_difference |
Diagnostic (only with an N2YO key): largest angle between N2YO's prediction and this one. Near the peak, 1° is roughly 1 s of timing. |
The live position updates every 60 seconds, and every 10 seconds while the station is
above your horizon. Its fast-changing attributes (direction, range, altitude, speed) are
not recorded. To keep your database smaller still, you can exclude
device_tracker.home_iss_position and sensor.home_iss_elevation from the
recorder.
If no fresh orbit data can be downloaded for 14 days (a long internet outage, say), predictions would be misleading, so they pause: the entities go unavailable, a repair issue under Settings → Repairs explains why, and the globe says so instead of counting down. Everything resumes by itself once a download succeeds.
The integration adds an ISS Tracker page to the sidebar (switch it off in the options) and an ISS globe card for dashboards. Both show the same live 3D Earth:
- The real sky. NASA's Blue Marble from space, sharpening to Sentinel-2 satellite imagery (or Esri's, down to individual houses) as you zoom in. The night side is drawn from the real Sun position, with NASA's Black Marble city lights, a warm band of twilight along the terminator and an atmosphere that glows on the sunlit limb.
- The station at its true altitude, about 420 km up, lit when the Sun reaches it and dark in Earth's shadow, with its orbit as a glowing ribbon in space: blue in sunlight, violet in shadow, with clock times every 15 minutes and a fading trail behind.
- Your visibility cone: the cone of sky above your home, reaching up to the station's altitude, inside which it is at least 10° up. The intercept, the next pass through it, is drawn in gold when it is a pass you can see, and in silver when it is not, labelled with why: too low or too brief for your options, daylight, a sky too bright for your darkness threshold, or Earth's shadow. The next pass you can see is always drawn in gold, with its time. A dashed heading shows the orbit it comes in on.
- Countdown to the next visible pass, the next time it crosses your sky, and live directions ("Look SW, 34° up") while it is overhead.
- Where to look: a sky chart of the pass from your home, with a live dot during it.
- Preview the pass: fast-forwards the Earth, the Sun and the station to your next visible pass (or any pass in the list, however far ahead in the forecast window), then plays the pass slowly from above your home.
- Camera modes: your location, follow the ISS, or fly alongside it. Zoom out for the globe and in for a flat map; drag to rotate, right-drag or two fingers to tilt.
The page and the card follow your Home Assistant language: English, German, Dutch, French, Spanish, Italian, Polish, Brazilian Portuguese or Swedish (other languages show English).
| Night side, your visibility cone and the intercept | Previewing a pass from above home |
|---|---|
| Following the station across Asia | Zoomed in on home (Esri imagery) |
The card is listed in the dashboard editor as ISS globe and has a visual editor. In YAML:
type: custom:iss-globe-card
view: follow # home (default), follow or chase
imagery: satellite # satellite (default), esri or streets
# entry_id: ... # which location, if you have several
# labels: true # place names and borders
# show_track: true # orbit and clock times
# show_night: true # day and night
# show_footprint: true # area that can see the ISS
# show_home_zone: true # visibility cone
# show_countdown: true
# show_stats: true
# cooperative_gestures: false # require two fingers / Ctrl+scroll to move the map
# height: 420 # masonry dashboards; sections grids and panel views fill their spaceThe globe needs a browser with WebGL 2 (any current browser, including the Companion app). It redraws only a few times a second while idle, stops when it is off screen, and frees its graphics memory shortly after you leave the page.
Import the blueprint:
It sends a Companion-app notification when a pass is about to start, and optionally a daily heads-up:
ISS visible in 15 min Look up at 7:15 PM. Appears in the WNW (10° up), climbs to 67° in the SSW, fades into Earth's shadow in the ESE. Visible for 6.2 min, brightness -3.4.
Or build your own; the timestamp sensor works directly with a time trigger:
triggers:
- trigger: time
at:
entity_id: sensor.home_next_visible_pass
offset: "-00:10:00"The calendar works with a calendar trigger, which fires for every pass, even with an offset longer than the gap between two passes.
Each location has an ISS visible passes calendar, listed in Home Assistant's
Calendar with your other calendars (switch it off in the options). It holds the same
passes as sensor.home_next_visible_pass: every visible pass in the forecast window that
your options let through. Each event runs from when the station appears to when it
disappears, and says where to look:
ISS visible pass · up to 67° Appears in the WNW 10° up, climbs to 67° in the SSW, fades into Earth's shadow in the ESE. Brightness -3.4 · high confidence
The events are refined as new orbit data arrives, and a pass drops off the calendar soon after it is over. While predictions are paused, the calendar is unavailable and empty. Like the globe, the events follow Home Assistant's language (other languages show English); after changing the language, reload the location or restart Home Assistant.
An automation that sends a notification ten minutes before each pass:
triggers:
- trigger: calendar
event: start
offset: "-0:10:0"
entity_id: calendar.home_iss_visible_passes
actions:
- action: notify.mobile_app_your_phone # your phone's notify action
data:
title: "{{ trigger.calendar_event.summary }}"
message: >-
Look up at {{ as_timestamp(trigger.calendar_event.start) | timestamp_custom('%-I:%M %p') }}.
{{ trigger.calendar_event.description }}Besides the globe card, the sensors work with Home Assistant's own cards:
type: markdown
content: >
{% set p = 'sensor.home_next_visible_pass' %}
{% if states(p) not in ['unknown', 'unavailable'] %}
| When | Look | Peak | Brightness | Confidence |
|---|---|---|---|---|
{% for q in state_attr(p, 'upcoming') %}| {{ as_timestamp(q.start) | timestamp_custom('%a %-d %b, %-I:%M %p') }} | {{ q.start_direction }} → {{ q.end_direction }} | {{ q.peak_elevation | round(0) }}° {{ q.peak_direction }} | {{ q.magnitude }} | {{ q.confidence }} |
{% endfor %}
{% else %}No visible ISS passes in the forecast window.{% endif %}type: map
entities:
- device_tracker.home_iss_position
hours_to_show: 2- CelesTrak GP data in CCSDS OMM XML, the format CelesTrak recommends now that the legacy TLE format has run out of catalogue numbers. CelesTrak asks clients to download at most once every two hours. This integration downloads every six hours and shares one download across all configured locations. After a source refuses a request (HTTP 403, 404 or 429) or sends unusable data, it is left alone for two hours. A source that cannot be reached (timeout, connection error or HTTP 5xx) is tried again no sooner than five minutes later: in practice at the next half-hourly refresh, or while Home Assistant retries setup. A mirror whose data has stopped updating does not stop the search: the next source is asked.
- wheretheiss.at: backup TLE source.
- N2YO: optional, with your own API key.
The 3D globe loads its imagery directly in your browser, apart from the city lights, which come with the integration:
| Layer | Source | Terms |
|---|---|---|
| Earth from space | NASA GIBS Blue Marble | NASA imagery, free to use with credit |
| City lights (bundled) | NASA Black Marble 2016 via GIBS | Public domain (NASA); credited on the map |
| Close-up (default) | Sentinel-2 cloudless 2025 by EOX IT Services | CC BY-NC-SA 4.0: personal, non-commercial use |
| Close-up (Esri option) | Esri World Imagery | Esri's terms of use apply; choose it only if they suit you |
| Place names, borders, street map | OpenStreetMap, through Home Assistant's own map tile service | © OpenStreetMap contributors |
Credits for whichever layers are on screen are shown in the corner of the map.
python3.14 -m venv .venv && . .venv/bin/activate
pip install -r requirements_test.txt
pytest
node --test "tests/js/*.test.mjs" # the globe: geometry, sky chart, WebGL layers, view, panel, translationsThe unit tests compare the engine with stored Skyfield results, so they need neither Skyfield nor an ephemeris. To re-run the full cross-check against live orbit data:
pip install skyfield
python scripts/validate_against_skyfield.py --days 10 # downloads de421.bsp (~17 MB) on first runWhat Home Assistant shows for the integration (setup, options, entity names, the repair
notice, the calendar's events) is in custom_components/iss_tracker/translations/, one
JSON file per language, with strings.json as the English source. The calendar's
wording, under common, repeats the globe's. The sidebar page and the card have their
own text in custom_components/iss_tracker/frontend/translations/: English in en.js,
every other language in a JSON file with the same keys.
To add a language, copy a JSON file in both folders to the new language's code,
translate the values and keep each {placeholder} as it is. Then add the code to
LANGUAGES in frontend/iss-i18n.js. The JS tests check that every language has every
key, the same placeholders and the plural forms it needs; the Python tests check the
calendar's words the same way.
Releases are numbered MAJOR.MM with no v (1.00, 1.01, … 1.99, then 2.00), and the
number comes from version in manifest.json. When every check passes on main, CI
publishes that version as a release, unless it has been released already; until the
number goes up, pushes release nothing.
python scripts/version.py bump # manifest.json to the next version, e.g. 1.00 -> 1.01
python scripts/version.py status # whether pushing to main will release itOrbit propagation uses sgp4 by Brandon Rhodes, the
official Vallado SGP4 implementation. The globe is drawn with
MapLibre GL JS 6.11.2 (BSD-3-Clause, bundled under
custom_components/iss_tracker/frontend/vendor/). The city lights are NASA Black
Marble via GIBS (public domain), bundled under
custom_components/iss_tracker/frontend/img/. The icon and logo
(custom_components/iss_tracker/brand/, drawn by scripts/make_brand.py) use
Natural Earth coastlines (public domain) and the
Space Grotesk typeface (SIL Open Font
License 1.1). Licensed under the GNU GPL v3; see LICENSE.