Carte ISS avec heures passages

Salut à tous!

Je voulais vous présenter ma carte ISS réalisée ce weekend. J’avais envie pour une fois de sortir de Zigbee, Thread, Matter… Et faire un truc fun et sympa de vieux geek.
J’ai donc enfilé ma combinaison d’astronaute avec l’idée de faire une carte ISS qui m’indiquerait sa position en temps réel et les prochains passage au dessus de chez moi, dans le Rhône. J’ai bien sûr ajouté une automatisation qui envoie une annonce sur Alexa (uniquement de jour) et une notif sur mon tel.

Le résultat est ici :

Pour la base, installation de l’intégration ISS, c’est facile :

Bien penser à activer l’affichage sur une carte en cliquant sur la roue crantée :

Pour les passages et la position en temps réel, c’est un peu plus touchy. Je suis passé par l’API de N2YO.com (création de compte et génération d’une clé API).

Une fois la clé créée, modification de la config YAML (via File Editor) avec création de rest et de plusieurs sensors :

Pour finir, création des cartes.

1/ La map

type: map
entities:
  - entity: sensor.iss_position
hours_to_show: 1
default_zoom: 2
theme_mode: auto

2/ La distance réelle de mon domicile

graph: line
type: sensor
entity: sensor.distance_iss
detail: 1
grid_options:
  columns: full
  rows: auto

3/ Les passages globaux

type: markdown
title: 📡 Passages globaux ISS Tassin
content: |
  ## Prochain
  **Début :** {{ states('sensor.iss_prochain_passage_global_lisible') }}

4/ Les passages visibles de nuit

type: markdown
title: 👁️ Passages visibles ISS (nuit)
content: >
  ## Prochain

  **Début :** {{ states('sensor.iss_prochain_passage_visible_lisible') }}

  **Durée :** {{ (state_attr('sensor.iss_prochain_passage_visible', 'duration')
  | int(0) / 60) | round(1) }} min

  **Élévation max :** {{ state_attr('sensor.iss_prochain_passage_visible',
  'maxEl') }}°

  **Trajet :** {{ state_attr('sensor.iss_prochain_passage_visible',
  'startAzCompass') }} → {{ state_attr('sensor.iss_prochain_passage_visible',
  'maxAzCompass') }} → {{ state_attr('sensor.iss_prochain_passage_visible',
  'endAzCompass') }}

Je ne vous mets pas les automatisations pour les notifs, elles sont très classiques.

Voilà… Bon, c’est pas Houston, mais je trouve ça tellement cool !

J’espère que vous aurez aimé ce post et que ça pourra vous donner des idées ou vous aider.

Belle soirée à tous !

3 « J'aime »

Hello,

Intéressant, faudra que j’y jette un oeil pour compléter le mien :slight_smile:

cdt

3 « J'aime »

Ah, elle est très belle, cette carte! Et bien plus fouillée que la mienne, ça claque! Bravo :heart_eyes:

Hello,

J’y ai passé un peu de temps :sweat_smile: mais ta partie iss semble plus complète que la mienne. Mes codes de carte sont dispos si besoin.

Par contre ça serait pas mal d’avoir 2 blocs codes plutôt que ces photos pour cette partie :slight_smile:

cdt

Et voilà :slightly_smiling_face:


rest:
  - resource: "https://api.n2yo.com/rest/v1/satellite/visualpasses/25544/45.7578/4.8320/0/5/60/&apiKey=xxxxxxxxx"
    scan_interval: 7200
    sensor:
      - name: "ISS prochain passage visible"
        unique_id: iss_prochain_passage_visible
        value_template: "{{ value_json.passes[0].startUTC }}"
        json_attributes_path: "$.passes[0]"
        json_attributes:
          - startUTC
          - maxUTC
          - endUTC
          - duration
          - maxEl
          - mag
          - startAzCompass
          - maxAzCompass
          - endAzCompass

  - resource: "https://api.n2yo.com/rest/v1/satellite/radiopasses/25544/45.7578/4.8320/0/5/10/&apiKey=xxxxxxxx"
    scan_interval: 7200
    sensor:
      - name: "ISS prochain passage global"
        unique_id: iss_prochain_passage_global
        value_template: "{{ value_json.passes[0].startUTC }}"
        json_attributes_path: "$.passes[0]"
        json_attributes:
          - startUTC
          - maxUTC
          - endUTC
          - duration
          - maxEl
          - startAzCompass
          - maxAzCompass
          - endAzCompass

