Intégration Train Traveler (SNCF)

Hello tout le monde,

Je vous présente une intégration sur laquelle je travaille actuellement et qui, je pense, peut intéresser d’autres personnes car lors de mes recherches je n’ai rien trouvé de concluant. Je me suis donc dit que ça serait une bonne idée de la reverser à la communauté :slight_smile:

Cette nouvelle intégration permet, dans les grandes lignes, de récupérer les informations des prochains trains (SNCF) qui sont prévus entre une gare de départ et une gare d’arrivée et de retourner également les incidents en cours s’il y en a un en cours (exemple : les retards).

(Mise à jour du 31/05/2024) : Released depuis la version v0.1.0-alpha.1
Lien de l’intégration (actuellement en version 0.1.0-alpha non released) :

Les fonctionnalités actuelles :

  • Afficher les horaires de passages des prochains trains (selon la configuration)
  • Afficher l’horaire du dernier passage (selon la configuration)
  • Afficher les incidents, leurs messages et éventuels délais si existants

Exemple de carte que l’on peut créer :

L’intégration répond à mon besoin actuel, mais si vous apportez d’autres cas d’usages, je la ferai évoluer en fonction (et j’en profiterai pour optimiser un peu le tout … j’ai découvert le framework de Home Assistant pendant mes quelques heures de libres et c’est pas au top :laughing:)

(Mise à jour du 31/05/2024) : Installation via HACS disponible
L’installation est seulement manuelle pour le moment, je vais essayer de me dégager du temps semaine prochaine pour la rendre disponible via HACS afin de faciliter son installation et sa maintenabilité.

Je rappelle que l’intégration est en cours de développement et que des bugs peuvent survenir. La version n’est pas released, la code base peut donc encore évoluer, mais ça ne viendra pas écraser votre installation sans une mise à jour manuelle de votre part.

Code pour l’exemple de l’image :

type: vertical-stack
cards:
  - type: entity
    entity: sensor.train_traveler_mar_bor_next_journey_1
    name: Next Journey
  - type: entities
    entities:
      - type: attribute
        entity: sensor.train_traveler_mar_bor_next_journey_1
        name: Line
        icon: mdi:train
        attribute: line
      - type: attribute
        entity: sensor.train_traveler_mar_bor_next_journey_1
        name: Direction
        icon: mdi:directions
        attribute: direction
      - entity: sensor.train_traveler_mar_bor_next_journey_departure_1
        name: Departure Time
        icon: mdi:clock-fast
      - entity: sensor.train_traveler_mar_bor_next_journey_arrival_1
        name: Arrival Time
        icon: mdi:ray-end
      - entity: sensor.train_traveler_mar_bor_next_journey_duration_1
        name: Duration
        unit: seconds
        icon: mdi:timeline-clock-outline
      - type: attribute
        entity: sensor.train_traveler_mar_bor_next_journey_1
        name: Type
        icon: mdi:information-box-outline
        attribute: physical_mode
  - type: conditional
    conditions:
      - condition: state
        entity: binary_sensor.train_traveler_mar_bor_next_journey_disruption_1
        state: 'on'
    card:
      type: entities
      entities:
        - entity: sensor.train_traveler_mar_bor_next_journey_disruption_delay_1
          name: Delay
          icon: mdi:clock-start
        - entity: binary_sensor.train_traveler_mar_bor_next_journey_disruption_1
          type: attribute
          name: Message
          icon: mdi:alert-circle
          attribute: disruption_message
  - type: entities
    entities:
      - entity: sensor.train_traveler_mar_bor_next_journey_1
        type: custom:multiple-entity-row
        name: 'Next Train #1'
        state_header: Heure de départ
        format: time
        entities:
          - entity: sensor.train_traveler_mar_bor_next_journey_duration_1
            format: duration
            name: Durée
      - entity: sensor.train_traveler_mar_bor_next_journey_2
        type: custom:multiple-entity-row
        name: 'Next Train #2'
        state_header: Heure de départ
        format: time
        entities:
          - entity: sensor.train_traveler_mar_bor_next_journey_duration_2
            format: duration
            name: Durée
      - entity: sensor.train_traveler_mar_bor_next_journey_3
        type: custom:multiple-entity-row
        name: 'Next Train #3'
        state_header: Heure de départ
        format: time
        entities:
          - entity: sensor.train_traveler_mar_bor_next_journey_duration_3
            format: duration
            name: Durée
  - type: entity
    entity: sensor.train_traveler_mar_bor_last_journey_1
    name: Last Journey
