Horaires SNCF via api.sncf

Bonjour,
Voilà après quelque jours de prompting GPT j’ai réussis à récupérer et afficher les horaires des trains dans HA. Je sais qu’il y a une integration dédié mais j’ai préféré utiliser directement une api.
Voilà ce qu’il vous faut pour le faire:

  1. S’enregistrer pour obtenir un token API sur Demandez votre clé d'accès gratuite - SNCF NUMÉRIQUE

  2. Recupérer le code de la gare ( Nice pour moi ) depuis Fréquentation en gares — SNCF Open Data

  3. Créer un sensor dans rest.yaml :

# SNCF Departures from Nice
  - resource: https://<votre token ici>@api.sncf.com/v1/coverage/sncf/stop_areas/stop_area:SNCF:87756056/departures?data_freshness=realtime
    scan_interval: 60
    sensor:
      - name: SNCF Next Departure Nice
        value_template: >
          {% set departures = value_json['departures'] %}
          {% if departures | length >= 1 %}
            {% set dep1 = departures[0]['stop_date_time']['departure_date_time'] %}
            {{ strptime(dep1, '%Y%m%dT%H%M%S').strftime('%Y-%m-%d %H:%M:%S') }}
          {% else %}
            No upcoming departures
          {% endif %}
        json_attributes:
          - departures
  1. Créer une carte:
    4.1:
type: markdown
title: Trains départs Nice
content: >-

  {% set departures = state_attr('sensor.sncf_next_departure_nice',
  'departures') %}

  {% if departures and departures | length >= 1 %}

  {% for next_train in departures[:10] %}

  {% set dep_time = next_train['stop_date_time']['departure_date_time'] %}

  {% set base_dep_time =
  next_train['stop_date_time']['base_departure_date_time'] %}

  {% set delay_minutes = ((strptime(dep_time, '%Y%m%dT%H%M%S') -
  strptime(base_dep_time, '%Y%m%dT%H%M%S')).seconds / 60) | int %}

  {% set dep_time_formatted = strptime(dep_time,
  '%Y%m%dT%H%M%S').strftime('%H:%M') %}

  {% set base_dep_time_formatted = strptime(base_dep_time,
  '%Y%m%dT%H%M%S').strftime('%H:%M') %}

  {% if delay_minutes > 0 %}

  {% set display_time = "<s>" ~ base_dep_time_formatted ~ "</s> " ~
  dep_time_formatted %}

  {% else %}

  {% set display_time = dep_time_formatted %}

  {% endif %}

  {% if 'canceled' in next_train.get('additional_informations', []) %}

  {% set status = "🚫 Annulé" %}

  {% elif delay_minutes > 0 %}

  {% set status = "🚨 Retard: " ~ delay_minutes ~ " min" %}

  {% else %}

  {% set status = "✅ À l'heure" %}

  {% endif %}

  {% set destination =
  next_train['display_informations']['direction'].split('(')[0].strip() %}

  {% if destination in ['Monaco', 'Menton', 'Ventimiglia'] %}

  **{{ display_time }} {{ destination.upper() }}, {{
  next_train['display_informations']['trip_short_name'] }} - {{ status }}**

  {% else %}

  {{ display_time }} {{ destination }}, {{
  next_train['display_informations']['trip_short_name'] }} - {{ status }}

  {% endif %}

  {% endfor %}

  {% else %}

  🚫 Aucun départ imminent

  {% endif %}

4.2:

type: markdown
title: Trains départs Nice2
content: >-
  {%- set departures = state_attr('sensor.sncf_next_departure_nice',
  'departures') -%}   {%- if departures and departures | length >= 1 %}
    {%- for next_train in departures[:10] %}
      {%- set stop_date_time = next_train.get('stop_date_time', {}) -%}
      {%- set dep_time_str = stop_date_time.get('departure_date_time') -%}
      {%- set base_dep_time_str = stop_date_time.get('base_departure_date_time') -%}
      {%- if dep_time_str and base_dep_time_str %}
        {%- set dep_time = strptime(dep_time_str, '%Y%m%dT%H%M%S') -%}
        {%- set base_dep_time = strptime(base_dep_time_str, '%Y%m%dT%H%M%S') -%}
        {%- if dep_time and base_dep_time %}
          {%- set delay_minutes = ((dep_time - base_dep_time).total_seconds() / 60) | int -%}
          {%- set dep_time_formatted = dep_time.strftime('%H:%M') -%}
          {%- set base_dep_time_formatted = base_dep_time.strftime('%H:%M') -%}
          {%- if delay_minutes > 0 %}
            {%- set display_time = "<s>" ~ base_dep_time_formatted ~ "</s> " ~ dep_time_formatted -%}
            {%- set status = "🚨 Retard: " ~ delay_minutes ~ " min" -%}
          {%- else %}
            {%- set display_time = dep_time_formatted -%}
            {%- set status = "" -%}
          {%- endif %}
          {%- set display_informations = next_train.get('display_informations', {}) -%}
          {%- set direction = display_informations.get('direction', '') -%}
          {%- set destination = direction.split('(')[0].strip() -%}
          {%- set trip_short_name = display_informations.get('trip_short_name', '') -%}
          {%- set commercial_mode = display_informations.get('commercial_mode', '') -%}
          {%- set train_type_and_number = commercial_mode ~ '-' ~ trip_short_name -%}
          {%- if destination in ['Monaco', 'Menton', 'Ventimiglia'] %}
            {%- set output_line = "**" ~ display_time ~ "** **" ~ destination | upper ~ "**, " ~ train_type_and_number -%}
          {%- else %}
            {%- set output_line = "**" ~ display_time ~ "** " ~ destination ~ ", " ~ train_type_and_number -%}
          {%- endif %}
          {%- if status %}
            {%- set output_line = output_line ~ " - " ~ status -%}
          {%- endif %}
          {{ output_line }}
        {%- endif %}
      {%- endif %}
    {%- endfor %}
  {%- else %}
    '🚫 Aucun départ imminent'
  {%- endif %}

  1. Admirer votre carte et suivre les départs :grin: (j’ai mis en gras/majuscule les destinations qui m’itéresse le plus )
    5.1:

    5.2:
7 « J'aime »

Hello , dans le fichier Rest.yaml , si je colle le code tel que j ai une erreur ,
Je l ai bien déclaré dans config.yaml , ,
Tu peux m orienté stp ?

quelle est l’erreur?
Si tu declares le sensor dans configuration.yaml, pas besoin de le mettre dans rest.yaml.
Si tu veux rest.yaml, il faut dans configuration.yaml indiquer que pour les sensors rest il y a un autre fichier. Tu mets donc:
rest: !include rest.yaml

[quote=« akunia, post:3, topic:48506 »]

Magique :slight_smile: simplement un problème entre le clavier et la chaise ^^
Tuto parfaitement fonctionnel !
Merci a toi

T’es sur que c’est bon? Ca m’étonnerai que tu ai des départs de Lille à Sospel par ex :laughing:
Blague à part, je suis content que ça servira à quelqu’un.

Merci ^^ en effet tu as tout a fait raison ^^


c est une peu plus de mon coin ^^

Salut,
J’ia modifié un peu la carte ( point 4.2 et 5.2 dans le premier post) en enlevant l’information sur les trains annulés ( je ne voie pas la data dans le sensor qui permettrai de le determiner), en enlevant l’information sur les trains qui sont à l’heure, en ajoutant le type de train ( p.ex. TER) et rendant la carte plus compacte. Voici ce que ça donne:

Ça marche aussi chez moi. Merci @akunia pour ce partage.

Avoir les horaires des trains dans HA, ça ouvre plein de possibilités. Par exemple :

  • Avoir des notifications si un train est en retard.
  • Décaler l’heure du réveil si le train est en retard ou annulé (bon encore faudrait-il avoir un réveil pilotable :laughing:)

Je t’en prie @XavB :grin:

Décaler l’heure du réveil si le train est en retard ou annulé (bon encore faudrait-il avoir un réveil pilotable :laughing:)

Comme j’ai mentionné je n’ai pas trouvé comment tracker les trains annulés, donc pas possible ( a moins que quelq’un a une idée, car rien dans le réponse à la requête ne permet de le trouver).
Pour le réveil en fonction de retards de train, tu peux sans doute pouvoir le configurer avec l’app sur le téléphone si tu n’as pas de source audio pilotable.

Perso, je prends toujours le même train le matin, donc une bonne stratégie serait de détecter son absence du tableau d’affichage.

Je vais essayer de jouer avec https://api.sncf.com/v1/coverage/sncf/disruptions endpoint pour voir si l’info remonte, mais cela impliquera un autre sensor donc un rafraichissement moins fréquent car sncf limite le compte gratuit à 5000 requêtes/jour. Si on a plusieurs stations faudra faire attention

Bonjour, désolé mais je me bats avec l’indentation du sensor dans configuration.yaml…
Il ne veut pas me valider le fichier ou me dit :

Avertissements de configuration

Integration error: scan_interval - Integration 'scan_interval' not found.
Integration error: json_attributes - Integration 'json_attributes' not found. Integration error: value_template - Integration 'value_template' not found. Integration error: resource - Integration 'resource' not found.
Integration error: name - Integration 'name' not found.

Je bloque…
Merci.

dans mon example j’utilise rest.yaml.
Si tu veux directement dans configuration yaml, faut declarer le type de sensor donc:

# SNCF Departures from xxx
rest:
  - resource: https://<votre token ici>@api.sncf.com/v1/coverage/sncf/stop_areas/stop_area:SNCF:87756056/departures?data_freshness=realtime
    scan_interval: 60