Du coup, j’ai ajouté la carte des aurores sur la mienne, comme tu as fait. Je trouve ça très stylé.

1 « J'aime »

J’en oublie la moitié… :grinning_face_with_smiling_eyes:

      - name: "ISS position"
        unique_id: iss_position
        icon: mdi:space-station
        state: "{{ states('sensor.iss') }}"
        attributes:
          latitude: "{{ state_attr('sensor.iss', 'latitude') }}"
          longitude: "{{ state_attr('sensor.iss', 'longitude') }}"

      - name: "Distance ISS"
        unique_id: distance_iss
        icon: mdi:map-marker-distance
        unit_of_measurement: "km"
        state: >
          {% set lat_iss = state_attr('sensor.iss', 'latitude') | float(none) %}
          {% set lon_iss = state_attr('sensor.iss', 'longitude') | float(none) %}
          {% set lat_home = state_attr('zone.home', 'latitude') | float(none) %}
          {% set lon_home = state_attr('zone.home', 'longitude') | float(none) %}
          {% if lat_iss is not none and lon_iss is not none
                and lat_home is not none and lon_home is not none %}
            {{ distance(lat_iss, lon_iss, lat_home, lon_home) | round(0) }}
          {% else %}
            {{ none }}
          {% endif %}

      - name: "ISS prochain passage visible lisible"
        unique_id: iss_prochain_passage_visible_lisible
        icon: mdi:clock-outline
        state: >
          {% set ts = states('sensor.iss_prochain_passage_visible') | int(0) %}
          {% if ts > 0 %}
            {{ ts | timestamp_custom('%d/%m/%Y à %H:%M', true) }}
          {% else %}
            Indisponible
          {% endif %}

      - name: "ISS prochain passage global lisible"
        unique_id: iss_prochain_passage_global_lisible
        icon: mdi:clock-outline
        state: >
          {% set ts = states('sensor.iss_prochain_passage_global') | int(0) %}
          {% if ts > 0 %}
            {{ ts | timestamp_custom('%d/%m/%Y à %H:%M', true) }}
          {% else %}
            Indisponible
          {% endif %}
1 « J'aime »

La mienne corrigée avec inspiration de ta carte :

1 « J'aime »

Re,

Merci pour les codes, on est généralement loin pour les aurores boréales :slight_smile: mais bon ça colle au thème :slight_smile:

cdt

1 « J'aime »

Re,

ça décolle bientôt, la carte passe dans cet affichage 30 min avant un lancement

Peek 29-04-2026 16-04

cdt

1 « J'aime »

Re,

Tu n’as pas de soucis d’emplacement ISS fuyant avec ton sensor si il ne remonte pas les infos de lat long quelques fois ? ça arrive

mon ancienne version en bas je trichais avec lat et long mais pas de sensor, j’avais introduit une mémoire ( qd il passe en orange c’est qu’il renvoie 0 0 et du coup le positionnement part en live )

Je n’avais pas creusé de mon côté, mais la carte du haut se base sur ton approche.

Du coup j’ai modifié

      - name: "ISS position"
        unique_id: iss_position
        icon: mdi:space-station
        state: "{{ states('sensor.iss') }}"
        attributes:
          latitude: "{{ state_attr('sensor.iss', 'latitude') }}"
          longitude: "{{ state_attr('sensor.iss', 'longitude') }}"

par

- trigger:
  - platform: state
    entity_id: sensor.iss
  sensor:
  - name: "IL"
    unique_id: iss_location_fixed
    icon: mdi:space-station
    state: "Tracking"
    attributes:
      latitude: >
        {% set current_lat = state_attr('sensor.iss', 'latitude') %}
        {% if current_lat is not none and current_lat != 0 %}
          {{ current_lat }}
        {% else %}
          {{ state_attr('sensor.iss_location', 'latitude') | default(0) }}
        {% endif %}
      longitude: >
        {% set current_lon = state_attr('sensor.iss', 'longitude') %}
        {% if current_lon is not none and current_lon != 0 %}
          {{ current_lon }}
        {% else %}
          {{ state_attr('sensor.iss_location', 'longitude') | default(0) }}
        {% endif %}

qui semble être efficace comme on le voit sur le gif sur la carte du haut, du coup je me base aussi là dessus pour ue le tracker n’affiche pas 0 0

Peek 29-04-2026 18-00

cdt

Salut,

Je n’ai pas fait attention je vais vérifier. Merci pour ce retour.

J’ai trouvé une autre approche clés en main avec cette carte iframe que je trouve assez réussie :