title: Marcheprime - Bordeaux

A vos remarques !

Posts mises à jour :

9 « J'aime »

Bonjour,
Merci beaucoup pour cette intégration !
C’est top !

Est-ce qu’il serait possible pour la carte d’avoir une version multicolonne ?

Exemple:
Première case (comme actuellement): Prochain train
Deuxième case (comme actuellement): Détails du prochain train
Troisième case (comme actuellement): affichage de l’alerte si besoin
Quatrième case (nouveau): Afficher par exemple les 3 prochains horaires (ou en fonction du nb de forecasts indiqués lors de la création de l’appareil) ainsi que leur durée sous forme de « tableau ».
Cinquième case (comme actuellement): Dernier train de la journée

Enfin, une fonctionnalité serait intéressante: si on suit plusieurs trajets, on créé plusieurs appareils.
Exemple: Amiens-Paris / Paris-Amiens / Longueau-Paris / Paris-Longueau

Il serait pratique sur la carte d’avoir une liste (ou combobox) qui affiche ces quatre itinéraires créés et quand on en sélectionne un, la carte se met à jour avec les informations de l’itinéraire sélectionné.

Merci++++

Tout a fait d’accord, pour moi l’api doit recuperer la liste des prochaines trains et en faire une liste, un peut comme celle-ci ( que je fait pour des bus de chez moi ) :

list:
  - retard: 0
    depart: "2024-05-28 14:54:00+02:00"
    depart_reel: "2024-05-28 14:54:00+02:00"
    status: programmer
  - retard: 2134
    depart: "2024-05-28 14:29:00+02:00"
    depart_reel: "2024-05-28 15:04:34+02:00"
    status: SCHEDULED
  - retard: 0
    depart: "2024-05-28 15:54:00+02:00"
    depart_reel: "2024-05-28 15:54:00+02:00"
    status: programmer
  - retard: 0
    depart: "2024-05-28 16:37:00+02:00"
    depart_reel: "2024-05-28 16:37:00+02:00"
    status: programmer
  - retard: 0
    depart: "2024-05-28 16:50:00+02:00"
    depart_reel: "2024-05-28 16:50:00+02:00"
    status: programmer
  - retard: 0
    depart: "2024-05-28 17:05:00+02:00"
    depart_reel: "2024-05-28 17:05:00+02:00"
    status: programmer
  - retard: 5825
    depart: "2024-05-28 15:29:00+02:00"
    depart_reel: "2024-05-28 17:06:05+02:00"
    status: SCHEDULED
  - retard: 0
    depart: "2024-05-28 17:36:00+02:00"
    depart_reel: "2024-05-28 17:36:00+02:00"
    status: programmer
...
...
bus_restant_pour_aujourd_hui: 11

ce qui permet d’etre souple et de faire ce qu’on veut ( custom dashboard, affichage que du suivant, tous les train , … )

Perso, je fait ca avec un tableau :

Screenshot from 2024-05-29 14-27-50

Avec ce code :

  - type: conditional
    conditions:
      - condition: state
        entity: sensor.bus_23
        state_not: unavailable
    card:
      type: custom:flex-table-card
      title: Bus Aller
      strict: true
      sort_by:
        - depart+
      columns:
        - name: Depart
          data: list
          modify: |-
            if (parseInt(x.retard) >= 1 )
              '<div style="color:#E70B0B;">' + x.depart.substr(11,5) + ' -> ' + x.depart_reel.substr(11,5) + '</div>'
            else
              x.depart.substr(11,5)
        - name: Depart2
          data: list
          modify: x.depart
          id: depart
          hidden: true
        - name: DepartReel
          data: list
          modify: x.depart_reel
          id: departreel
          hidden: true
        - name: Retard
          data: list
          modify: |
            if (parseInt(x.retard) >= 3600 )
              '<div style="color:#E70B0B;">' + Math.trunc(x.retard /3600) + 'h' + Math.trunc((x.retard %3600)/60) + 'm</div>'
            else if (parseInt(x.retard) >= 60 )
              '<div style="color:#E70B0B;">' + Math.trunc(x.retard /60) + 'm'+ '</div>'
            else if (parseInt(x.retard) >= 1 )
              '<div style="color:#E70B0B;">' + x.retard + 's</div>'
            else
              '<div style="color:#139523;">' + x.retard + '</div>'
        - name: Statut
          data: list
          modify: |
            if (x.status != "programmer" || x.status != "UPDATED")
              '<div style="color:#139523;">' + x.status + '</div>'
            else
              '<div style="color:#E70B0B;">' + x.status + '</div>'
      entities:
        include: sensor.bus_23
      card_mod:
        style: |
          tbody tr:hover {
            background-color: coral !important;
          }
      css:
        table+: 'border-collapse: collapse; padding: 1px;'
        th+: 'border: 1px solid white; padding: 3px;'
        td+: 'border: 1px solid white; padding: 3px;'