.........et la suite...........

Oui c est bon merci.

@akunia Bonjour! Je monitor le meme trajet. Il semblerait que depuis quelques semaines (au environ des changements horaire) l’API ne retourne plus les TERs. Aussi le endpoint Monaco ne retourne plus rien … Meme constatation de votre cote?

Nice:
api.sncf.com/v1/coverage/sncf/stop_areas/stop_area:SNCF:87756056/departures?data_freshness=realtime

Monaco:
api.sncf.com/v1/coverage/sncf/stop_areas/stop_area:SNCF:87756403/departures?data_freshness=realtime

Mmhhh, edit… En rentrant je constate que les TER sont de retour… Incroyable.

Je confirme,
C’était en cacahuètes depuis plusieurs jours et aujourd’hui ça remarche! SNCF vient de basculer en 2025? :rofl:

Hello !
Merci @akunia pour ce topic qui m’a permis de faire la meme chose pour mon dashboard mais au depart d’Orchies, et comme les trains sont souvent en retard j’ai pu amener quelques améliorations que je vous partage.

  1. Affichage par heure de départ initiale, j’ai ajouté le sort sur la boucle for
{% for next_train in departures[:10] | sort(attribute='stop_date_time.base_departure_date_time') %} 
  1. Visuel utilisant les ha-alert pour avoir un peu de couleurs
<ha-alert alert-type="success">...</ha-alert>
<ha-alert alert-type="warning">...</ha-alert>
<ha-alert alert-type="Error">...</ha-alert>
  1. Un ascenceur pour limiter la taille de la carte mais toujours avoir les 10 prochaines trains à disposition. Il faut utiliser card-mod depuis le HACS et rajouter dans l’editeur yaml
card_mod:
  style: |
    ha-markdown {
      max-height: 250px;
      overflow-y: auto;
    }

Voilà le code global, j’ai changer un peu l’affichage à ma convenance comme d’autres ici donc à adapter à vos besoins.

type: markdown
title: 🚂 Trains au départs d'Orchies
content: >-
  {% set departures = state_attr('sensor.sncf_next_departure_orchies',
  'departures') %} 

  {% if departures and departures | length >= 1 %} 

  {% for next_train in departures[:10] |
  sort(attribute='stop_date_time.base_departure_date_time') %} 


  {% set dep_time = next_train['stop_date_time']['departure_date_time'] %} 

  {% set base_dep_time =
  next_train['stop_date_time']['base_departure_date_time'] %} 

  {% set delay_minutes = ((strptime(dep_time, '%Y%m%dT%H%M%S') -
  strptime(base_dep_time, '%Y%m%dT%H%M%S')).seconds / 60) | int %} 

  {% set dep_time_formatted = strptime(dep_time,
  '%Y%m%dT%H%M%S').strftime('%H:%M') %}

  {% set base_dep_time_formatted = strptime(base_dep_time,
  '%Y%m%dT%H%M%S').strftime('%H:%M') %} 

  {% set destination =
  next_train['display_informations']['direction'].split('(')[0].strip() %} 

  {% if delay_minutes > 0 %} 

  {% set display_time = "<s>" ~ base_dep_time_formatted ~ "</s> " ~
  dep_time_formatted %} 

  {% else %} 

  {% set display_time = dep_time_formatted %} 

  {% endif %} 

  {% if 'canceled' in next_train.get('additional_informations', []) %} 

  {% set status = "🚫 Annulé" %}

  <ha-alert alert-type="error">{{ display_time }} -  **{{ destination }}** - {{
  status }}  

  {% elif delay_minutes > 0 %} 

  {% set status = "🚨 Retard: " ~ delay_minutes ~ " min" %}

  <ha-alert alert-type="warning">{{ display_time }} -  **{{ destination }}** -
  {{ status }} 

  {% else %}

  <ha-alert alert-type="success">{{ display_time }} -  **{{ destination }}** {%
  endif %}


  {% endfor %} 

  {% else %}

  🚫 Aucun départ imminent

  {% endif %}
card_mod:
  style: |
    ha-markdown {
      max-height: 250px;
      overflow-y: auto;
    }

Et le rendu visuel.

Ce qu’il reste à retravailler c’est la présence des Autocar dans la liste des trains, je n’ai pas encore trouvé la bonne manière de faire pour les retirer, si vous avez l’info, faites tourner :

Salut,
Belle réalisation, bravo et merci pour le partage.

Bonjour,
Désolé de déterrer le sujet mais est-ce qu’il serait possible selon vous de n’ajouter qu’une seule destination ?
Dans votre exemple, de n’afficher que les trains en direction de Valenciennes par exemple
C’est vraiment du plus, l’affichage actuel est déjà très bien pour mon usage
Merci