type: iframe
url: https://isstracker.pl/en/widget/map?disableInfoBox=1&lang=en
aspect_ratio: 100%
grid_options:
  columns: full
  rows: 6

C’est plus coloré.

J’essaye de prendre le temps de fouiller ta correction.

Merci :slightly_smiling_face:

Salut,

Je veux bien le code pour cette carte, stp. :slightly_smiling_face:

Hello,

Il faudra installer launch library pour avoir les sensors de lancement et custom button card

Code de la carte
type: custom:button-card
entity: sensor.next_launch_prochain_lancement
show_name: false
state:
  - operator: template
    value: |
      [[[ 
        var diff = (new Date(states['sensor.next_launch_date_de_lancement'].state) - new Date()) / 1000 / 60;
        return diff > 0 && diff < 15;
      ]]]
    styles:
      card:
        - background: rgba(231, 76, 60, 0.1)
        - border: 2px solid rgba(231, 76, 60, 1) !important
        - box-shadow: >-
            inset 0 0 4px rgba(231, 76, 60, 1), inset 0 0 10px
            rgba(231, 76, 60, 0.4)
        - transition: all 0.5s ease-in-out
      custom_fields:
        launch_time:
          - animation: blink 1s infinite
          - color: rgb(231, 76, 60)
styles:
  grid:
    - grid-template-areas: "\"mission\" \"launch_time\" \"info\" \"stats\" \"desc\""
    - grid-template-columns: 1fr
    - grid-template-rows: auto auto auto auto 1fr
  card:
    - padding: 14px
    - background: rgba(0, 0, 0, 0.4)
    - border-radius: 12px
    - border: 2px solid rgba(255, 255, 255, 0.5)
    - height: auto
  custom_fields:
    launch_time:
      - justify-self: start
      - padding-bottom: 4px
    stats:
      - padding: 10px 0
      - border-top: 1px solid rgba(255,255,255,0.1)
      - border-bottom: 1px solid rgba(255,255,255,0.1)
      - margin: 8px 0
    desc:
      - font-size: 12px
      - color: "#bdc3c7"
      - text-align: left
      - white-space: normal
      - width: 100%
      - display: "-webkit-box"
      - "-webkit-line-clamp": "10"
      - "-webkit-box-orient": vertical
      - overflow: hidden
custom_fields:
  mission: |
    [[[
      return `<div style="text-align:left">
                <span style="color: #3498db; font-size: 11px; font-weight: bold; text-transform: uppercase;">${states['sensor.next_launch_prochain_lancement'].attributes.provider}</span><br>
                <span style="font-size: 18px; font-weight: bold; line-height: 1.2;">${states['sensor.next_launch_mission_de_lancement'].state}</span>
              </div>`
    ]]]
  launch_time: |
    [[[
      const dateStr = states['sensor.next_launch_date_de_lancement'].state;
      const date = new Date(dateStr);
      const options = { day: '2-digit', month: 'short', hour: '2-digit', minute: '2-digit', hour12: false, timeZone: 'Europe/Paris' };
      const formattedDate = date.toLocaleString('fr-FR', options);
      return `<div style="text-align:left; color: #f1c40f; font-size: 14px; font-weight: bold;">
                <ha-icon icon="mdi:clock-outline" style="width:16px; color: #f1c40f;"></ha-icon> ${formattedDate}
              </div>`
    ]]]
  info: |
    [[[
      var pad = states['sensor.next_launch_prochain_lancement'].attributes.pad;
      var facility = states['sensor.next_launch_prochain_lancement'].attributes.facility;
      return `<div style="text-align:left; color: #95a5a6; font-size: 12px; margin-top: 2px; line-height: 1.2;">
                <ha-icon icon="mdi:map-marker" style="width:14px; color: #95a5a6;"></ha-icon> <b>${pad}</b><br>
                <span style="font-size: 10px; opacity: 0.8; margin-left: 18px;">${facility}</span>
              </div>`
    ]]]
  stats: |
    [[[
      var proba = states['sensor.next_launch_probabilite_de_lancement'].state;
      var orbit = states['sensor.next_launch_mission_de_lancement'].attributes.target_orbit;
      var status = states['sensor.next_launch_statut_de_lancement'].state;
      var rocket = states['sensor.next_launch_mission_de_lancement'].attributes.rocket_name || 'Falcon 9';
      var displayProba = (proba === 'unknown' || proba === 'unavailable') ? 'Inconnu' : proba + '%';
      var color = (status.includes('Go')) ? '#2ecc71' : '#f1c40f';
      
      return `
        <div style="display: flex; flex-direction: column; font-size: 14px; gap: 6px; text-align: left;">
          <div style="display: flex; justify-content: space-between;"><span><b>Météo</b></span><span>${displayProba}</span></div>
          <div style="display: flex; justify-content: space-between;"><span><b>Lanceur</b></span><span>${rocket}</span></div>
          <div style="display: flex; justify-content: space-between;"><span><b>Orbite</b></span><span>${orbit}</span></div>
          <div style="display: flex; justify-content: space-between; color: ${color};"><span><b>Statut</b></span><span><b>${status}</b></span></div>
        </div>`
    ]]]
  desc: |
    [[[
      return states['sensor.next_launch_mission_de_lancement'].attributes.description;
    ]]]