a titre d’exemple, sur la nouvelle integration ecole-directe, c’est le meme type de comportement/liste , …

Hello,

Les entités remontées permettent de faire le même style de tableau, même si ce n’est pas un type « list » que je ne connaissais pas. Cependant, je reconnais que ça serait probablement plus simple à gérer pour un trajet donné (appareil), ça doit pouvoir s’adapter facilement. Je le note pour évolution (@roumano si tu as un lien d’une doc qui explique comment avoir des attributs en liste sur un sensor, je suis preneur)

Pour répondre à la demande de @Matthieu_D1, c’est possible en adaptant un peu la carte.


Pour ça, j’ai créé une entrée input_select en mettant les différents trajets puis j’ai utilisé les modules multiple-entity-row et state-switch que j’ai installés via HACS pour rendre les cartes plus lisibles

Code de la carte pour exemple (on voit qu’effectivement une liste de data serait plus facile à gérer que différentes entités, je vais voir pour faire une adaptation prochainement)

Edit : (code pour v0.1.0-alpha.1)

title: Train Traveler
type: vertical-stack
cards:
  - type: entities
    entities:
      - input_select.voyages
  - type: custom:state-switch
    entity: input_select.voyages
    states:
      Amiens - Paris:
        type: vertical-stack
        title: Amiens - Paris
        cards:
          - type: entity
            entity: sensor.train_traveler_ami_par_next_journey_1
            name: Prochain Train
          - type: entities
            entities:
              - type: attribute
                entity: sensor.train_traveler_ami_par_next_journey_1
                name: Line
                icon: mdi:train
                attribute: line
              - type: attribute
                entity: sensor.train_traveler_ami_par_next_journey_1
                name: Direction
                icon: mdi:directions
                attribute: direction
              - entity: sensor.train_traveler_ami_par_next_journey_departure_1
                name: Departure Time
                icon: mdi:clock-fast
              - entity: sensor.train_traveler_ami_par_next_journey_arrival_1
                name: Arrival Time
                icon: mdi:ray-end
              - entity: sensor.train_traveler_ami_par_next_journey_duration_1
                name: Duration
                unit: seconds
                icon: mdi:timeline-clock-outline
              - type: attribute
                entity: sensor.train_traveler_ami_par_next_journey_1
                name: Type
                icon: mdi:information-box-outline
                attribute: physical_mode
          - type: conditional
            conditions:
              - condition: state
                entity: binary_sensor.train_traveler_ami_par_next_journey_disruption_1
                state: 'on'
            card:
              type: entities
              entities:
                - entity: >-
                    sensor.train_traveler_ami_par_next_journey_disruption_delay_1
                  name: Delay
                  icon: mdi:clock-start
                - entity: >-
                    binary_sensor.train_traveler_ami_par_next_journey_disruption_1
                  type: attribute
                  name: Message
                  icon: mdi:alert-circle
                  attribute: disruption_message
          - type: entities
            entities:
              - entity: sensor.train_traveler_ami_par_next_journey_1
                type: custom:multiple-entity-row
                name: 'Prochain Train #1'
                state_header: Heure de départ
                format: time
                entities:
                  - entity: sensor.train_traveler_ami_par_next_journey_duration_1
                    format: duration
                    name: Durée
              - entity: sensor.train_traveler_ami_par_next_journey_2
                type: custom:multiple-entity-row
                name: 'Prochain Train #2'
                state_header: Heure de départ
                format: time
                entities:
                  - entity: sensor.train_traveler_ami_par_next_journey_duration_2
                    format: duration
                    name: Durée
              - entity: sensor.train_traveler_ami_par_next_journey_3
                type: custom:multiple-entity-row
                name: 'Prochain Train #3'
                state_header: Heure de départ
                format: time
                entities:
                  - entity: sensor.train_traveler_ami_par_next_journey_duration_3
                    format: duration
                    name: Durée
          - type: entity
            entity: sensor.train_traveler_ami_par_last_journey_1
            name: Dernier Train
      Paris - Amiens:
        type: vertical-stack
        title: Paris - Amiens
        cards:
          - type: entity
            entity: sensor.train_traveler_par_ami_next_journey_1
            name: Prochain Train
          - type: entities
            entities:
              - type: attribute
                entity: sensor.train_traveler_par_ami_next_journey_1
                name: Line
                icon: mdi:train
                attribute: line
              - type: attribute
                entity: sensor.train_traveler_par_ami_next_journey_1
                name: Direction
                icon: mdi:directions
                attribute: direction
              - entity: sensor.train_traveler_par_ami_next_journey_departure_1
                name: Departure Time
                icon: mdi:clock-fast
              - entity: sensor.train_traveler_par_ami_next_journey_arrival_1
                name: Arrival Time
                icon: mdi:ray-end
              - entity: sensor.train_traveler_par_ami_next_journey_duration_1
                name: Duration
                unit: seconds
                icon: mdi:timeline-clock-outline
              - type: attribute
                entity: sensor.train_traveler_par_ami_next_journey_1
                name: Type
                icon: mdi:information-box-outline
                attribute: physical_mode
          - type: conditional
            conditions:
              - condition: state
                entity: binary_sensor.train_traveler_par_ami_next_journey_disruption_1
                state: 'on'
            card:
              type: entities
              entities:
                - entity: >-
                    sensor.train_traveler_par_ami_next_journey_disruption_delay_1
                  name: Delay
                  icon: mdi:clock-start
                - entity: >-
                    binary_sensor.train_traveler_par_ami_next_journey_disruption_1
                  type: attribute
                  name: Message
                  icon: mdi:alert-circle
                  attribute: disruption_message
          - type: entities
            entities:
              - entity: sensor.train_traveler_par_ami_next_journey_1
                type: custom:multiple-entity-row
                name: 'Prochain Train #1'
                state_header: Heure de départ
                format: time
                entities:
                  - entity: sensor.train_traveler_par_ami_next_journey_duration_1
                    format: duration
                    name: Durée
              - entity: sensor.train_traveler_par_ami_next_journey_2
                type: custom:multiple-entity-row
                name: 'Prochain Train #2'
                state_header: Heure de départ
                format: time
                entities:
                  - entity: sensor.train_traveler_par_ami_next_journey_duration_2
                    format: duration
                    name: Durée
              - entity: sensor.train_traveler_par_ami_next_journey_3
                type: custom:multiple-entity-row
                name: 'Prochain Train #3'
                state_header: Heure de départ
                format: time
                entities:
                  - entity: sensor.train_traveler_par_ami_next_journey_duration_3
                    format: duration
                    name: Durée
          - type: entity
            entity: sensor.train_traveler_par_ami_last_journey_1
            name: Dernier Train
      Longueau - Paris:
        type: vertical-stack
        title: Longueau - Paris
        cards:
          - type: entity
            entity: sensor.train_traveler_lon_par_next_journey_1
            name: Prochain Train
          - type: entities
            entities:
              - type: attribute
                entity: sensor.train_traveler_lon_par_next_journey_1
                name: Line
                icon: mdi:train
                attribute: line
              - type: attribute
                entity: sensor.train_traveler_lon_par_next_journey_1
                name: Direction
                icon: mdi:directions
                attribute: direction
              - entity: sensor.train_traveler_lon_par_next_journey_1
                name: Departure Time
                icon: mdi:clock-fast
              - entity: sensor.train_traveler_lon_par_next_journey_arrival_1
                name: Arrival Time
                icon: mdi:ray-end
              - entity: sensor.train_traveler_lon_par_next_journey_duration_1
                name: Duration
                unit: seconds
                icon: mdi:timeline-clock-outline
              - type: attribute
                entity: sensor.train_traveler_lon_par_next_journey_1
                name: Type
                icon: mdi:information-box-outline
                attribute: physical_mode
          - type: conditional
            conditions:
              - condition: state
                entity: binary_sensor.train_traveler_lon_par_next_journey_disruption_1
                state: 'on'
            card:
              type: entities
              entities:
                - entity: >-
                    sensor.train_traveler_lon_par_next_journey_disruption_delay_1
                  name: Delay
                  icon: mdi:clock-start
                - entity: >-
                    binary_sensor.train_traveler_lon_par_next_journey_disruption_1
                  type: attribute
                  name: Message
                  icon: mdi:alert-circle
                  attribute: disruption_message
          - type: entities
            entities:
              - entity: sensor.train_traveler_lon_par_next_journey_1
                type: custom:multiple-entity-row
                name: 'Prochain Train #1'
                state_header: Heure de départ
                format: time
                entities:
                  - entity: sensor.train_traveler_lon_par_next_journey_duration_1
                    format: duration
                    name: Durée
              - entity: sensor.train_traveler_lon_par_next_journey_2
                type: custom:multiple-entity-row
                name: 'Prochain Train #2'
                state_header: Heure de départ
                format: time
                entities:
                  - entity: sensor.train_traveler_lon_par_next_journey_duration_2
                    format: duration
                    name: Durée
              - entity: sensor.train_traveler_lon_par_next_journey_3
                type: custom:multiple-entity-row
                name: 'Prochain Train #3'
                state_header: Heure de départ
                format: time
                entities:
                  - entity: sensor.train_traveler_lon_par_next_journey_duration_3
                    format: duration
                    name: Durée
          - type: entity
            entity: sensor.train_traveler_lon_par_last_journey_1
            name: Dernier Train
      Paris - Longueau:
        type: vertical-stack
        title: Longueau - Paris
        cards:
          - type: entity
            entity: sensor.train_traveler_par_lon_next_journey_1
            name: Prochain Train
          - type: entities
            entities:
              - type: attribute
                entity: sensor.train_traveler_par_lon_next_journey_1
                name: Line
                icon: mdi:train
                attribute: line
              - type: attribute
                entity: sensor.train_traveler_par_lon_next_journey_1
                name: Direction
                icon: mdi:directions
                attribute: direction
              - entity: sensor.train_traveler_par_lon_next_journey_departure_1
                name: Departure Time
                icon: mdi:clock-fast
              - entity: sensor.train_traveler_par_lon_next_journey_arrival_1
                name: Arrival Time
                icon: mdi:ray-end
              - entity: sensor.train_traveler_par_lon_next_journey_duration_1
                name: Duration
                unit: seconds
                icon: mdi:timeline-clock-outline
              - type: attribute
                entity: sensor.train_traveler_par_lon_next_journey_1
                name: Type
                icon: mdi:information-box-outline
                attribute: physical_mode
          - type: conditional
            conditions:
              - condition: state
                entity: binary_sensor.train_traveler_par_lon_next_journey_disruption_1
                state: 'on'
            card:
              type: entities
              entities:
                - entity: >-
                    sensor.train_traveler_par_lon_next_journey_disruption_delay_1
                  name: Delay
                  icon: mdi:clock-start
                - entity: >-
                    binary_sensor.train_traveler_par_lon_next_journey_disruption_1
                  type: attribute
                  name: Message
                  icon: mdi:alert-circle
                  attribute: disruption_message
          - type: entities
            entities:
              - entity: sensor.train_traveler_par_lon_next_journey_1
                type: custom:multiple-entity-row
                name: 'Prochain Train #1'
                state_header: Heure de départ
                format: time
                entities:
                  - entity: sensor.train_traveler_par_lon_next_journey_duration_1
                    format: duration
                    name: Durée
              - entity: sensor.train_traveler_par_lon_next_journey_2
                type: custom:multiple-entity-row
                name: 'Prochain Train #2'
                state_header: Heure de départ
                format: time
                entities:
                  - entity: sensor.train_traveler_par_lon_next_journey_duration_2
                    format: duration
                    name: Durée
              - entity: sensor.train_traveler_par_lon_next_journey_3
                type: custom:multiple-entity-row
                name: 'Prochain Train #3'
                state_header: Heure de départ
                format: time
                entities:
                  - entity: sensor.train_traveler_par_lon_next_journey_duration_3
                    format: duration
                    name: Durée
          - type: entity
            entity: sensor.train_traveler_par_lon_last_journey_1
            name: Dernier Train


Perso, j’ai fait un truc simple via multiscrape, mon sensor est cree comme ca :

  sensor:
    - name: 'Bus 23 '
      unique_id: bus_23
      value_template: >
        {% if value_json[0].times | list | count >= 1 %}
          {{ strptime(((today_at('00:00') + timedelta(seconds= (value_json[0].times[0].scheduledArrival | int) )) | string), '%Y-%m-%d %H:%M:%S%z') }}
        {% else %}
          unavailable
        {% endif %}
      device_class: timestamp
      attributes:
        - name: list
          value_template: >
            [
            {%- for i in range(0, value_json[0].times | list | count, 1) -%}
              {%- if (value_json[0].times[i].realtimeState == 'SCHEDULED') -%}
                {%- set status = 'programmer' -%}
              {%- else -%}
                {%- set status = value_json[0].times[0].realtimeState -%}
              {% endif %}
              {%- set cycle = {'retard': value_json[0].times[i].arrivalDelay, 'depart': (today_at('00:00') + timedelta(seconds= ((value_json[0].times[i].scheduledArrival | int) ))) | string, 'depart_reel': (today_at('00:00') + timedelta(seconds= ((value_json[0].times[i].realtimeArrival | int) ))) | string, 'status': status }-%}
              {{ cycle }},
            {% endfor %}
            ]