j’utilise en picture element, mais ça doit pas poser de soucis ailleurs

cdt

1 « J'aime »

Re,

Bon voilà j’ai ce que je veux pour les coordonnées + la map.

Les coordonnées de l’iss remontent, affichage de tout en bleu mode standard
Les coordonnées ne remontent pas, pas d’affichage lat 0 long 0 la carte affiche les anciennes coordonnées et l’emplacement de l’iss sur la map ne bouge pas ( jusqu’au retour des nouvelles ) et l’affichage passe en orange clignotant.

Peek 01-05-2026 11-23

comme d’hab, c’est copieux :smiley: je met le code ici si ça intéresse

code des cartes
            - type: vertical-stack
              card_mod:
                style: |
                  ha-card {
                    background: rgba(0,0,0,0) !important;
                    backdrop-filter: blur(6px) !important;
                    -webkit-backdrop-filter: blur(6px) !important;
                    box-shadow: none !important;
                    border-radius: 8px !important;
                    padding: 6px !important;
                  }
              cards:
                - type: custom:button-card
                  entity: sensor.iss_location
                  name: ISS TRACKER
                  show_name: true
                  show_icon: true
                  icon: mdi:space-station
                  extra_styles: |
                    @keyframes blink {
                      0% { opacity: 1; }      /* Totalement visible */
                      50% { opacity: 0.6; }    /* Partiellement visible  */
                      100% { opacity: 1; }    /* Retour au total visible */
                    }
                  styles:
                    card:
                      - background: rgba(0, 0, 0, 0.4)
                      - border: 2px solid rgba(255, 255, 255, 0.5)
                      - border-radius: 10px
                      - box-shadow: none
                      - padding: 0px 12px
                      - height: 40px
                    grid:
                      - grid-template-areas: "\"i n coords\""
                      - grid-template-columns: 35px auto 1fr
                      - grid-template-rows: 1fr
                    icon:
                      - width: 24px
                      - justify-self: start
                      - align-self: center
                      - color: |
                          [[[ 
                            // On regarde l'état de la source réelle (sensor.iss)
                            return (states['sensor.iss'].state === 'unavailable' || states['sensor.iss'].state === 'unknown') ?
                            '#e67e22' : '#3498db' 
                          ]]]
                      - animation: |
                          [[[ 
                            return (states['sensor.iss'].state === 'unavailable') ? 
                            'blink 2s ease-in-out infinite' : 'none' 
                          ]]]
                    name:
                      - justify-self: start
                      - align-self: center
                      - font-weight: bold
                      - font-size: 14px
                      - color: white
                      - margin-right: 15px
                    custom_fields:
                      coords:
                        - justify-self: start
                        - align-self: center
                        - font-size: 14px
                        - color: |
                            [[[ 
                              // On regarde l'état de la source réelle (sensor.iss)
                              return (states['sensor.iss'].state === 'unavailable' || states['sensor.iss'].state === 'unknown') ?
                              '#e67e22' : '#3498db' 
                            ]]]
                        - animation: |
                            [[[ 
                              return (states['sensor.iss'].state === 'unavailable') ? 
                              'blink 2s ease-in-out infinite' : 'none' 
                            ]]]
                  custom_fields:
                    coords: |
                      [[[ 
                        // On récupère les valeurs actuelles
                        let lat = entity.attributes.latitude;
                        let lon = entity.attributes.longitude;

                        // Si les valeurs sont à 0, nulles ou non définies, 
                        // on va chercher les anciennes valeurs stockées dans les sensors individuels
                        if (!lat || lat === 0) {
                          lat = Number(states['sensor.iss_latitude'].state);
                        }
                        if (!lon || lon === 0) {
                          lon = Number(states['sensor.iss_longitude'].state);
                        }

                        // Sécurité : si après ça on a toujours 0 ou rien (cas rare), on affiche un message
                        if (lat === 0 && lon === 0) {
                          return `<span>Signal perdu...</span>`;
                        }

                        return `<span>Lat: ${lat.toFixed(2)} | Lon: ${lon.toFixed(2)}</span>`;
                      ]]]
                - type: map
                  entities:
                    - entity: sensor.iss_location
                  aspect_ratio: "4:3"
                  default_zoom: 1
                  auto_fit: true
                  hours_to_show: 1.4
                  theme_mode: dark
                  card_mod:
                    style:
                      .: |
                        ha-card {
                          border-radius: 15px !important;
                          border: 2px solid rgba(255, 255, 255, 0.5) !important;
                          overflow: hidden;
                        }
                      ha-map$: |
                        @keyframes trace_blink {
                          0%   { stroke-opacity: 0.9; }
                          50%  { stroke-opacity: 0.3; }
                          100% { stroke-opacity: 0.9; }
                        }
                        @keyframes marker_blink {
                          0%   { opacity: 1; }
                          50%  { opacity: 0.4; }
                          100% { opacity: 1; }
                        }
                        /* La ligne de trajectoire */
                        .leaflet-overlay-pane path {
                          stroke: {% if states('sensor.iss') in ['unavailable', 'unknown'] %} #e67e22 {% else %} #3498db {% endif %} !important;
                          stroke-width: 3px !important;
                          animation: {% if states('sensor.iss') in ['unavailable', 'unknown'] %} trace_blink 2s ease-in-out infinite {% else %} none {% endif %} !important;
                        }
                        /* Le cercle (halo) autour de l'icône "I" */
                        .leaflet-marker-icon div {
                          border-color: {% if states('sensor.iss') in ['unavailable', 'unknown'] %} #e67e22 {% else %} #3498db {% endif %} !important;
                          background-color: rgba(0,0,0,0.5) !important;
                        }
                        /* L'icône elle-même et son ombre orange */
                        .leaflet-marker-pane .leaflet-marker-icon {
                          filter: {% if states('sensor.iss') in ['unavailable', 'unknown'] %} drop-shadow(0 0 6px #e67e22) {% else %} drop-shadow(0 0 6px #3498db) {% endif %} !important;
                          animation: {% if states('sensor.iss') in ['unavailable', 'unknown'] %} marker_blink 2s ease-in-out infinite {% else %} none {% endif %} !important;
                        }

et je remet le sensor

- trigger:
  - platform: state
    entity_id: sensor.iss
  sensor:
  - name: "IL"
    unique_id: iss_location_fixed
    icon: mdi:space-station
    state: "Tracking"
    attributes:
      latitude: >
        {% set current_lat = state_attr('sensor.iss', 'latitude') %}
        {% if current_lat is not none and current_lat != 0 %}
          {{ current_lat }}
        {% else %}
          {{ state_attr('sensor.iss_location', 'latitude') | default(0) }}
        {% endif %}
      longitude: >
        {% set current_lon = state_attr('sensor.iss', 'longitude') %}
        {% if current_lon is not none and current_lon != 0 %}
          {{ current_lon }}
        {% else %}
          {{ state_attr('sensor.iss_location', 'longitude') | default(0) }}
        {% endif %}

cdt

1 « J'aime »

Salut !

Je ne connaissait pas du tout Launch Library. Avec une api qu’on peut utiliser librement en plus. Merci de l’info :wink:

De mon coté j’ai abandonné uptonight sur HA, je suis parti sur un tableau de bord externe, plus adapté à ce que j’avais envie de faire, notammement pour avoir une app quand je fais de l’astrophoto. C’est pas encore parfait, mais ça avance

1 « J'aime »

Re,

Du coup on peut avoir un aperçu ? des fois que ça me donne des idées :smiley:

cdt

Re,

En test, j’ai zappé la partie temps réel, à voir

cdt

Oui pas de soucis. Mais du coup plus rien à voir avec HA.

J’ai fait quelques captures sur la page ´visual tour’ dans la description

2 « J'aime »

Hello

je vois que ISS à la cote :grin:

Juste avec les coordonné gps de votre ville en manuel ou en autorisant la geoloc du navigateur , et cle api de la nasa

Page html , intègre dans carte iframe de HA





3 « J'aime »

Re,

Tjs en test, désolé le gif est long à démarrer

Peek 07-05-2026 23-17

Zoom automatique sur la carte quand on passe en « VISIBLE » en vert ( j’ai mis un déclenchement à 800km et moins)

cdt