tu peut demander a @Giga77 , pour le detail de son iimplementation au besoin

Au besoin, son code est la : GitHub - hacf-fr/hass-ecoledirecte: Ecole directe integration for Home Assistant

( et je sais que l’integration meteofrance utilise aussi ce type de liste pour les forcast )

1 « J'aime »

Merci, je regarderai ça.

Je pense que ça serait plus simple d’utilisation et un peu plus optimisé car, je ne l’ai pas précisé, mais l’API mise à disposition est limitée à 5000 requêtes par jour, ce qui est bien mais qui peut vite être consommées si on a un taux de rafraichissement trop rapproché (l’intégration propose une fréquence de 12 min par défaut qui peut être réglée à la configuration).

Merci, top !
Je vais tester l’aspect pratique semaine prochaine

Salut

Il suffit d’assigner une « list » ou un « dict » à l’attribut.
Exemple :

    @property
    def extra_state_attributes(self):
        """Return the state attributes."""
        attributes = []
        attributes.append(...........)

        if is_too_big(attributes):
            _LOGGER.warning("attributes are too big! %s", attributes)
            attributes = []

        return {
            "updated_at": self.coordinator.last_update_success_time,
            "homework": attributes,
        }

Il y a une limite de taille pour les attributs : 16ko

Tu es déjà bien avancé, mais as-tu regarder ces tutos : Developpement - Home Assistant Communauté Francophone

Tu devrais avoir des plages d’horaire dans la configuration, pour interroger l’API qu’au moment opportun (Ex: 7h-9h et 16h-18h).

Merci, super ! Je pense que l’adaptation ne devrait pas être trop compliquée, faut que je vois ensuite comment ça s’articule pour afficher ces données dans les différentes cartes.

Trouver la bonne période de rafraîchissement est un peu compliqué, car j’aimerais que ça fonctionne bien pour beaucoup sans faire une usine à gaz.

Typiquement pour mon cas d’usage, spécifier un horaire de rafraîchissement n’est pas satisfaisant car je ne prends pas le train forcément au même horaire tous les jours.

Cependant, je fais tourner l’intégration depuis 1 bonne semaine maintenant et je n’ai jamais atteint la limite journalière (je suis 2 trajets avec le dernier trajet pour chacun), mais je pense que pour certains ça risque d’être limite … Je réfléchis à comment optimiser les appels, et pour l’instant je vois deux pistes :

  1. Détecter la fermeture et ouverture de service (en vérifiant par exemple que la date du prochain passage est le lendemain). Dans ce cas, ne plus faire de rafraîchissement jusqu’à 1h avant la reprise du service.

Par exemple, chez moi, j’ai une interruption de 7h environ (c’est probablement pas pareil partout mais bon), on pourrait donc économiser presque 1/4 des requêtes

  1. Ajouter un service au niveau de l’intégration qui permettrait de « forcer » un rafraîchissement, ainsi on pourrait augmenter le temps d’attente entre 2 rafraîchissements et faire une automatisation qui forcerait une mise à jour

Par exemple, avoir une entité qui donne l’heure à laquelle on souhaiterait partir, et déclencher une automatisation basée sur cette entité pour mettre à jour les données plus fréquemment avant l’heure de départ.

La grosse problématique qui est aussi l’intérêt de l’intégration, c’est qu’on veut savoir si le train qu’on a prévu de prendre sera bien là et à l’heure … et malheureusement ces informations sont souvent données dans les derniers instants.

1 « J'aime »

il faut donc (aussi) un bouton refresh (et avoir un statut de quand date le dernier refresh pour éviter qu’une personne clique 10fois d’affilé)

1 « J'aime »

C’est une possibilité, mais ce n’est pas indispensable. Une intégration met à disposition des appareils, des entités et des services. Les cartes et les interactions (scripts, scènes, automatisations, etc.) sont des éléments différents.

Comme dit plus haut, on pourrait imaginer une entité « Réveil » qui est câblée sur une autre intégration par exemple et qui remonte la douce alarme du téléphone le matin :smile: et appeler le service « update » de l’intégration à ce moment.

Ou encore, au moment du démarrage de la machine à café : déclencher l’appel au service et faire annoncer à son assistant vocal favori, la météo, les news, et les horaires des prochains trains (avec les incidents).

L’intégration doit être « bête et méchante » et faire ce qu’on lui demande en soit, même si on peut mettre en place quelques garde-fous pour éviter des erreurs de configuration évidemment.

Hello,

J’ai pris en compte vos remarques, et je publie donc une nouvelle version contenant quelques nouveautés

Mise à jour v0.1.0-alpha.1

Cette version comprend les mises à jour suivantes :

  • Ajout de l’intégration via HACS
    • Vous pouvez maintenant installer et sélectionner la version via HACS
  • Ajout d’une entité « journeys » qui contient l’ensemble des prochains trajets demandés dans une liste
  • Ajout d’une fonction expérimentale permettant d’arrêter de requêter l’API entre les horaires de fermeture et d’ouverture
  • Modification des noms par défaut des entités pour une meilleure lisibilité
  • Quelques améliorations du code

Renommage des entités :

Les entités sont maintenant nommées selon le trajet de l’appareil qu’elles suivent pour une meilleure lisibilité selon le schéma suivant :

sensor.train_traveler_<départ>_<arrivée>_next_journey_<numero_prochain_train>

où :

  • <départ> sont les 3 premières lettres de la gare de départ
  • <arrivée> sont les 3 premières lettres de la gare d’arrivée
  • <numéro_prochain_train> représente l’indice du train dans la liste

Exemple pour un trajet « Paris - Marseille » qui suit les 2 prochains trains, les entités seront nommées de la manière suivante :

sensor.train_traveler_par_mar_next_journey_1
sensor.train_traveler_par_mar_next_journey_2

Entité sensor.train_traveler_<départ>_<arrivée>_journeys :

Grâce à cette version, il est maintenant possible de ne travailler qu’avec une seule entité pour récupérer les informations des prochains trains.
Merci @roumano pour la suggestion

Exemple :

Capture d'écran 2024-05-31 164307

Avec le code suivant (utilisation de custom:flex-table-card) :

type: custom:flex-table-card
title: Départ - Arrivée
strict: true
sort_by:
  - Départ+
columns:
  - data: journeys
    modify: new Date(x.departure_time).toLocaleString()
    name: Départ
  - data: journeys
    modify: new Date(x.arrival_time).toLocaleString()
    name: Arrivée
  - data: journeys
    modify: |
      if (parseInt(x.delay) >= 3600 ) 
        '<div style="color:#E70B0B;">' + Math.trunc(x.delay /3600) + 'h' + Math.trunc((x.delay %3600)/60) + 'm</div>'
      else if (parseInt(x.delay) >= 60 ) 
        '<div style="color:#E70B0B;">' + Math.trunc(x.delay/60) + 'm'+ '</div>'
      else if (parseInt(x.delay) >= 1 ) 
        '<div style="color:#E70B0B;">' + x.delay + 's</div>' 
      else '<div style="color:#139523;">Aucun retard</div>'
    name: Retard
entities:
  include: sensor.train_traveler_mar_bor_journeys

Cela nécessite l’installation d’une card custom, c’est pourquoi j’ai laissé le détail par entité également pour les personnes qui souhaitent plutôt travailler en mode « vanilla ».

Expérimentation : arrêt des appels API entre les horaires d’ouverture et de fermeture

Vous pouvez activer cette option à la configuration d’un appareil (trajet), elle est notée « expérimentale » car il est possible que quelques effets de bords surviennent. Désactivez-là si vous rencontrez des soucis et un petit retour pour m’en faire part serait le bienvenu :slight_smile:

A+ et n’hésitez pas à me faire vos retours

2 « J'aime »

Bonjour,
Je voudrais tester cette intégration et j’ai relevé un petit bug :
Dans le readme sur github le lien du dépot personalisé qui est mettre dans HACS n’est pas bon, il faut un ‹ - › au lieu de ‹ _ › :

Merci pour le travail effectué :+1:

1 « J'aime »

Salut,

Sympa, ce petit dev.
Perso je vais regarder pour optimiser un peu l’affichage :

  • virer la date dans les départs/arrivées si c’est celle du jour.
  • virer les secondes dans les horaires… Les trains pile à la seconde près c’est pas en France :wink:

EDIT

type: custom:flex-table-card
title: Orléans - Paris
strict: true
sort_by:
  - Depart+
columns:
  - data: journeys
    modify: |
      const departure = new Date(x.departure_time);
      const today = new Date();
      if (departure.toDateString() === today.toDateString()) {
        departure.toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'});
      } else {
          departure.toLocaleDateString() +' '+ departure.toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'});
      }
    name: Départ
  - data: journeys
    modify: |
      const arrival = new Date(x.arrival_time);
      const today = new Date();
      if (arrival.toDateString() === today.toDateString()) {
        arrival.toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'});
      } else {
          arrival.toLocaleDateString() +' '+ arrival.toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'});
      }
    name: Arrivée
  - data: journeys
    modify: |
      if (parseInt(x.delay) >= 3600 ) 
        '<div style="color:#E70B0B;">' + Math.trunc(x.delay /3600) + 'h' + Math.trunc((x.delay %3600)/60) + 'm</div>'
      else if (parseInt(x.delay) >= 60 ) 
        '<div style="color:#E70B0B;">' + Math.trunc(x.delay /60) + 'm'+ '</div>'
      else if (parseInt(x.delay) >= 1 ) 
        '<div style="color:#E70B0B;">' + x.delay + 's</div>' 
      else '<div style="color:#139523;">Aucun retard</div>'
    name: Retard
entities:
  include: sensor.train_traveler_orl_par_journeys

image

3 « J'aime »

Good catch ! Un copier / coller un peu trop rapide. Je le note pour la prochaine release
Edit: C’est corrigé sur la branche main !

Effectivement, l’unité n’est peut être pas adaptée à notre système ferroviaire :stuck_out_tongue:

Merci pour les retours !

Bonjour,
Merci pour cette carte.
Par contre, j’ai un problème d’affichage quand il y a un retard: il me met « NaNm » ?

Oui normal.
La carte ne fait qu’afficher ce que fourmis l’intégration. Donc quand le NaNm s’affiche, c’est que l’info du retard n’est pas encore indiquée

Merci.
Je ne suis pas très fort en programmation de carte.
Est-ce qu’il serait possible dans ce cas d’afficher un truc du style « Pas d’information sur le retard » ?

Salut Pulpy,

Je note ta super technique de gestion des dates et comment afficher, que l’heure, proprement , …

a l’automne, j’ai fait la même chose pour une ligne de bus, et j’aimerais partager l’affichage que je trouve relativement sympa, si tu veut t’en inspirer … :

Capture d’écran du 2024-06-18 15-41-02

et le code ( un peut cracra, qui pourrait être simplifier avec une vrai gestion des dates) :

...
      sort_by:
        - depart+
      columns:
        - name: Depart
          data: list
          modify: |-
            if (parseInt(x.retard) >= 1 )
              '<div style="color:#E70B0B;">' + x.depart.substr(11,5) + ' -> ' + x.depart_reel.substr(11,5) + '</div>'
            else
              x.depart.substr(11,5)
        - name: Depart2
          data: list
          modify: x.depart
          id: depart
          hidden: true
        - name: DepartReel
          data: list
          modify: x.depart_reel
          id: departreel
          hidden: true
        - name: Retard
          data: list
          modify: |
            if (parseInt(x.retard) >= 3600 )
              '<div style="color:#E70B0B;">' + Math.trunc(x.retard /3600) + 'h' + Math.trunc((x.retard %3600)/60) + 'm</div>'
            else if (parseInt(x.retard) >= 60 )
              '<div style="color:#E70B0B;">' + Math.trunc(x.retard /60) + 'm'+ '</div>'
            else if (parseInt(x.retard) >= 1 )
              '<div style="color:#E70B0B;">' + x.retard + 's</div>'
            else
              '<div style="color:#139523;">' + x.retard + '</div>'
        - name: Statut
          data: list
          modify: |
            if (x.status != "programmer" || x.status != "UPDATED")
              '<div style="color:#139523;">' + x.status + '</div>'
            else
              '<div style="color:#E70B0B;">' + x.status + '</div>'

Bonne journée

2 « J'aime »

Salut,

@Matthieu_D1 Il doit y a avoir moyen de mettre une info sur la carte à partir du moment pour le retard n’est pas connu, mais c’est à mon sens plus un truc à faire coté intégration
@roumano Merci, je note l’idée de redéfinir la date de départ (et donc d’arrivée) avec le cumul du retard :wink: