Chargement…
| Pseudo | Inscrit le | Sur | Langue | Site | Newsletter | Offres | Droits | |
|---|---|---|---|---|---|---|---|---|
| Chargement... | ||||||||
Diffusés au hasard pendant un calcul, visibles par tous. « Prioritaire si long » : favorisés une fois le temps estimé dépassé.
| Bulle | Texte | Options | Traductions | ||
|---|---|---|---|---|---|
| Chargement... | |||||
| Tâche | Fréquence | Dernier passage | État | ||
|---|---|---|---|---|---|
| Chargement... | |||||
| Sauvegarde · quand | Dernière copie | Taille | Conservation | État |
|---|
| Conteneur | État | Processeur | Mémoire | Réseau (reçu / envoyé) | Disque (lu / écrit) | Processus |
|---|
backend/app/weather/__init__.py (calage sur ABRP, voir la Méthode de calcul).
| Date | État | Source, passage, durée |
|---|
Troisième choix météo, « Selon prévisions » : la consommation de chaque tronçon de 5 km suit la météo prévue à l'endroit et à l'heure de passage (température, vent de face ou de dos, pluie), à partir de la base « Standard » (= 20 °C sans vent ni pluie). Le chauffage dépend de la pompe à chaleur du véhicule (de série, ou en option cochée « Oui » par la personne) et de sa taille (masse). Prévisions DWD ICON-EU (secours ECMWF) téléchargées quatre fois par jour, environ 4 h après chaque passage du modèle (4 h 15, 10 h 15, 16 h 15, 22 h 15 UTC) ; normales climatiques ERA5 (1991-2020) pour les trajets réguliers (mois en cours), au-delà de J+2 ou sans prévision. Formules et comparaison ABRP : « Méthode de calcul », section 3. Voyant vert = prévision à jour, dernier relevé réussi à la source principale, normales chargées ; rouge sinon (raison affichée).
| Date | État | Superchargeurs | Remarque |
|---|---|---|---|
| Chargement... | |||
Chargement…
Page non référencée par les moteurs de recherche. Glisser catégories et visuels pour les réordonner ; un nouveau visuel n'est visible qu'une fois publié.
Chargement…
État de chaque demande sur chaque plateforme, puis double vérification (le Mac vérifie Android, le PC vérifie iOS). Rien ne se modifie ici.
Chargement…
| Traité | Reçu le | Nom | Adresse mail | Message | Envoyé |
|---|---|---|---|---|---|
| Chargement... | |||||
| Objet | Traduit | Modifié | |
|---|---|---|---|
| Chargement... | |||
| Objet | Langue | Destinataires | Statut | Envoi |
|---|---|---|---|---|
| Chargement... | ||||
| Date | Type | Destinataire | Objet | Statut |
|---|---|---|---|---|
| Chargement... | ||||
| Jour / heure | Type | Origine | Lieux | Véhicule | Résultat | |
|---|---|---|---|---|---|---|
| Chargement... | ||||||
| Véhicule | Batt. | Utile | Prise | DC max | Conso | Masse | PAC | Précond. | Sources | Courbe | Vérifié |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Chargement... | |||||||||||
| Badge | Émetteur | Prix du badge | Réseaux couverts | Origine des prix | Clics |
|---|---|---|---|---|---|
| Chargement... | |||||
| Opérateur | Identifiant | Type | Valeur | Plafond | Utilisé par | Conditions | Mise à jour |
|---|---|---|---|---|---|---|---|
| Chargement... | |||||||
Principes de calcul, hypothèses retenues et techniques d'accélération, avec l'impact de chacune sur le résultat. Les valeurs chiffrées sont celles du code au moment de la rédaction (référence entre parenthèses) : si une constante change, cette page doit être mise à jour.
Un calcul se déroule toujours dans le même ordre :
waycategory) ; GraphHopper : classe de route et vitesse limite/moyenne par tronçon (path details). OSRM n'en fournit pas. Un itinéraire sans profil retombe sur une vitesse constante déduite de sa vitesse moyenne.country_time.py). Modèle : durée = facteur du pays × (temps brut + 10 % du temps passé sur les autoroutes limitées à plus de 120 km/h) + 4 min. Les 4 minutes sont une marge fixe par itinéraire (réduite à 25 % de la durée pour un très court trajet). Facteurs (campagne du 28/10/2026 : 241 trajets de jour à 10 h dans 17 pays, jeudi, trafic habituel de Google) : France 1,05 ; Allemagne 1,28 ; Italie 1,08 ; Espagne 1,03 ; Portugal 1,02 ; Royaume-Uni 1,16 ; Pays-Bas 1,08 (1,09 jusqu'au 30/10/2026) ; Belgique 1,08 ; Suisse 1,13 ; Autriche 1,11 ; Luxembourg 1,17 ; Irlande 1,21 ; Suède 1,03 ; Norvège 0,96 ; Finlande 0,98 ; Danemark 1,11 ; Pologne 1,14 ; autres pays (non relevés) 1,10. Erreur moyenne avec Google 6,6 % (validation croisée, biais -0,7 %), contre un modèle précédent trop court de 11,5 % à 10 h. Seule la durée change : ni la distance, ni le profil de vitesse, donc pas la consommation. Pays-Bas : en plus, les autoroutes limitées à 100 km/h passent à 130 de 19 h à 6 h (OpenStreetMap : maxspeed:conditional, ignoré par GraphHopper dont le temps est celui du jour) ; le temps de ces seuls tronçons est multiplié par 0,85 la nuit si l'heure de départ est indiquée (heure du tronçon = départ + temps déjà roulé), sinon 1,00 (au plus défavorable : le jour). Limites : relevés de jour seulement (la nuit est environ 3 % plus courte, la pointe de 17 h 45 environ 5 à 8 % plus longue : pas encore corrigé), échantillon mince pour le Luxembourg, la Belgique, l'Irlande, la Finlande, la Suède, la Norvège et le Danemark (4 à 11 trajets), montagne (-3 %) un peu sous-estimée. Coût : environ 2 à 6 ms une fois par itinéraire calculé, 0,03 ms ensuite. Idée « péage × 1,10 » écartée (apport nul).backend/app/data/villes_20000.csv, 4 873 villes d'Europe), chargées une fois au démarrage ; le test ne fait aucune requête à une base ni au réseau. Effet mesuré sur les 461 trajets : urbains -8,8 % à -4,0 %, erreur moyenne 6,7 % à 6,4 %, trajets de moins de 40 minutes brutes -4,9 % à -1,5 %, aucune autre catégorie ne sort de ±4 % (A -0,2 %, V +2,7 %, C +3,0 %, M -3,2 %). Validation : 400 tirages 70/30, gain dans 94 % des tirages ; test de permutation (mêmes trajets, villes mélangées) p < 0,001 ; la même correction sans condition de ville dégrade (erreur 6,9 %). Réserves : 74 trajets seulement concernés, valeur de la correction à ± 0,75 minute près, le gain n'apparaît pas quand on calibre sur les 221 trajets récents et qu'on teste sur les 240 plus anciens (erreur inchangée à 6,2 %), il apparaît dans l'autre sens (7,3 % à 6,7 %). Même passage : facteur des Pays-Bas ramené de 1,09 à 1,08 (réajustement sur les 461 trajets : 1,083), faute de quoi la correction urbaine aurait porté le biais néerlandais de +2,9 % à +3,6 %. Les 17 pays restent dans ±3 % (extrêmes : Pays-Bas +2,7 %, Pologne +2,3 %, Irlande +2,0 %, Norvège -1,9 %). Coût : environ 3 microsecondes par itinéraire court (0,2 µs au-delà de 45 minutes), 4,4 Mo de mémoire par processus.La consommation dépend de la vitesse réelle de chaque tronçon : énergie d'un tronçon = distance × consommation de référence (fiche véhicule, kWh/100 km, proche du WLTP à ~90 km/h) × facteur météo × facteur vitesse du tronçon. Elle est ramenée en points de batterie (SOC) par rapport à la capacité utile du véhicule. Les énergies cumulées le long du trajet sont précalculées : l'énergie d'un arc borne → borne est une simple différence, sans ralentir la recherche.
Vitesse de croisière d'un tronçon : sur autoroute, la limite affichée du tronçon (OpenStreetMap, détail max_speed de GraphHopper) bornée à 130 -- 120 en Belgique, en Suisse, en Espagne, 100 aux Pays-Bas le jour, 70 mph au Royaume-Uni, 110 ou 90 sur certaines portions françaises, 130 sur une autobahn sans limite ou si la limite est inconnue (et toujours 130 avec ORS) ; depuis le 06/10/2026, avant : 130 sur toute autoroute. Effet en France : Paris → Marseille (Mégane 60, météo standard) 116,6 → 112,2 kWh rechargés, 41,50 → 39,88 € ; Rennes → Bordeaux 30,3 → 30,2 kWh. Plafonnée par « vitesse maximale sur autoroute », qui ajuste aussi la durée, voir section 2 ; ailleurs, la vitesse moyenne du tronçon ramenée à la limite légale immédiatement supérieure (30, 50, 70, 80, 90, 110) ou, avec GraphHopper, la vitesse limite OSM ; toujours plafonnée par le réglage « vitesse maximale sur autoroute » (choix selon le pays, voir le tableau). Les tronçons à 80-90 km/h réduisent donc la consommation par rapport à la référence.
| Paramètre | Valeur | Explication et impact |
|---|---|---|
| Facteur météo « standard » | × 1,05 (réglable) | Modifiable dans l'onglet « Réglages du calcul » (table app_settings, prise en compte au calcul suivant, valeurs entre 0,5 et 2, « mauvaises » ≥ « standard » ; les valeurs affichées ici sont les défauts). Temps doux à frais (environ 10 à 25 °C). Valeur par défaut. Ne porte que sur la température / le vent / la pluie : l'effet de la vitesse est modélisé à part (1,15 avant la modélisation de la vitesse, puis 1,08 ; plusieurs essais en septembre 2026 ; 1,05 retenu par l'utilisateur, ABRP annonçant 23 % d'arrivée sur Rennes ↔ Angers en Mégane 60). Repère indépendant : Mégane E-Tech 60 par 14-18 °C = 23,2 kWh/100 km à 130 km/h et 20,9 à 120 km/h (Caradisiac) ; avec × 1,05 et le facteur vitesse ci-dessous, le calcul donne 20,9 et 19,8 (volontairement en dessous, voir la ligne « facteur vitesse »). L'ancienne option « bonnes » est fusionnée dans « standard » ; l'API accepte toujours good et le traite comme « standard ». |
| Facteur météo « mauvaises » | × 1,15 | Froid, pluie, vent ou forte chaleur (chauffage ou clim + résistance à l'avancement). Un vrai froid vif (≈ −5 °C) dépasserait +25 %. Était × 1,25 jusqu'en septembre 2026. |
| Météo « Selon prévisions » (05/10/2026) | × 1 par 20 °C sans vent ni pluie | Troisième choix, à côté de « Standard » et « Mauvaises » (inchangés). Le facteur « standard » sert de base (temps doux), puis chaque tronçon de 5 km est corrigé selon la météo prévue à l'endroit et à l'heure où l'on y passe (heure de passage estimée d'après le profil de vitesse ; heure de départ choisie, sinon maintenant ; trajets enchaînés : fin du trajet précédent). Calculé une fois sur le profil d'énergie de l'itinéraire (app/weather, planner/energy.py) ; aucun appel météo pendant un calcul. Par tronçon : consommation = référence × [0,78 × roulement + 0,22 × (v/90)²] × pertes à froid + écart de traînée dû au vent et à la densité de l'air + chauffage ou clim.
weather_updater, onglet « Tâches planifiées ») : 4 h 15, 10 h 15, 16 h 15 et 22 h 15 UTC, environ 4 h après chaque passage ICON-EU (00, 06, 12 et 18 UTC) ; un créneau ne télécharge rien tant qu'aucun passage plus récent n'est complet (nouvel essai toutes les 30 min). Avant le 10/10/2026 : deux fois par jour (4 h 10 et 15 h 10), un départ à 18 h 30 utilisait le passage de 6 h UTC. E-mail d'alerte aux administrateurs seulement si le voyant reste rouge au moins 2 h (un relevé raté puis rattrapé n'envoie plus rien), jamais depuis un serveur de développement. Normales (secours) : ERA5 1991-2020 (Copernicus C3S, licence CC-BY 4.0, scripts/weather_normals.py, fichier data/weather/normals.npz) : température moyenne de chaque mois heure par heure (cycle journalier réel), vent et pluie moyens du mois ; utilisées pour un trajet régulier (plus d'une fois par mois : normales du mois en cours, décision de Jérémy), au-delà de J+2 ou sans prévision de moins de 36 h ; sans normales → facteur neutre, c.-à-d. exactement « Standard ». Voyant vert / rouge en haut de l'admin et dans « Réglages du calcul » (journal des relevés sur 90 jours) : rouge si la prévision a plus de 20 h, si le dernier relevé a échoué ou est passé par la source de secours, ou si les normales manquent ; e-mail aux comptes administrateurs au passage au rouge (une fois par incident) et au retour au vert (vérifié après chaque relevé et toutes les heures par le service weather_updater). Calage ABRP (ABRP réglé à usure 0 %, 130 km/h, départ 90 %, météo imposée par son API, rapport à 20 °C ; Rennes → Angers) : vent et pluie sur la Mégane E-Tech 60 : vent 30 km/h de face ABRP 1,271 / Kargit 1,221, de dos 0,816 / 0,818 ; forte pluie 1,045 / 1,058 ; Paris → Orléans 0 °C ABRP 1,198 / Kargit 1,194. Chauffage ajusté sur 12 véhicules (petits, moyens, grands, avec et sans pompe à chaleur), écart quadratique moyen 0,05 sur 10 °C, 0 °C et −10 °C ; à 0 °C (ABRP / Kargit) : Spring 1,131 / 1,195 ; 500e 1,118 / 1,210 ; e-208 1,202 / 1,225 ; ID.3 1,305 / 1,232 ; Mégane 1,207 / 1,214 ; avec pompe à chaleur : Model 3 1,132 / 1,157 ; Model Y 1,167 / 1,151 ; i4 1,169 / 1,153 ; bZ4X 1,175 / 1,171 ; EV6 1,164 / 1,155 ; EQS 1,121 / 1,172 ; Zoé 1,111 / 1,142. ABRP a un modèle propre à chaque véhicule (tiré des données de ses utilisateurs) : Kargit retrouve la tendance (masse, pompe à chaleur) mais pas les particularités d’un modèle (ID.3 sans pompe à chaleur nettement plus gourmand chez ABRP, Spring et 500e plus sobres). 30 °C : ABRP 0,997 à 1,036 selon le véhicule, Kargit 0,99 à 1,01. Limites : verglas et neige au sol sans chute non comptés ; vent « moyen » sur 25 km ; pompe à chaleur « en option » comptée sans tant que la personne n’a pas répondu « Oui » |
| Facteur vitesse d'un tronçon | 1,00 à 90 km/h | 0,78 + 0,22 × (v / 90)², v = vitesse de croisière plafonnée : 22 % de la consommation de référence est supposée aérodynamique (40 % jusqu'en septembre 2026, puis 32 %). Calé sur ABRP : Mégane E-Tech 60 partant de 28 rue Marthe Simard à Rennes à 100 % arrive à 12 % chez Atlante Chambray-lès-Tours (258 km) ; le calcul donne 11,5 % (contre −1 % avec 40 %, 5 % avec 32 %), et 6 arrêts sur Rennes → Nice comme ABRP. 130 km/h → × 1,24 ; 120 → × 1,17 ; 110 → × 1,11 ; 90 → × 1,00 ; 80 → × 0,95. Compromis assumé : cela donne ≈ 20,9 kWh/100 km à 130 km/h pour la Mégane 60, en dessous des 23,2 mesurés par Caradisiac par 14-18 °C, donc optimiste pour quelqu'un qui roule réellement à 130 (plafond « vitesse max » du profil ou facteur météo pour compenser). Plancher à 0,75 (jamais atteint avec ce réglage). Approximation, pas une simulation physique. Part aérodynamique propre à chaque véhicule (26/09/2026) : les 22 % valent pour la Mégane E-Tech 60 (SCx 0,648 m² pour 16,1 kWh/100 km) ; pour les autres, la part suit la traînée rapportée à la consommation de référence, 0,22 × (SCx / conso) / (0,648 / 16,1), bornée entre 0,14 et 0,32 (planner/energy.py, aero_fraction). SCx = Cx × surface frontale, colonne vehicles.aero_cda_m2 (sources dans aero_source) : SCx publié, sinon Cx × surface publiée, sinon Cx × 0,84 × largeur × hauteur ; 194 véhicules sur 211, les autres gardent 0,22. Exemples à 130 km/h : Model 3 GA × 1,21, BMW i4 × 1,20, Mégane × 1,24, Kona 64 × 1,28, Dacia Spring × 1,32. Sur les 20 trajets comparés à ABRP (départ 90 %), l’écart absolu moyen de durée passe de 4,1 à 3,8 %. Calculé une fois par itinéraire : sans effet sur le temps de recherche. Affichage (30/09/2026) : « Consommation retenue » des Options avancées (site et appli, vehicleCruiseFactor) et la consommation autoroute du profil utilisent la même part propre au véhicule ; avant, les Options prenaient 0,22 pour tous (Ioniq 5 84 kWh à 130 km/h, météo standard : 21,6 affiché contre 22,0 calculé). |
| Vitesse max sur autoroute | France, Allemagne, Italie, Luxembourg : 110 / 120 / 130 km/h (défaut 130) ; Belgique, Suisse, Espagne : 100 / 110 / 120 (défaut 120) ; Pays-Bas : 100 / 110 / 120 / 130 (défaut 130) ; Royaume-Uni : 60 / 65 / 70 mph = 97 / 105 / 113 km/h (défaut 70 mph) | Plafond de toute vitesse de croisière ; choix selon le pays du site ou de l'appli (SPEED_BY_COUNTRY, 06/10/2026), la limite du pays par défaut. Réglage des options avancées (« Mon véhicule et ma conduite » du profil jusqu'au 07/10/2026 ; « Appliquer » : cette recherche ; « Appliquer et mémoriser » : default_max_speed_kmh) ; une valeur enregistrée hors des choix du pays est ignorée (défaut du pays). L'ancienne valeur 90 est devenue 110. Rennes → Lille (Audi Q4) : 130 → 88,7 kWh rechargés, 110 → 66,7 kWh (−25 %). Allonge aussi la durée de conduite (voir section 2 : + 45 min sur Rennes → Lille à 110 km/h). |
Fiche du véhicule (vehicle_spec) | 07/10/2026, site et appli | Le véhicule choisi est envoyé avec chaque calcul sous forme de fiche complète (schemas.VehicleSpec, app/vehicle_spec.py) : nom, modèle d'origine (base_vehicle_id, absent pour un véhicule créé de zéro), batterie utile (10-200 kWh), batterie totale, consommation mixte (3-80 kWh/100, valeur de référence : météo et vitesse s'appliquent par-dessus), puissance max (20-400 kW), prise (CCS | CHAdeMO), dégradation (0-30 %), pompe à chaleur, préconditionnement, masse à vide, SCx, courbe de recharge (12 paliers au plus). Ce qui n'est pas saisi reprend les valeurs du modèle ; courbe : celle de la fiche, sinon la courbe du modèle tant que la puissance max n'a pas changé, sinon la courbe par défaut. Un véhicule créé de zéro calcule sur le support du véhicule standard (standard_default) avec ses propres valeurs. Appliquée à une copie du véhicule pour le calcul, jamais en base. Remplace les anciens réglages « consommation personnalisée » et « dégradation » (consumption_override_kwh_100km, battery_degradation_percent, toujours acceptés pour les anciens liens). Lu une fois par calcul : sans effet sur la durée de recherche. Valeurs cohérentes seulement (07/10/2026, site, appli et serveur, mêmes messages) : champs numériques limités aux chiffres, à un séparateur décimal (virgule ou point) et aux espaces des milliers, 7 caractères ; « Saisissez un nombre » sinon ; nom obligatoire, 60 caractères, jamais deux fois le même nom dans « Mes véhicules » (aux majuscules, accents et espaces près ; « Dupliquer » donne « (copie) », « (copie 2) »…) ; courbe : paliers complets qui se suivent de 0 à 100 % sans trou ni chevauchement, fin > début, puissance entre 1 kW et la puissance max (« Palier 2 : doit commencer à 70 % »…, vehicle_spec.curve_problem, contrôlé aussi par /api/account/vehicles) ; une rangée de courbe vide est ignorée. |
| Dégradation batterie | 0-30 %, 5 % proposé à la confirmation | Capacité retenue = batterie utile × (1 − dégradation %) (ex. Mégane E-Tech 60, 5 % → 57,0 kWh). Réglée à la confirmation d'un modèle du catalogue (5 % par défaut) ou dans la configuration avancée ; pour tous, connecté ou non. |
| Équipements en option | Pompe à chaleur, préconditionnement, charge rapide CCS | Questions posées à la confirmation seulement si l'équipement est en option sur le modèle (colonnes de l'onglet Véhicules) : « Non · Je ne sais pas » par défaut (compté sans), « Oui » ; de série → « Oui » non modifiable dans la configuration avancée. Charge rapide CCS en option (vehicles.dc_charge = 'option' : Zoé R110/R135, e-up! 2020, Twingo E-Tech, Spring 2e génération) : « Oui » par défaut ; « Non » bloque le choix (Kargit ne planifie que des recharges rapides). |
| Mes véhicules | 10 au plus par compte | « Choisir » vaut pour la recherche en cours (site : aussi l'adresse de la page ; appli : ce téléphone) ; « Choisir et mémoriser » (compte connecté) ajoute la fiche à « Mes véhicules » (table user_vehicles, /api/account/vehicles) et en fait le véhicule par défaut du compte (users.selected_user_vehicle_id), repris au chargement suivant. Véhicule par défaut supprimé : le plus ancien des véhicules restants devient le véhicule par défaut (07/10/2026). Un invité calcule avec sa fiche sans rien enregistrer. Lien partagé (décision de Jérémy, 07/10/2026) : le véhicule du lien ne s'applique que si la personne qui l'ouvre n'a pas de véhicule dans son compte ; un rechargement de sa propre page garde son véhicule. Aucun véhicule choisi : le calcul ouvre d'abord « Choisir mon véhicule ». Anciens véhicules personnalisés repris une fois par la migration 20261007_user_vehicles.sql. |
| Batterie mini en roulant | 10 % | SOC minimum toléré avant une borne (option « batterie mini »). |
| Batterie mini à l'arrivée | 10 % | SOC minimum à la destination finale. |
Dénivelé (septembre 2026) : ORS renvoie l'altitude du tracé (données SRTM, environ 5 km d'échantillonnage, lissée sur 3 échantillons pour ne garder que le relief de fond : le relief ondulé est déjà compris dans la consommation de référence, calée sur ABRP). Par intervalle : en montée, énergie = énergie à plat + dénivelé × masse × g / 0,85 (rendement batterie → roues). En descente (refait le 10/10/2026, planner/energy.py::_add_terrain), la gravité remplace d'abord la traction du tronçon à plat : énergie = à plat − dénivelé × masse × g × 0,6 ; quand elle dépasse ce que le tronçon aurait consommé (pente forte et vitesse modérée), la consommation tombe à zéro et l'excédent recharge la batterie (rendement 0,6), mis en réserve pour les tronçons suivants (la consommation cumulée reste croissante, ce dont la recherche a besoin ; une réserve encore inutilisée à l'arrivée est rendue à la batterie d'arrivée). Pas de régénération au-dessus de 95 % de batterie (départ à 100 % d'une station de ski), suivi depuis la batterie de départ. Le seuil « ni conso ni recharge » dépend donc de la vitesse et de l'aérodynamique, pas d'une pente fixe. Le 0,6 est calé sur ABRP (Mégane 60, 7 % de dégradation, 9 trajets) : la théorie (1/0,85 = 1,18) rendait Kargit 5 à 10 points plus optimiste qu'ABRP en montagne (freins à friction dans les virages et aux limitations, ralentissements, relief lissé) ; avec 0,6 : Grenoble → Briançon −2,9 points par rapport à ABRP, Briançon → Grenoble −2,9, Clermont → Millau +1,3, Millau → Clermont +1,9, Val Thorens → Albertville +0,4, Rennes ↔ Plouigneau et Cancale → Rennes à moins d'1 point. Avant (septembre) : −0,5 × énergie potentielle, sans jamais de gain. Masse = masse à vide de la fiche technique + 100 kg (passager et bagages) ; les masses des 210 véhicules ont été relevées sur fiches-auto.fr, ev-database.org, meilleurelectrique.fr, auto-data.net et les fiches constructeur (colonne « Masse à vide » de l'onglet Véhicules, source en infobulle), avec repli 1150 kg + 7 kg par kWh de batterie (véhicule générique). Les itinéraires GraphHopper/OSRM, sans altitude, l'empruntent à l'itinéraire ORS voisin (au moins 35 % du tracé en commun, sinon relief ignoré). Ordres de grandeur (Mégane 60, météo standard) : Grenoble → Briançon +12,7 points de batterie (+6,6 kWh/100 km), Briançon → Grenoble −1,1, Chambéry → Modane +7,2, Modane → Chambéry −2,8, Nice → Barcelonnette +17,3, Rennes → Nice +11,1 sur 1204 km, Rennes → Tours +1,5. Calculé une fois par itinéraire : aucun impact mesurable sur les temps de calcul (ORS 116 ms sans altitude, 117 avec). Le détour vers une borne n'inclut pas le relief.
Hypothèses non modélisées : température réelle, vent réel, préconditionnement de la batterie. Les détours vers une borne sont comptés au taux de consommation moyen du trajet, à plat (leur vitesse et leur relief sont inconnus). La vitesse moyenne d'une étape du routeur est une approximation de la vitesse de croisière (ronds-points et virages la tirent vers le bas, d'où l'arrondi à la limite légale).
scripts/recalibrate_charging_curves.py) : chaque courbe garde sa forme mais est mise à l'échelle (plafonnée à la puissance DC max) pour donner un temps 10 → 80 % réaliste, tiré des mesures du Supertest 2024 d'Automobile Propre quand le modèle y figure (ID.7 Pro 27 min, Cupra Born 28, Volvo EX30 28, Scénic 38, Peugeot e-208 29, ID.3 Pro 32, Renault 5 30) ou du temps constructeur + 10 à 15 % de marge (Ioniq 5 / EV6 : 20 min, Model 3 GA 30, Niro EV 47, Leaf e+ 50, Dacia Spring 52…). Mégane E-Tech 60 : 10 → 80 % en 35 min (34 à 38 min mesurés, 30 annoncés), 10 → 100 % en environ 1 h ; la charge 80 → 100 % est longue pour tous les véhicules (10 → 100 % : 43 min pour une Ioniq 5, 55 à 75 min pour la plupart des autres). Les formes elles-mêmes restent des approximations par famille de plateforme documentées dans db/seed_charging_curves.sql. À défaut de courbe : 2 paliers (100 % de la puissance DC max jusqu'à 70 % de SOC, puis 40 % au-delà). 22/09/2026 : 41 véhicules ajoutés (recherche sourcée par famille de plateforme -- Stellantis eCMP/eCMP2/STLA, VW MEB, Hyundai/Kia E-GMP 400V/800V, BYD LFP, Renault-Nissan AmpR Small, BMW FAAR/Neue Klasse, Mercedes MFA2/EVA2, Toyota e-TNGA, Xpeng SEPA 2.0, MG MSP, Honda, Ford, Smart SEA -- voir scripts/recalibrate_charging_curves.py pour le détail par véhicule et les sources). Pour une plateforme encore absente de la base, la forme est reprise d'un véhicule de la plateforme la plus proche déjà calibrée puis remise à l'échelle sur la cible 10 → 80 % propre à ce véhicule (mesurée indépendamment quand trouvée, sinon constructeur + marge comme les autres). 25/09/2026 : 130 véhicules ajoutés, dont la Dacia Spring 2e génération (commandes ouvertes le 08/09/2026), les modèles électriques vendus neufs qui manquaient encore et les modèles d’occasion courants (Renault Zoé ZE50 R110/R135, Nissan Leaf 40 kWh en CHAdeMO, Kona et e-Niro 64 kWh de 1re génération, BMW i3, e-Golf, e-208/e-2008/Corsa-e 50 kWh, anciennes Model 3/S/X, e-tron, I-Pace, EQC…), avec les années dans le libellé. Même méthode (une source par champ, forme de courbe de la plateforme la plus proche, recalage sur la cible 10 → 80 % du véhicule) ; les cibles sont lues dans scripts/vehicles_batch_20260925.json (statut par véhicule : mesure indépendante, équivalence, constructeur + marge, ou estimation EV Database quand rien d’autre n’a été trouvé). Le catalogue ne couvre que les versions capables de recharge rapide DC : Zoé ZE40 ou ZE50 sans option CCS, Twingo Electric, Smart EQ, Kangoo Z.E. n’y figurent pas (aucune borne de la base ne pourrait les recharger). Famille e-3008 73 kWh (e-3008, ë-C5 Aircross, Grandland, Compass) : 10 → 80 % commun de 40 min. Les fiches qui décrivent une ancienne version portent désormais leurs années (Spring 2021-2025, Mini Cooper SE F56, EQA 250, EV6 77,4 kWh, RZ 300e, XC40 Recharge), la version actuelle ayant sa propre fiche. 08/10/2026 : 423 véhicules ajoutés, passe « tous les VE vendus en Europe » (demandée par Jérémy après des retours d’utilisateurs : Ioniq 5 58 kWh, ID.3 Pure 52 kWh). Liste de contrôle : les 1 472 versions européennes d’EV Database, plus la liste France du 30/09 ; toutes marques, rares comprises (NIO, Zeekr, Lucid, TOGG, Hongqi, Voyah…), anciennes versions jamais homologuées WLTP comprises (consommation NEDC, optimiste, signalée dans la fiche). Une fiche par version techniquement différente (batterie utile, puissance DC max y compris d’une année-modèle à l’autre, consommation WLTP différente de plus de ~8 %), une source par champ. Les fiches portent aussi la pompe à chaleur et le préconditionnement (météo « Automatique selon prévisions ») et le SCx quand il est publié. Les voitures commandables mais pas encore livrées sont incluses (décision de Jérémy : Audi A2 e-tron, BMW i3 Neue Klasse, Range Rover Sport Electric…). Écartées : versions sans recharge DC, et les fiches sans consommation homologuée ou sans puissance DC max connue (non importées par scripts/import_vehicles_batch.py). Cibles 10 → 80 % : scripts/vehicles_batch_20261008.json (surtout des estimations EV Database, quelques mesures indépendantes). Fiches existantes corrigées le même jour d’après EV Database (scripts/vehicles_corrections_20261008.sql) : batterie utile de la Mach-E ER RWD (93,8 → 88), Fiat 500e (39,9 → 37,3), Dacia Spring 2021-2025 (26,8 → 25,0, 26,8 étant la brute), BYD Seal U Comfort (69 → 71,8), Omoda E5 (60,8 → 58,9), Lynk & Co 02 (66 → 62) ; la fiche Scénic 87 passe à la version 89 kWh annoncée par Renault en septembre 2026 (10 → 80 % : 38 → 32 min).stations_query.py, 25/09/2026) : pour une voiture en CHAdeMO (Nissan Leaf), c’est la puissance de la prise CHAdeMO de la fiche borne, et 50 kW quand elle est inconnue (CHAdeMO porté par le même point qu’un CCS) ou pas crédible (au-delà de 100 kW : Ionity déclare 350 kW pour les CHAdeMO de ses « Multicharger », limités à 50 kW d’après son support ; les CHAdeMO déclarés chez Ionity et Electra existent bien, une prise par station environ) -- avant, elle héritait de celle du CCS (« 300 kW » chez Allego) ; Leaf e+ Paris → Lyon : 83 → 96 min de recharge. Voitures en CCS : inchangé, sans coût ajouté à la recherche. Correction de septembre 2026 : la version précédente faisait la moyenne arithmétique des puissances, ce qui surestimait la puissance dès qu'on traverse des paliers lents (Mégane E-Tech 60, 28 → 100 % : 34 min annoncées, 43 min avec le bon calcul, + 4 min d'arrêt). Effet sur les plans : temps de recharge cumulé Rennes → Lille 75 → 89 min, Paris → Marseille 105 → 132 min.station_access.py, appelé par candidates_for_route). Verdict mémorisé par borne et par sens (secteur de 45°), en mémoire et en base (table station_access_checks) : une aire n'est vérifiée qu'une fois par sens. Sur les 20 trajets de test : 230 bornes vérifiées, 90 écartées (ex. « A7 - Aire de la Bouterne (direction Lyon) » en descendant : +55 km réels pour 0,6 km estimé ; IONITY Corbières Nord en allant vers le sud) ; 2 trajets sur 20 changent d'arrêt. Temps de calcul : inchangé une fois les verdicts mémorisés ; première vérification limitée à 2,5 s et 40 bornes par itinéraire (≈ +0,5 s par trajet la première fois). Carte : sur le trajet sélectionné, un trait fin montre le chemin réel vers chaque borne (sortie, accès, retour), demandé après l'affichage (POST /api/stop-access, GraphHopper, sans effet sur le temps de recherche). Depuis le 26/09/2026, ce temps de détour compte aussi dans le choix des bornes (graph.py et _search_native.pyx, arête de conduite) : en « le plus rapide » 1 min de détour pèse comme 1 min de charge, en équilibré au même prix de la minute, en « le moins cher » il ne sert qu'à départager deux coûts égaux. Avant, il n'était compté que dans la durée affichée : Model Y Propulsion Lille → Lyon choisissait un Superchargeur à 7 km de l'axe pour une minute de charge gagnée (7 h 36 → 7 h 27 avec la correction). Parité moteur compilé / Python : 432 recherches identiques ; temps de calcul inchangé. « Le plus rapide » tient compte du prix depuis le 29/09/2026 (retour d'un utilisateur, accord de Jérémy) : classement = coût + durée × 1 €/min (FAST_VALUE_OF_TIME_EUR_PER_MIN), au lieu de la durée seule. Le temps reste très prioritaire (un arrêt qui fait gagner 10 min pour 5 € de plus est toujours choisi), mais Kargit ne paie plus 12 € pour une minute : Limoges → Kembs (Model Y) prenait un Allego à 0,59 €/kWh pour gagner 1 min de détour sur le Superchargeur de Limoges à 0,32 €/kWh. Aller-retour Kembs ↔ Limoges : 922 min / 128,29 € → 931 min / 89,03 € ; Marseille → Toulouse (Model Y) : même durée, 33,39 → 24,33 € (le départage entre options aussi rapides était arbitraire). Le moteur compilé utilise sa formule « équilibré » avec cette valeur du temps (aucune recompilation). Temps de calcul : 0 à 0,1 s de plus selon le trajet (mesures sur 4 recherches).Règles de confort (ajoutées car le plan mathématiquement le moins cher proposait des arrêts absurdes, par exemple +1 point de batterie pour 0,22 €) :
Une limite optionnelle du nombre d'arrêts est aussi disponible (« m'arrêter moins souvent, quitte à payer plus »).
| Filtre | Valeur par défaut | Remarque |
|---|---|---|
| Couloir autour du tracé | 8 km | Recherche spatiale exacte (PostGIS) sur toute la géométrie du trajet. |
| Puissance DC minimale | 50 kW | Modifiable dans le profil / les options. |
| Connecteur | celui du véhicule | CCS, CHAdeMO ou Type 2. |
| Points de charge minimum | 1 | Évite les bornes uniques si l'utilisateur le demande. |
| Opérateurs exclus | choix utilisateur | En plus des opérateurs sans prix vérifiable (Freshmile, Greenspot), gardés en base mais jamais proposés. |
| Regroupement | tranche de 3 km × opérateur | Voir « Vitesse de calcul » : on ne garde qu'une borne par tranche et par opérateur (détour le plus court, puissance la plus élevée en départage). |
L'ordre des bornes le long du trajet est obtenu en projetant chaque borne sur un sous-échantillon d'environ 300 points du tracé : imprécision de quelques centaines de mètres, sans effet sur le résultat.
Fiche borne (survol sur la carte). Elle ne change rien au calcul : elle est demandée à la volée au survol d'un arrêt (jamais pendant la recherche, donc sans effet sur sa durée) et rejoue le tarif de la borne pour cette session (énergie, durée et heure d'arrivée de l'arrêt). Les connecteurs viennent de la base IRVE : nombre de prises et puissance max par type (CCS, Type 2, CHAdeMO), recalculés à chaque import. L’IRVE ne donne la puissance que par point de charge : sur un point rapide qui porte aussi une prise Type 2 (courant alternatif) ou un CHAdeMO à côté du câble CCS, cette puissance ne vaut que pour le CCS ; ces prises-là sont affichées sans puissance, et un point Type 2 seul est plafonné à 43 kW (corrigé le 25/09/2026, la fiche affichait « Type 2 150 kW »). Position des bornes : l’import quotidien (scripts/import_irve.py) ne garde que les coordonnées situées en France métropolitaine, Corse comprise (colonnes consolidées puis coordonneesXY, une paire latitude/longitude inversée étant remise dans l’ordre) ; une borne sans position plausible est écartée, donc retirée de la base au passage suivant, outre-mer compris pour l’instant (25/09/2026 : Allego Saint-Clément-de-Rivière placée dans le golfe de Guinée, Tesla Val-de-Meuse au Kazakhstan -- cette dernière retrouve sa vraie position, 16 bornes d’outre-mer retirées). Depuis le 28/09/2026, chaque position est aussi soumise au géocodeur du serveur (Photon, recherche inverse dans un rayon de 2 km, 4 requêtes en parallèle, environ 3 s pour toutes les bornes) : une borne qui ne tombe pas en France est écartée (Allego Anglet placée dans la Manche, une borne Powerdot réellement en Belgique rattachée au Pas-de-Calais). Géocodeur injoignable : contrôle sauté, toutes les bornes gardées. Site en plusieurs fiches (28/09/2026) : un réseau qui republie ses bornes sous de nouveaux identifiants laisse l’ancienne déclaration dans le fichier (Allego : FRALLPEVCARS… de mars puis FREVCP… de septembre ; Ionity : fiches de 2023 à une prise sous les sites de 2026). Même opérateur et (a) à moins de 20 m, ou (b) jusqu’à 500 m avec au moins la moitié des prises en commun, sur des numéros d’au moins 6 chiffres (les numéros courts coïncident entre bornes différentes). Prises en commun (sans le préfixe : FRALLEGO9000571 = FREVCE9000571) ou fiche plus vieille de 6 mois : on ne garde que la déclaration la plus récente, sans additionner les prises. Prises différentes et même période (deux bornes Lidl d’un parking) : une seule fiche, prises additionnées. Les tarifs Electra régénérés chaque jour suivent la fiche gardée, les exclusions des utilisateurs aussi ; deux fiches portant chacune un tarif saisi à la main restent séparées. Sur la prod : environ 750 fiches en trop retirées (Electra, Allego, TotalEnergies, Ionity…). Les tarifs sont toujours au plus quatre lignes, chacune au prix de l'heure d'arrivée : le tarif retenu par le calcul (quel qu'il soit), la carte bancaire, l'appli de l'opérateur de la borne sans abonnement (seulement si elle est moins chère que la carte bancaire, ou toujours quand la borne n'a pas de prix par carte, comme Tesla) et le prix avec l'abonnement de cet opérateur (le moins cher) quand le calcul ne l'a pas retenu ; les abonnements d'un autre réseau ne sont affichés que s'ils sont le tarif retenu. Une ligne dont le prix varie selon l'heure (au moins deux plages de prix différents) porte « variable selon l'horaire » : un clic déplie sa grille horaire (une seule dépliée à la fois), montant de la session par plage. Heure d'arrivée connue : la plage cochée est celle de l'arrivée ; inconnue : la plus chère, comme dans le calcul. Sur ordinateur, la fiche reste ouverte quand on y amène la souris et pendant un déplacement de la carte (elle suit alors sa borne) ; elle se ferme quand la souris la quitte ou repart ailleurs sur la carte, et à chaque zoom ou dézoom (même règle que l'onglet Bornes). Sur la carte du trajet, une horloge dans la bulle de prix d'un arrêt signale que son tarif retenu varie selon l'heure (champ hourly des arrêts de /api/plan et /api/trip-plan, même règle que l'onglet Bornes : tariffs.option_varies_by_hour, au moins deux plages horaires à des prix différents pour ce moyen de paiement / abonnement ; calculé après la recherche sur les seuls arrêts retenus, lignes de tarif déjà en cache).
IECharge sans disponibilité réelle (18/10/2026, demande de Jérémy). Une borne IECharge dont le flux est figé depuis plus de 24 h, ou dont moins de la moitié des points ont un état connu, est écartée des trajets exactement comme une borne hors service (live_status.UNKNOWN_EXCLUDED_OPERATORS, lue par out_of_service_station_ids()) : ces bornes sortent souvent parce qu'elles sont peu chères, et une borne en panne sans disponibilité affichée coûte une étape à l'utilisateur. Sur la base locale : 92 stations IECharge sur 159 sont écartées, 67 ont une disponibilité réelle. Elles reviennent d'elles-mêmes dès que leur flux bouge de nouveau.
Disponibilité des prises en direct (26/09/2026). Source : la consolidation nationale des données IRVE dynamiques du Point d’accès national (transport.data.gouv.fr, schéma etalab/schema-irve-dynamique, un point de charge par ligne : état en service / hors service, libre / occupé, état de chaque type de prise). Le serveur la relit toutes les 2 minutes (backend/app/live_status.py, environ 1,6 Mo compressé, calcul d’environ 0,5 s dans un fil d’arrière-plan) et la croise avec les points de charge de chaque borne, enregistrés par l’import IRVE (colonne stations.pdc_types). Affichée dans la fiche de borne (onglet Bornes et détail d’un arrêt) : « libres / total » par type de prise, prises et borne hors service, heure de la donnée avec un point vert qui pulse (maquette pack_disponibilite_D2). Règles, pour ne jamais afficher un chiffre faux : seulement les opérateurs dont le flux est vraiment en direct (mesuré le 26/09/2026 : Tesla, Ionity, Fastned, Izivia, Powerdot, Lidl, TotalEnergies, Engie Vianeo, Driveco ; Electra, Allego, Atlante, Freshmile et Leclerc publient avec des heures ou des mois de retard, rien n’est affiché pour eux) ; un opérateur dont le flux ne bouge plus depuis 1 h est ignoré automatiquement ; au moins la moitié des points de la borne doivent avoir un état connu, et le compte ne porte que sur ceux-là (un point absent du flux est le plus souvent un ancien identifiant resté dans le fichier des bornes) ; plus rien n’est affiché si la dernière relecture a plus de 10 minutes. La fiche relit la disponibilité chaque minute tant qu’elle reste ouverte. Effet sur le calcul : une borne dont tous les points sont connus et hors service est écartée des trajets (au moment de lister les bornes le long de l’itinéraire, api/_shared.py::candidates_for_route ; vérification en mémoire, sans effet mesurable sur la durée de recherche) ; rien n’est écarté quand la donnée a plus de 10 minutes. Depuis le 15/10/2026 (retour d’un utilisateur : Lille → Nice avec IONITY Power, arrêts proposés sur des bornes IECharge affichées hors service à Reims et à Misérieux) : une borne est aussi écartée quand toutes ses prises rapides (CCS, CHAdeMO) d’état connu sont hors service, même si une prise lente (Type 2) marche encore — l’affichage de la fiche ne change pas. Test local du 15/10 : 4 987 bornes suivies, 58 entièrement hors service, 299 écartées des trajets (241 de plus, par exemple des Lidl aux deux CCS en panne). Bornes muettes (16/10/2026, Jérémy : « écarter après 30 jours ») : une borne dont aucun point n’a changé d’état dans le flux depuis 30 jours (live_status.MUTE_EXCLUDE_S), alors que son opérateur publie pour ses autres bornes, est écartée des trajets (rien n’est affiché sur sa fiche). Cas d’origine : IECharge Reims B, muette depuis le 09/08, et Misérieux, depuis le 15/09, toujours « en service » dans le flux mais hors d’usage chez l’opérateur ; proposées sur Lille → Nice. Seulement pour les opérateurs suivis en direct ; test local du 16/10 : 106 bornes de plus écartées. Ajout du 28/09/2026 : Allego suivi par le flux national borne par borne (seulement les bornes dont un point a changé d’état depuis moins de 24 h : 250 sur 402 le 28/09, les autres étant figées depuis des semaines, PER_STATION_OPERATORS) ; Electra lu à la demande dans l’API publique de leur carte (backend/app/electra_live.py) : une requête pour la station à l’ouverture de sa fiche, gardée 1 min, car leur flux national arrive par paquets avec plusieurs heures de retard ; Electra n’intervient pas dans le calcul des trajets (bornes hors service non écartées). Atlante : rien de fiable en direct à cette date (flux national figé, fichier de leur carte vieux d’environ 10 h) ; suivi depuis le 06/10/2026, voir plus bas. Ajout du 29/09/2026 : R3 suivi de la même façon qu’Allego, borne par borne (mesuré le 29/09 : dernier changement d’état il y a 1 min, 150 stations sur 165 avec un changement depuis moins de 24 h, 4 figées, 11 avec moins de la moitié de leurs points dans le flux). Ajout du 06/10/2026 : Atlante suivi de la même façon, borne par borne (mesuré le 06/10 : dernier changement d’état il y a moins d’une minute, 167 stations françaises sur 183 avec un changement depuis moins de 24 h, dont 100 depuis moins d’1 h ; 12 figées depuis plus de 7 jours, non affichées). Points publiés en double : le flux national contient environ 10 000 points sur deux lignes (deux sources : Atlante, Electra et d’autres), parfois avec des états différents ; Kargit garde la ligne au changement d’état le plus récent (avant : la dernière du fichier, au hasard). Un horodatage sans fuseau horaire est lu à l’heure de Paris (avant : en UTC, ce qui le rajeunissait de 2 h l’été). Pays voisins (29/09/2026, backend/app/eu_live.py), mêmes règles d’affichage : Suisse, flux officiel OFEN (data.geo.admin.ch, OICP), relu toutes les 2 min ; sans horodatage par point, Kargit note lui-même les changements d’état, et un opérateur n’est suivi que si l’un de ses points a changé depuis moins d’1 h : 503 stations sur 637 le 29/09, 7 min après le démarrage (GOFAST et Tesla absents ou figés) ; Pays-Bas, flux officiel NDW (OCPI, 17 Mo compressés), relu toutes les 4 min dans un processus à part (eu_live_nl.py : le décodage prend environ 5 s et 1 Go, il bloquerait sinon les recherches) ; une station n’est suivie que si l’un de ses points a changé depuis moins de 24 h (Tesla, E.ON, Valk… figés, écartés d’eux-mêmes) : 691 stations sur 791 le 29/09 ; Portugal, flux officiel DATEX II de MOBI.E (point d’accès national, fiche n° 149, libre accès, republié toutes les 5 min, ~36 Mo de XML) : un HEAD par minute, relu seulement quand son ETag change, dans un processus à part (eu_live_pt.py, ~23 s) ; fichier tronqué rejeté (balise de fin absente ou moins de 12 000 points), identifiant de point présent sur plusieurs sites ignoré ; même règle que la Suisse (changements notés par Kargit, opérateur suivi seulement si l’un de ses points a changé depuis moins d’1 h) : 2 077 stations sur 2 229 lors du test du 29/09 ; Royaume-Uni, flux « open data » imposés par les Public Charge Point Regulations 2023, seuls ceux ouverts sans inscription : MFG (OCPI, 3,6 Mo, relu toutes les 5 min, au-delà il répond 429) : 577 stations sur 591 ; « INOPERATIVE » chez MFG = prise sœur occupée (puissance partagée), comptée occupée ; GeniePoint publie aussi mais son flux refuse l’adresse du serveur (403, Cloudflare) ; InstaVolt, Osprey, Shell, IONITY, GRIDSERVE, Fastned : formulaire ou contrat exigé, rien; Italie (sources officielles sans compte ; ni la plateforme nationale PUN ni le compte « invité » d’Enel) : IONITY par le serveur de l’appli IONITY Direct (une fiche par station, état de chaque prise, 42 appels toutes les 5 min) et Free To X par sa page carte officielle (un appel toutes les 5 min, servie par un cache d’environ 5 min, retiré de l’heure affichée) : même règle que la Suisse (changements notés par Kargit, opérateur suivi seulement si l’une de ses prises a changé depuis moins d’1 h), 42 et 109 stations sur 42 et 109 ; Duferco à la demande, à l’ouverture de la fiche (jeton anonyme de sa carte publique, gardé 2 min), codes 1 et 9 libres, 2 et 5 occupés, 3, 4, 7 et 8 hors service ; Ewiva, Tesla, Fastned : aucun direct public ; Allego : données figées (jamais une prise occupée), non affichées; Electra dans tous les pays par leur API (BE, CH, DE, ES, LU, NL). Mesure du 29/09 (deux relevés à 3 min d’intervalle) : 166 changements d’état en Suisse, 1 014 aux Pays-Bas. Allemagne, Belgique (hors Electra), Espagne : pas de flux ouvert sans inscription (Mobilithek, clés à demander). Le flux national français ne sert plus qu’aux bornes françaises.
Disponibilité en direct des pays de la campagne « toute l’Europe » (15/10/2026, backend/app/eu_live_europe.py, règles dans eu_live.py). Mêmes règles d’affichage que les pays voisins (au moins la moitié des points de la station avec un état connu, rien au-delà de 10 min, jamais un état figé). Sources officielles sans compte ; flux de fond (un appel pour tout un réseau ou tout un pays, dans le même fil que les pays voisins, intervalles de 2 à 5 min décalés au démarrage ; les deux plus lourds à décoder dans un processus à part) : Grèce, registre national ΜΥΦΑΗ (tous opérateurs, fichier relu seulement si son ETag a changé ; rien s’il n’est plus republié depuis 30 min) ; Bulgarie, API publique de Fines Charging (toutes les 3 min, comme elle le demande) ; Pologne, registre EIPA (libre / occupé seulement, « en panne » compté occupé) pour les stations du registre et Shell, bp pulse, TotalEnergies rattachés à leur point EIPA — avec l’accès officiel « reader » obtenu le 15/10/2026 (jeton EIPA_READER_TOKEN dans l’environnement du serveur), fichier officiel dynamic relu toutes les 2 min (au plus 200 fois par heure pour une limite de 240, rien avant l’heure suivante après un refus ; 8,7 Mo non compressés, environ 6 Go par jour ; décodé dans un processus à part à priorité basse, environ 1 s de calcul par relevé) : « hors service » distingué et heure de chaque point, donc règle des sources horodatées (test local : 1 033 stations suivies sur 1 432 dès le premier relevé, contre 742 avec la carte publique) ; sans jeton, carte publique comme avant ; les prix des stations polonaises du registre, de Lidl et de Kaufland sont relus chaque nuit par le même accès (source eipa_pl de scripts/update_api_tariffs.py : un téléchargement par fichier, bornes suspendues et stations fermées comptées absentes) ; Finlande, registre Fintraffic (stations du registre, Lidl, Neste) ; Lituanie, registre Via Lietuva ; Danemark, Suède, Norvège, flux AFIR de Spirii (13 exploitants, plus Shell Recharge et TotalEnergies au Danemark) ; Tchéquie et Slovaquie, carte publique de PRE (SMATRICS : PRE, ČEZ, ZSE Drive, rattachés par coordonnées) ; Slovaquie, carte GreenWay ; Slovénie, registre PROMETEJ ; Chypre, carte pcharge ; cartes Driivz d’ESB ecars et d’Applegreen (Irlande), de Recharge (NO, SE, FI, DK) et de MOL Plugee (RO, HU, HR, SK, CZ) : un point = une borne ; Liechtenstein, registre suisse de l’OFEN (même lecture que la Suisse) ; Belgique, EnergyVision / My EV Platform (26/10/2026, préparé en local, backend/app/eu_live_energyvision.py) : flux DATEX II d’états avec clé (variable ENERGYVISION_DATEX_KEY du serveur, gratuite, valable 6 mois : un message clair apparaît dans le journal au premier refus 401/403 et 30 jours avant l’expiration, date lue dans l’en-tête X-Api-Key-Expires-At), un appel toutes les 2 min (cache du fournisseur : 60 s), usage limité à l’affichage dans Kargit ; l’heure fournie par point est celle du dernier message reçu de la borne (pas d’un changement d’état) : un point muet depuis plus de 6 h compte « inconnu », rien si la publication a plus de 10 min ou si le flux est tronqué ; rattachement par identifiant de point, ou par position pour les stations wallonnes ; Norvège et Suède, NOBIL Real-time (27/10/2026, préparé en local, backend/app/eu_live_nobil.py) : produit gratuit « NOBIL - Real-time » de data.enova.no, avec clé d’abonnement (variable NOBIL_REALTIME_KEY du serveur ; sans clé ou clé refusée, rien n’est publié et le journal le dit une fois par heure). Le service est une connexion WebSocket permanente (Azure Web PubSub) : on demande un jeton (valable 60 s pour se connecter) puis un fil unique du processus principal garde la connexion ouverte (nouvelle connexion automatique avec attente croissante, ou après 3 min de silence) et reçoit, environ 4 par seconde, un message par changement d’état d’une prise (disponible, en charge, bloquée, hors service, inconnu) ; il n’y a ni instantané initial ni battement de cœur, donc l’état d’une prise n’est connu qu’après son premier changement et la couverture grandit avec le temps (test local : 201 stations sur 2 813 suivables après 1 h). L’état est gardé en mémoire et enregistré toutes les 5 min : relu après un redémarrage de moins de 15 min, vidé après une coupure plus longue (des changements ont pu être manqués). Mêmes règles d’affichage que les autres flux horodatés : au moins la moitié des points de la station avec un état connu, un état de plus de 24 h compte inconnu, rien si aucun message depuis 10 min, aucun appel réseau pendant une recherche de trajet. Rattachement par identifiant EVSE ; E.ON Drive et Allego (sans EVSE en base) par position à 50 m au plus d’une fiche NOBIL du même exploitant ; Circle K, Mer, IONITY, Recharge et Spirii gardent leur propre flux. À la demande, à l’ouverture de la fiche (une requête par station, gardée 1 min, 3 min pour EPBiH ; au plus 2 requêtes à la fois par source) : EVPoint (Bulgarie), Elektroprivreda BiH, Renovatio e-charge (Roumanie), MVM Mobiliti avec OMV et TEA (Hongrie), Petrol (Croatie), Circle K, Mer (Norvège, Suède), Shell Recharge (Tchéquie, Slovaquie, Hongrie, Pologne hors EIPA : rien si le Stationsfinder n’a pas été mis à jour depuis 1 h) et IONITY dans les 14 nouveaux pays où il est présent. Fraîcheur : source avec l’heure du dernier changement de chaque point (Grèce, Fines, Finlande, Lituanie, Spirii, SMATRICS, Slovénie, Mobiliti) → réseau sans aucun changement depuis 1 h = rien, station sans changement depuis 24 h = rien ; source sans horodatage (Driivz, pcharge, GreenWay, EIPA) → Kargit note lui-même les changements (gardés d’un redémarrage à l’autre), réseau sans changement vu depuis 1 h = rien ; pour EIPA en plus station sans changement vu depuis 24 h = rien (un « occupé » qui est peut-être une panne n’est jamais affiché longtemps). Aucun appel réseau pendant une recherche de trajet. Test du 15/10 (quatre relevés à 5 min) : environ 4 200 stations suivies par les flux de fond (Grèce 464, Bulgarie 237, Finlande 1 126, Lituanie 405, Tchéquie 560, Slovaquie 306, Danemark 171, Norvège 288, Suède 192, Irlande 181, Slovénie 91, Pologne 88 au bout de 15 min — la couverture grandit au fil des changements vus —, Hongrie, Roumanie, Croatie et Chypre par MOL Plugee et pcharge) ; volume d’environ 0,9 Go par jour, dont un tiers pour Spirii. Non branchés : Wink (Bulgarie : aucun changement vu, pas d’horodatage), Allego (données figées), HEP ELEN, Eldrive, EZO, Weev, InstaVolt, Neste hors Finlande, Tesla.
Superchargers Tesla, API officielle (27/10/2026, backend/app/eu_live_tesla.py, scripts/etranger/tesla_ocpi_collecte.py). Kargit est le « eMSP » d'une connexion OCPI 2.2.1 avec Tesla (poignée de main faite le 27/10/2026 ; le portail ne propose que le périmètre Royaume-Uni, donc seuls les sites GB reviennent : 191 sites lus, 185 rapides retenus). Disponibilité : l'état de chaque prise (AVAILABLE, CHARGING, BLOCKED, RESERVED ; OUTOFORDER et INOPERATIVE = hors service) est relu toutes les 120 s par une lecture différentielle (date_from), dans un fil à part du backend, avec les règles de fraîcheur des autres flux horodatés (réseau sans mise à jour depuis 1 h = rien, station sans mise à jour depuis 24 h = rien). Prix : le registre reçoit les tarifs de Tesla hors TVA tels que donnés ; aucun prix n'est écrit dans les tarifs tant que Tesla ne précise pas la TVA. Aucun appel réseau pendant une recherche de trajet. France et reste de l'UE : relevé manuel (Chrome) inchangé ; l'étiquette de ces prix est « relevé du site Tesla (tesla.com/findus, lu via navigateur) » (corrigée le 28/10/2026, elle disait « API officielle Tesla »).
Superchargers Tesla de Finlande (28/10/2026, scripts/etranger/tesla_afir_fi.py, scripts/etranger/prix_afir_fi.py). Le point d'accès national finlandais (Fintraffic / Digitraffic, sans compte) publie les 49 Superchargers du pays, absents jusque-là de la base. Stations : importées chaque nuit dans la zone de travail (source fi_tesla_afir seulement, les autres stations finlandaises ne sont jamais touchées) puis publiées par la mise à jour de nuit des bornes rapides. Prix : lus chaque nuit par la chaîne des prix (afir_fi, paiement par l'appli Tesla) ; le registre les donne hors TVA (taxIncluded = NO), la TVA finlandaise de 25,5 % est ajoutée ; plages horaires conservées (ex. 0,38 € HT, 0,44 € HT de 16 h à 20 h) ; les frais de congestion (30 €/h au-delà de 5 min quand le site est saturé) ne sont pas modélisés. Disponibilité en direct : état de chaque prise relu toutes les 3 min dans le même registre (/locations/statuses/all), avec les règles de fraîcheur des autres flux horodatés.
Superchargers Tesla de toute l'Europe (28/10/2026 ; scripts/etranger/tesla_findus_sites.py, tesla_stations_ajout.py, tesla_releve_vers_base.py). La liste complète de tesla.com/findus (1 762 sites ouverts, tous pays) et le détail de chacun (nom, adresse, pays, prises, puissance, grilles de prix) sont lus dans un navigateur, comme le relevé de l'onglet « Prix Tesla » (Akamai bloque tout serveur). Les sites qu'aucune station Tesla de la base n'approche à moins de 400 m sont ajoutés (CCS, puissance maximale du site ; sites de démonstration, non publics ou sans grille écartés ; Turquie et Maroc hors périmètre), en ajout seulement : rien n'est remplacé. Les prix reprennent la règle de la France (app/tesla_prices.py : appli Tesla seulement, prix sans abonnement et membres, jour le plus cher par heure, sites réservés aux Tesla en sub_tesla_reserve, monnaies converties au taux BCE du jour, contrôle 0,10-1,50 €/kWh après conversion ; COUNTRIES couvre désormais tous les pays où Tesla a des Superchargers sauf l'Autriche et la Finlande, dont les prix viennent d'E-Control et du point d'accès AFIR ; plafond de prix en monnaie locale relevé à 1 000 pour les forints et les couronnes islandaises). Pas de taux BCE pour le dinar serbe : stations sans prix. Aucune mise à jour automatique : le relevé se refait à la main (favori de l'admin, ou lecture complète comme ici).
Formats par langue (29/10/2026 ; docs/i18n/FORMATS.md, frontend/assets/i18n.js, docs/i18n/_nombres.json). Les calculs restent en euros, en km et en minutes ; seul l'affichage change. Heure : 24 h dans toutes les langues (« 14:30 », « 14.30 » en danois et en finnois). Durée : « 2 h 05 » en français, « 2 hr 5 min », « 2 Std. 5 Min. », « 2 u 5 min », « 2 tim 5 min », « 2 t 5 min » ou « 2 godz. 5 min » selon la langue. Vitesse : km/h, km/u (néerlandais), km/t (danois, norvégien), mph au Royaume-Uni. Date, pourcentage (« 80 % » ou « 80% ») et consommation d'essence (mpg au Royaume-Uni) suivent le pays. Test : node scripts/tests/i18n_nombres.mjs et i18n_formats.mjs (lancés par la mise en production).
Textes fabriqués par le serveur (29/10/2026 ; frontend/i18n/_vehicules.json, frontend/i18n/_serveur.json, backend/app/sub_names_i18n.json, backend/app/sub_names.py, scripts/tests/i18n_serveur.py). L'API renvoie toujours les noms et textes d'origine (français) : vehicle_id, noms stockés et calcul ne changent jamais. Les mots descriptifs des noms de véhicules (« propulsion », « 4 roues motrices », « depuis 2024 », « 7 places », « Autonomie étendue »…) et des noms d'abonnements et de cartes (« Freshmile (carte) », « Charge Pass (badge gratuit) »…), ainsi que les messages d'erreur et raisons d'impossibilité de l'API, sont traduits dans les 11 langues par les mêmes dictionnaires que le reste du site et des applis (modèles ajoutés par scripts/i18n_build.py et scripts/i18n_apps.py ; les noms de modèles constructeur restent tels quels). Les pages SEO traduisent les noms d'abonnements côté serveur (table en mémoire, résultat gardé : aucun coût sur les recherches de trajets, qui ne sont pas touchées). La page « connexion annulée » de Google / Apple / Facebook suit la langue du site de départ. Contrôle (français résiduel dans les courriels, la désinscription, les textes de chargement, l'API, les pages SEO et les dictionnaires) : docker exec ev_backend python scripts/tests/i18n_serveur.py, puis docker run … python scripts/tests/i18n_serveur.py dico (voir l'en-tête du script).
Badges et cartes des bornes lentes hors France (29/10/2026 ; scripts/lentes/estimer_cartes_fr.py, scripts/lentes/badges_etranger.json). Le générateur des cartes appliquait ses règles aux seules bornes françaises ; il les étend maintenant pays par pays, uniquement quand l'émetteur publie une règle chiffrée. Même règle, plusieurs pays : Chargemap Pass (+10 %, BE DE CH IT ES GB), Charge Now Urban (prix de la borne + frais par session de la grille officielle du pays : 0,35 € en zone euro, 0,35 £, 0,38 CHF, 2,70 DKK, 4,12 SEK, 4,12 NOK, 1,53 PLN), Shell Recharge (+0,35 £ au Royaume-Uni seulement : ailleurs Shell publie des paliers fixes sans dire quel réseau est dans quel palier). Offres propres à un pays : Vattenfall InCharge (NL, +0,35 €/session hors réseau InCharge), Road Flex (NL BE, +0,31 €/session + 0,024 €/kWh hors réseau E-Flux), ANWB Laadpas (NL, +0,89 €/session), Greenchoice (NL, +0,40 €/session), Tap Electric pay-as-you-go (BE GB, +10 %). Prix fixes par réseau publiés par l'émetteur : Eneco eMobility (NL : Allego 0,63, autres 0,54 ; BE : Allego 0,51, autres 0,59 €/kWh, + 0,39 € par session ; réseau Eneco exclu), lignes non estimées (sans ≈, confiance 0,8). Les montants sont dans la devise du pays de la borne (jamais convertis, comme les tarifs directs). Les prix destinés aux particuliers sont considérés TTC (décision du 28/10/2026), noté sur chaque ligne ; une offre dont la page dit « HT » n'est pas reprise. Écartés : offres sans règle chiffrée (Zapmap, Elton, Plugsurfing, Monta…), prix « à partir de », abonnements (traités à part). Offres réservées aux membres : proposées seulement si l'utilisateur déclare posséder le badge. Autres prix fixes par réseau (lignes non estimées, sans ≈) : Enel On Your Way en Italie (grille officielle du 01/10/2026, courant alternatif jusqu'à 43 kW, TTC : A2A 0,72, ACEA 0,69, Atlante 0,67, ChargePoint 0,69, Duferco 0,80, Electrip 0,69, EMOBITALY 0,75, Hera 0,75, Iplanet 0,65, Neogy 0,69, Plenitude 0,69, Repower 0,69, R-ev 0,65, Route220 0,67 ; réseaux Enel X Way et Ewiva exclus ; Virta 1,36 et Go Electric non repris : chiffre atypique ou unité douteuse), Petrol OneCharge en Slovénie (Elektro Ljubljana 0,35 + 1,00 € par session) et en Croatie (HEP-ELEN 0,56), et le Charge Pass Basic de Mobilize en Allemagne (0,56 €/kWh sur tous les réseaux en courant alternatif, hors réseaux Plug Inn et Mobilize). Relus dans le navigateur le 29/10/2026 puis ajoutés : MAINGAU Autostrom en Allemagne (0,52 chez Berliner Stadtwerke, JET, Kaufland, Ladeverbund+, Lidl, MAINGAU, TankE, Vattenfall, EDEKA et Hamburger Energiewerke ; 0,82 chez Allego, E.ON, EnBW, EWE Go, Fastned, LichtBlick et Vaylens ; 0,72 chez tous les autres ; prix des nouveaux clients, TVA 19 % incluse), ÖAMTC ePower en Autriche (0,44 réseau ÖAMTC, 0,54 chez les partenaires nommés, 0,64 chez E.ON et MER en Allemagne) et Swisscharge en Suisse (CHF : MOVE 0,57, AVIA VOLT 0,50, Shell 0,86, eCarUp 0,81, Agrola 0,49, Migrol 0,48). Écartés après relecture : EWE Go (« partenaires » non définis), V-Go (prix qui change au 01/02/2027, surtout du rapide), Energie Steiermark (minimum mensuel de 3,90 € à partir de 2027), Q8 Luxembourg (frais de 0,35 € non rattachés au Luxembourg). Prix fixes sur les bornes sans prix de base (29/10/2026, décision de Jérémy : « tout le temps, pour tous les pays, France comprise »). Quand la règle d'une offre est un prix fixe publié par l'émetteur (grille par réseau, indépendante du prix de la borne : Eneco, Enel On Your Way, Petrol OneCharge, MAINGAU, ÖAMTC ePower, Swisscharge, Charge Pass Basic de Mobilize en Allemagne), la ligne est aussi écrite pour les bornes du réseau concerné qui n'ont aucun prix de base (ni carte bancaire, ni appli, ni itinérance) : une seule ligne par borne et par offre, valable toute la journée, sans frais de stationnement ni plafond (inconnus), non estimée (sans ≈, confiance 0,8). Un réseau absent de la grille et sans prix par défaut ne reçoit rien. Aucun prix de base n'est jamais supposé : les offres dont le prix dépend de celui de la borne (« × 1,10 », « -9 % », frais ou remise sur le prix de la borne, constructeurs, Chargemap Pass, Shell Recharge…) restent limitées aux bornes qui ont un prix de base. Aucune offre française n'a aujourd'hui de prix fixe publié : le moteur les prendrait en France dès qu'une grille « FR » est ajoutée au fichier. Une borne dont les seules lignes sont de tels prix de badge n'a pas de tarif connu tant que l'utilisateur ne déclare pas le badge : elle reste « tarif non communiqué » et n'est pas comptée comme ayant un tarif (carte, opérateurs, récapitulatif des pays) ; avec le badge déclaré, elle affiche le prix du badge (comportement des badges inchangé : l'offre n'est visible que si l'utilisateur la déclare). Les lignes sont régénérées chaque nuit avec les autres estimations (source estimation:…).
Onglet « Récapitulatif » des bornes (29/10/2026, demande de Jérémy ; scripts/stats/recap_bornes.py, backend/app/api/admin_recap.py, table bornes_recap). Une photo des chiffres des bornes, rapides puis lentes, pays par pays, enregistrée à chaque fois que Jérémy demande les statistiques des bornes (docker exec kargit_backend python scripts/stats/recap_bornes.py --apply) ; l’onglet affiche la dernière photo. Colonnes : total du pays (seulement si une source officielle le donne, scripts/stats/totaux_pays.json ; vide tant qu’aucun registre national n’est chargé), bornes dans Kargit (tables stations et slow_stations), avec prix (une ligne de tarif en vigueur aujourd’hui, hors exemples, propre à la borne ou générique de son opérateur dans son pays, comme la carte ; pour les lentes, une ligne sans abonnement : carte bancaire ou appli), sans prix, prix à jour (au moins une de ces lignes est tenue à jour automatiquement — API de l’opérateur, flux officiel, relecture de nuit, sources reconnues par SOURCE_AUTO_FAST / SOURCE_AUTO_SLOW — et vérifiée depuis moins de 3 jours, ou est un relevé Tesla fait par Jérémy depuis moins de 14 jours, comme le voyant Tesla ; les grilles lues à la main ne comptent pas, même réappliquées chaque nuit), améliorations restantes (texte tenu à la main dans scripts/stats/ameliorations_bornes.json, plus les trois opérateurs qui ont le plus de bornes sans prix, calculés) et évolution depuis la photo précédente (écart du nombre de bornes, de bornes avec prix et de prix à jour). Aucun effet sur le calcul ni sur la carte.
Prix Electroverse des bornes lentes de France (28/10/2026 ; scripts/lentes/estimer_cartes_fr.py, scripts/lentes/electroverse_releves.json). Avant : le prix « Electroverse (carte Electrocard / appli) » d'une borne lente était supposé égal à son prix direct. Relevé dans l'appli Electroverse (sans compte, bornes tirées au hasard, lectures de 14 h à 18 h) : 118 bornes lentes sur 44 réseaux (deux passages le 28/10/2026), le prix du kWh d'Electroverse est égal au prix direct à ±1,5 % pour 56 d'entre elles seulement (écart médian 0 %, de −46 % à +83 %). Le générateur applique donc le relevé à chaque régénération (une ligne : source estimation:slow_est_octopus_electroverse) : borne relevée = prix lu (énergie, et frais de connexion, prix au temps ou attente quand l'appli les affiche), exact (sans ≈, confiance 0,95), seulement sur le créneau valable à l'heure de la lecture (le créneau de nuit reste estimé) ; réseau dont au moins 2 bornes relevées ont le même prix Electroverse, différent du prix direct (Power Dot, Pass pass, ALDI, Easy Charge, e-charge50, Iléwat, SMOYS, Qovoltis, Enerstock, Yusco, Qwello, SIGEIF ; eborn, Freshmile et E-Totem quand 75 % des bornes relevées partagent un prix, sur au moins 4) : ce prix du kWh sur les autres bornes du réseau, reste affiché avec ≈ (confiance 0,8, ou 0,65 pour la règle majoritaire), les frais ne sont pas repris ; réseau où Electroverse égale le prix direct sur toutes les bornes relevées (Mobive, Lidl, Réveo, Belib', Indigo, Izivia Impact, Métropolis, Driveco, SDEC, SYDELA, 61mobility, Seymaborne…) : prix direct, preuve notée (confiance 0,9) ; réseaux variables d'une borne à l'autre (Ouest Charge, Stations-e, Ecocharge77, Alizé, EVzen, R3 SPV), ou non échantillonnés : prix direct, hypothèse d'origine, ≈. Aucune API publique : le relevé est manuel et ces prix peuvent changer (Electroverse annonce des tarifs dynamiques) ; un accès aux données a été demandé à Electroverse le 28/10/2026. Les tarifs de nuit et de week-end n'ont pas été relevés. Un écart constant sur tout un réseau peut aussi venir d'un prix direct de Kargit faux (ALDI, Easy Charge, Qwello, Freshmile à vérifier chez l'exploitant). Hors France (relevé du 29/10/2026, 37 bornes en Allemagne, Pays-Bas, Espagne, Italie et Royaume-Uni, scripts/lentes/electroverse_releves_etranger.json) : même méthode et mêmes catégories, mais une ligne Electroverse n'est écrite que là où un relevé la prouve (borne relevée, réseau à prix fixe ou égal au direct sur au moins 2 bornes) ; les réseaux non relevés n'ont pas de ligne Electroverse (pas de prix supposé égal au direct). Prix fixes différents du direct : EnBW 0,70, Berliner Stadtwerke KommunalPartner 0,61, HEnW-Mobil 0,40, Lidl 0,48 (Allemagne) ; Wenea 0,41, Qwello 0,33 (Espagne) ; Neogy 0,67, Enel X Way 0,52, Plenitude 0,52 (Italie) ; ChargePlace Scotland 0,55 £ (Royaume-Uni) ; égal au direct : Allego, Vattenfall InCharge, EQUANS, 50five (Pays-Bas), ACCIONA (Espagne), Duferco (Italie), Evolt (Royaume-Uni). Seul le prix du kWh est repris (temps, attente et frais de connexion non comparés), lectures du matin (8 h à 9 h) ou du soir, devise du pays, confiance 0,65.
Fusion des opérateurs des bornes lentes de tous les pays (28/10/2026 ; scripts/lentes/operateurs_fr_fusion.py pour la France, scripts/lentes/operateurs_etranger_fusion.py hors France). Le filtre « opérateur » de la carte des bornes lentes liste le nom d'exploitant du flux de chaque pays : un même exploitant écrit de plusieurs façons y apparaissait plusieurs fois. Les graphies du MÊME exploitant sont réunies pays par pays (même marque, même société à une forme juridique près, établissements d'une même société) : France, 21 groupes (Atlante, ALDI, Driveco, TotalEnergies, Sorégies, EVzen, Qwello, EV MAP, GreenFlux…, 4 344 stations) ; hors France, Qwello, Wirelane, Mercedes-Benz AG, Wenea, CityCharging, Vattenfall InCharge, Chargewell, StellaPower, Luminus, APCOA, etc. (8 127 stations). Jamais deux sociétés distinctes ni deux marques voisines (liste des refus dans docs/bornes_lentes/FUSION_OPERATEURS_LENTES_1028.md et FUSION_OPERATEURS_ETRANGER_1028.md). La migration met à jour le nom ET la clé de regroupement des fiches d'un même site ; l'importer applique la même règle à chaque import, donc la fusion tient. Aucun tarif lent n'est rattaché à un opérateur par son nom (sauf les grilles par « aménageur exact » de Qwello et Wenea, qui listent aussi le nouveau nom) : les prix ne changent pas.
Prix manquants, 2e passe, lots Nord, Est et Tesla (28/10/2026, base locale seulement : paquets de production prêts, rien joué ; scripts/lentes/prix_se_pages.py, surveiller_grilles_no.py, scripts/etranger/prix_powercharge_no.py, prix_powerdot_pl_lentes.py, tesla_bands_vers_tarifs.py). Suède et Norvège : grilles officielles en SEK / NOK (OKQ8, Eviny, Cheap Charge, St1, Hedin Supercharge, Qwello, Parkering Göteborg ; Kople, Eviny, Fastcharge, Powercharge, Supercharge), converties en euros au taux BCE du jour ; la TVA est prouvée par écrit pour Qwello et Eviny seulement, pour les autres le prix consommateur affiché est tenu pour TTC (loi suédoise de l'information sur les prix, loi norvégienne prisopplysningsforskriften § 3 ; la source et le motif sont notés dans chaque ligne). Les pages tarifaires sont relues chaque nuit (hash du bloc de prix : un écart arrête le chargement). Aucun prix unique pour ChargeNode, Monta, Virta, Voltiva, Aimo, Waybler (le propriétaire de la borne fixe le prix) : rien n'est chargé. Tesla Norvège et Suède : relevé de tesla.com/findus (comme la France, NOK / SEK convertis, jour le plus cher, tranches horaires, sans abonnement et membres ; COUNTRIES de app/tesla_prices.py contient NO et SE, plafond de prix en monnaie locale relevé à 60, contrôle 0,10-1,50 EUR/kWh après conversion). Est : grilles SI (MEGA M, Moon, Lidl), AT (Stromquelle, APCOA), PL (Qwello, LAS EV, Enefit, Eleport), CZ (Chargee), DK (région H, Verdo) ; Virta Finlande lue par l'API anonyme de la carte publique de Virta (TTC, HT et TVA 25,5 % fournis ; non documentée, sans licence écrite : non jouée en production tant que Jérémy n'a pas tranché). Pas de prix supposé : tout opérateur dont le TTC n'est pas prouvé reste sans prix.
Prix manquants, 2e passe, lot Ouest : Espagne, Portugal, Belgique, Royaume-Uni (28/10/2026, base locale seulement, scripts/lentes/appliquer_grilles_ouest.py, appliquer_prix_sud.py, prix_de_be_lu.py). Grilles officielles TTC ajoutées : Endesa X Way Espagne (PDF « Tarifas y suscripciones », vigueur 01/03/2026, « Todos los precios mostrados son con IVA (21%) incluido » ; prix e-Flex / sans inscription jour 07-23 h et nuit 23-07 h par tranche AC / DC < 100 kW / DC ≥ 100 kW ; les Canaries (codes postaux 35xxx et 38xxx, IGIC 3 %) ont leurs propres prix et Ceuta / Melilla n'ont aucun prix : ligne réservée par rattachement.codes_postaux ou codes_postaux_exclus, codes à 5 chiffres), Atlante Portugal (page tarifs, paiement ad hoc 0,60 / 0,70 / 0,75 « IVA incluída » ; les stations qui ont déjà le prix ad hoc MOBI.E ne reçoivent pas en plus la ligne de grille), Continente Plug&Charge (« Valores com IVA », frais d'inactivité 0,20 €/min après 15 min comptés depuis la fin de charge), Lidl Belgique (appli Lidl Plus, TTC prouvé par les CGV ; le tarif Lidl.be sans appli n'est pas écrit : TVA non dite), InstaVolt et Osprey Royaume-Uni pour les bornes rapides sans tarif (livres converties en euros au taux BCE du jour). Règle inchangée : une station qui a déjà un prix d'une autre source n'est jamais touchée ; un prix variable selon la borne, une fourchette, un prix « dès » ou un prix hors taxe sans preuve ne sont pas écrits (TotalEnergies et ENGIE Vianeo Belgique, Allego, Iberdrola, Repsol, EDP, Galp, char.gy, Clenergy, GeniePoint... : voir le rapport du lot Ouest).
GRIDSERVE, Royaume-Uni : disponibilité en direct par l'API Open Data (25/10/2026, backend/app/eu_live.py, GB_KEYED_FEEDS). Même API officielle que les prix (OCPI 2.2.1 imposé par les Public Charge Point Regulations 2023, clé reçue le 23/10/2026, un seul appel toutes les 5 min pour 237 sites). Mêmes règles d'affichage que les autres flux britanniques : points rapides (CCS / CHAdeMO) seulement, état de chaque prise (OUT_OF_ORDER compté hors service, REMOVED et UNKNOWN ignorés), au moins la moitié des points avec un état connu, station suivie seulement si une prise a changé d'état depuis moins de 24 h (horodatage de chaque prise publié). Les sites de l'API ont des identifiants différents de nos stations (celles du site web de GRIDSERVE) : chacun est rattaché à la station GRIDSERVE la plus proche, à moins de 150 m. Mesuré en local le 26/10/2026 : 174 stations suivies sur 177. Sans clé dans l'environnement, ce flux est simplement ignoré.
Prix selon la puissance de la borne (16/10/2026, Jérémy : Le Plein, 0,42 €/kWh jusqu'à 100 kW et 0,58 € au-delà). Le calcul des trajets prend toujours le prix de la tranche de la puissance maximale de la station (le pire cas, comme les repères de la carte). Dans la fiche d'une borne (onglet Bornes et fiche d'un arrêt), une ligne de tarif dont les tranches de puissance ont des prix différents porte « variable selon la puissance », repliée par défaut : une ligne par tranche (« Jusqu'à 100 kW », « Plus de 100 kW »…) avec son prix, celle de la borne cochée (power_bands de /api/tariffs-map/station/{id}/card, tranches lues avant le filtre de puissance). Vaut pour tous les opérateurs à prix par puissance (Le Plein, Allego, Aral, MobiSDEC…).
Fiche borne de l'onglet Bornes. Même présentation que la fiche des trajets, sans trajet donc sans « tarif retenu par le calcul » : elle utilise une session type de 30 kWh (25 min), comme les prix des repères, et affiche le prix effectif au kWh (frais de session et de temps répartis sur l'énergie), la puissance max, le nombre de prises, les connecteurs et les services à moins de 300 m. Un lieu de l'autre côté d'une autoroute ne compte pas (17/10/2026, Jérémy : toilettes « de l'autre côté alors qu'on ne peut pas traverser » ; avant, tout lieu à moins de 300 m à vol d'oiseau était retenu). Obstacles : les autoroutes et les voies rapides que OpenStreetMap dit réservées aux véhicules à moteur (motorroad=yes), hors ponts et tunnels (220 000 tronçons : France, Belgique, Luxembourg, Suisse, Pays-Bas, Allemagne, Italie, Espagne, Portugal, Royaume-Uni ; table osm_barriers, scripts/extract_osm_barriers.py puis import_osm_barriers.py) ; une 2x2 voies à carrefours ou à feux se traverse à pied et n'est jamais un obstacle. Un lieu est écarté quand le segment borne → lieu coupe un obstacle (fonction SQL amenity_reachable, migration 20261017_amenity_barriers.sql), sauf si le franchissement est à moins de 150 m d'un passage : pont ou tunnel de route, chemin, passerelle ou couloir qui croise l'autoroute, ou tronçon d'autoroute lui-même en pont ou en tunnel (table osm_barrier_crossings, scripts/extract_osm_crossings.py ; cas de l'Autogrill italien bâti en pont au-dessus de l'autoroute). Même règle partout où les lieux servent : fiche, filtre « Services » et préférence des trajets (refresh_station_amenities(), précalculé, 10 s, aucun effet sur la durée de la recherche), anneaux de la carte. En base locale : 0,2 à 5 % des lieux écartés selon le pays (France 1,8 %, Allemagne 3,2 %, Pays-Bas 5,4 %), 6 % des toilettes en France (130 sur 2 213), 14 % en Allemagne ; typiquement la borne d'une aire d'autoroute et les toilettes de l'aire du sens opposé, vérifiées sur OpenStreetMap (A10, A71, A61). Lieux en pays ENTIER, mis à jour chaque mois (21/10/2026, Jérémy) : avant, hors France, seuls les lieux à moins de 300 m des stations connues fin septembre étaient chargés (Allemagne 63 600 lieux sur 286 000, aucun lieu pour l'Autriche, la Pologne, la Tchéquie, etc. : une station nouvelle loin d'une ancienne n'avait aucun service). Désormais scripts/etranger/services_osm_pays.py (cron du serveur, 1er dimanche du mois, 0 h 10 UTC ; --mensuel) traite UN pays à la fois : il télécharge le fichier OpenStreetMap Geofabrik du pays (un téléchargement par pays et par mois, débit plafonné à 20 Mo/s, somme de contrôle MD5 vérifiée, supprimé aussitôt après), l'extrait dans un conteneur jetable (scripts/etranger/services_osm_extraire_pays.py : pyosmium, index des nœuds sur disque, 12 Go de mémoire au plus, priorité minimale ; mêmes catégories que la France car la fonction categorize() de extract_osm_amenities.py est réutilisée telle quelle ; restaurants et fast-foods, cafés, toilettes non réservées aux clients, boulangeries, supermarchés et supérettes ; même règle de barrière d'autoroute : is_barrier, is_motorway_span et is_crossing sont les fonctions des deux scripts d'extraction des autoroutes et des passages, calculés dans la même lecture du fichier) puis remplace les lignes DE CE PAYS (osm_amenities, osm_barriers, osm_barrier_crossings, colonne country) et recalcule les services des bornes de ce pays (refresh_station_amenities_pays(), migration 20261021_services_osm_pays_entiers.sql ; mêmes règles que refresh_station_amenities(), avec en plus les bornes d'un pays voisin à moins de 300 m des lieux du pays traité) dans une seule transaction : tables temporaires, remplacement, recalcul, contrôles, validation ; la moindre erreur annule tout. Garde-fous : refusé (code 3, base inchangée) si le pays n'a plus que 70 % de ses lieux, de ses autoroutes ou de ses passages ; annulé si les lieux de la France ont changé ; un lieu déjà présent sous un autre pays (même catégorie, même nom, à 1 m : les extraits se chevauchent aux frontières, l'extrait irlandais contient l'Irlande du Nord) n'est pas dédoublé. La France n'est jamais touchée. Mode simulation par défaut en ligne de commande (la transaction est annulée à la fin) ; état dans data/services_osm/_etat.json (tâche « Services autour des bornes : pays entiers » de l'onglet Tâches planifiées). Un pays que la tâche n'a pas encore traité garde ses anciens lieux. Toucher un service (restaurant, café, boulangerie, commerces, toilettes ; 17/10/2026, Jérémy) entoure sur la carte, 6 secondes, les lieux de cette catégorie pris en compte (mêmes lieux OpenStreetMap et même rayon de 300 m que le résumé, 40 au plus ; carte des trajets pour la fiche flottante, carte Bornes pour la fiche Bornes ; recadrage si un lieu est hors cadre, ou sur la partie visible au-dessus de la feuille sur téléphone) : appel à la demande /api/tariffs-map/station/{id}/amenities?category=…, gardé 1 h, sans effet sur la recherche de trajet. Les tarifs suivent la colonne « Abonnements à considérer » de droite (au départ : les abonnements déjà possédés, sans rien enregistrer sur le compte) : rien de coché = le tarif le moins cher avec abonnement (tous réseaux), l'appli de l'opérateur sans abonnement, la carte bancaire ; des abonnements cochés = une ligne par abonnement coché applicable à la borne, puis l'appli sans abonnement et la carte bancaire en repère, la moins chère étant cochée (corrigé le 17/10/2026 : un abonnement coché plus cher que l'appli sans abonnement n'est plus coché à sa place, la fiche coche la même option que la pastille de la carte ; Electra, Atlante Go coché 0,49 € contre 0,39 € sans abonnement). Un abonnement réservé à ceux qui le possèdent déjà (Plug Inn Charge Pass) n'est jamais proposé tant qu'il n'est pas coché. Appel à la demande (/api/tariffs-map/station/{id}/card?map_view=true), sans effet sur la recherche de trajet. La fiche suit sa borne quand on déplace la carte ; un zoom ou un dézoom la ferme. La carte des tarifs n'affiche que des bornes chiffrées : les opérateurs sans prix vérifiable (Freshmile, Greenspot, voir pricing_policy.py) et les bornes sans aucun tarif connu ne sont ni dessinés, ni comptés, ni proposés dans le filtre des opérateurs.
Bornes étrangères : prise, pays et doublons (14-15/10/2026). Une borne n'entre dans Kargit que si son type de prise est connu (CCS, CHAdeMO ou Type 2) : sinon rien ne permet de dire qu'elle convient au véhicule, ni que c'est de la charge rapide. Le type vient d'une source officielle : registre national, carte de l'opérateur, plateforme nationale italienne PUN, registre ChargePlace Scotland, déclaration de l'opérateur pour tout son réseau (LEAP24 et Tesla : CCS) ou, pour un modèle que le registre écossais nomme sans écrire ses prises, la fiche technique du fabricant (15/10/2026, ABB Terra 53, Circontrol Raption 50) : seules les prises présentes dans toutes les variantes du modèle sont retenues ; un modèle sans prise commune (Siemens QC45 : CCS ou CHAdeMO selon la version) ou sans fiche officielle (BP Pulse 50 kW) n'était pas publié ; décision de Jérémy du 15/10/2026 pour ces 26 bornes écossaises restantes : « considère que c'est du CCS » (completer_prises.py, étape « decision »). Une borne qui n'a qu'une prise Type 2 est de la charge en courant alternatif (43 kW au plus) : elle est écartée même si la source annonce plus de 50 kW (Powy Marsciano, « Type 2 DC 100 kW » à la PUN). Le garde-fou est appliqué à l'import (scripts/etranger/import_*.py) et à la publication (publier_prod.py : borne sans prise non exportée) ; le calcul lui-même n'a pas changé. Pays : une borne n'est rangée dans un pays que si sa position est dans ses frontières (scripts/etranger/frontieres.py, tracé simplifié de GraphHopper, 2 km de tolérance près d'une frontière) ; les anciennes boîtes latitude/longitude se recouvraient (Zagreb dans la boîte slovène, Podgorica dans la croate, Zakopane dans la slovaque) et 4 bornes étaient dans le mauvais pays. Même borne sous deux opérateurs (scripts/meme_site.py, MEME_BORNE_DEUX_OPERATEURS) : Italie Enel / Ewiva, Finlande Helen / Plugit (20 sites publiés deux fois par le registre AFIR, Plugit gardé), Autriche SYNECOTEC / da emobil ; la fiche retirée n'est pas publiée. Un site qu'un opérateur publie à la même adresse (rue et numéro) qu'une fiche du registre, à moins de 400 m, n'est plus ajouté une seconde fois (5 doublons belges retirés le 15/10).
VEHICLE_BRAND et BRAND_INCLUDED_SUBSCRIPTIONS dans tariffs.py ; même mécanisme pour le Charge Pass Basic sur les bornes Plug Inn Fast Charge (véhicules Renault, Dacia, Alpine) ; marque lue sur le véhicule du calcul, y compris dans les processus parallèles et pour la fiche borne).owned_only_subscription_ids, appliqué en entrée de /plan et /trip, aucun coût dans la boucle de recherche). La variante « sans nouvel abonnement » garde les abonnements déjà possédés (avant, elle les excluait aussi : un abonnement possédé était ignoré dès que le calcul de base en préférait un autre, non possédé).service_swaps = [{station_id, services, position_km, leg}] (/api/plan, /api/trip-plan) : la borne est retirée, et à ±30 km le long du trajet seule reste la borne la plus proche qui offre tous les services (table station_amenities, lieux à moins de 300 m ; une requête indexée, avant le regroupement par tranche) ; elle devient un arrêt imposé : la recherche (planner/graph.py et _search_native.pyx, champ CandidateStation.forced) ne peut ni la sauter ni en repartir sans y recharger (une arrivée à la borne imposée, qui doit encore y recharger, n'est jamais comparée à un passage qui vient d'y recharger : sinon « arrivé à 45 % pour 2,42 € » écartait « rechargé de 26 à 45 % pour 4,24 € » et « le moins cher » coûtait plus que « le plus rapide », corrigé le 06/10/2026) ; le reste du trajet est recalculé normalement (la recherche peut ajouter ou déplacer d'autres arrêts). Aucune borne qui convient : 422 « Aucune borne avec ces services… », le trajet affiché ne change pas. Affichée avec les bornes exclues (services entre parenthèses), gardée dans le trajet, l'adresse (ss) et les favoris ; « Réinitialiser les exclusions » l'annule. Sans l'option, recherche strictement identique (4 trajets de référence et les 432 recherches de scripts/search_parity.py comparés avant / après).arrival_hour à la place de departure_hour (/api/plan, chaque trajet de /api/trip-plan). Le serveur estime l'heure de départ d'après le tracé (durée de route + recharges estimées : énergie manquante à 60 % de la puissance max du véhicule, 8 min par arrêt ; _shared.departure_for_arrival), fait le calcul habituel avec cette heure (tarifs horaires, météo), puis recale toutes les heures (départ, arrivée aux bornes) pour arriver à l'heure demandée (arrival_shift) ; réponse : departure_hour retenu. Aucun calcul en plus : les tarifs horaires sont appliqués à l'heure estimée, en général à quelques minutes près. Un retour avec sa propre heure d'arrivée est calculé à part (pas de recherche combinée aller-retour), comme avec une heure de départ.min_charge_points, les bornes qui ont moins de points de charge sont écartées), paiement, services à privilégier, cashback, météo, vitesse max sur autoroute (110 / 120 / 130 km/h, 130 par défaut : max_speed_kmh, mémorisé dans default_max_speed_kmh), abonnements possédés, opérateurs exclus. Sous les résultats, la ligne de rappel ne cite que les options différentes de leur valeur par défaut. La croix ferme sans rien changer ; « Appliquer » vaut pour la recherche en cours (et l'adresse de la page : clés ms, pay, am) ; « Appliquer et mémoriser » (compte connecté) enregistre aussi sur le profil (default_min_roll_percent, default_min_charge_points, default_weather, default_card_only, default_preferred_amenities, default_consider_cashback, abonnements, opérateurs exclus ; migration 20261006_search_options.sql), relu au chargement suivant.
card_only, contexte tariffs.CARD_ONLY, rejoué dans les processus de calcul parallèles) : seules les lignes de tarif payables par carte (payment_method commençant par cb) restent, à chaque borne ; une borne sans tarif carte n'a plus de prix connu et n'est donc pas retenue (ex. Tesla Supercharger). « Appli ou carte bancaire » (défaut) : calcul inchangé.preferred_amenities : toilets, food = restaurant, café ou boulangerie, shops = commerces ; table station_amenities, lieux à moins de 300 m) : préférence, pas filtre. Une borne à qui il manque un des services choisis voit son coût de classement majoré de 10 % de la session, au moins 0,50 € (AMENITY_PENALTY_SHARE, AMENITY_PENALTY_MIN_EUR) : à prix proche, une borne avec les services passe devant ; le prix affiché et payé ne change jamais. Au regroupement des bornes voisines, celle qui a les services est gardée en premier (mark_preferred_amenities, une requête par recherche, avant le regroupement).days, offset, exclude_internal, platform, q de /api/admin/search-logs, plus /api/admin/search-logs/count pour le total, administrateurs seulement) ; le résultat et la durée du calcul ne sont pas enregistrés (décision de Jérémy : aucune écriture ajoutée aux routes de calcul), la colonne Résultat reste donc à « — ».assets/admin-contact.js, admin-logs.js, admin-jobs.js, admin-backups.js, admin-server.js) : Messages de contact — le gestionnaire les lit (route require_staff), seul l'administrateur coche « Traité » (require_admin) ; « Tout marquer traité » envoie une demande par message. Tâches planifiées — la durée d'un passage et l'état « lent » ne sont pas mesurés (rien n'est affiché à leur place) ; « Relancer » n'existe que pour les péages ; le « Journal » montre le dernier passage de chaque tâche. Sauvegardes et Serveur — lisent les résumés écrits par deploy/server/backup-status.sh (aussi les 5 dernières copies de chaque sauvegarde) et server-status.sh (aussi image, ports, volumes et redémarrages de chaque conteneur) ; le réseau n'a que les dernières 24 h : débits en Mb/s (octets × 8), totaux estimés à partir d'un relevé toutes les 5 minutes.hour de /tariffs-map/stations, arrival_hour de la fiche), plus le pire cas. La bulle de la borne, la ligne cochée de sa fiche et la plage cochée de sa grille horaire (dépliable par « variable selon l'horaire » sur chaque ligne dont le prix varie, une seule à la fois) donnent toujours le même prix ; une horloge sur la bulle signale un prix variable selon l'heure. Sans heure envoyée (ancienne version de la page), retour au pire cas horaire.PlugFilter, app/api/tariffs_map.py). Onglet Bornes seulement : les trajets gardent la prise du véhicule. Les bornes italiennes Electra et Free To X ont reçu leur type de prise le même jour (collecteurs prix_electra_it.py, prix_freetox_it.py), sans quoi ce filtre les aurait masquées.backend/app/closures.py). Source : événements routiers du réseau national non concédé (directions interdépartementales des routes), publiés en DATEX II par Bison Futé / transport.data.gouv.fr, licence ouverte 2.0, sans compte ; seules les fermetures totales (roadClosed) comptent (41 le 17/10/2026 : tronçon à deux points ou point, dates, sens fermé) ; voies fermées d'une seule file, travaux sans fermeture, alternats : ignorés ; les autoroutes concédées ont d'autres flux, pas encore lus. Relevé : un fil d'arrière-plan du serveur relit le flux toutes les 30 minutes (3 Mo), garde la dernière lecture réussie (aussi sur disque) et l'oublie après 48 h sans lecture réussie ; voyant et dernière erreur dans l'onglet « Tâches planifiées ». Calcul : l'itinéraire rapide trouvé est comparé aux fermetures actives à l'heure où il y passe (heure de départ + part du trajet, marge de 30 min ; départ sans heure : maintenant aujourd'hui, 9 h un autre jour ; « uniquement de nuit » 20 h–6 h, « de jour » le reste) : quelques millisecondes, sans appel réseau ; un trajet est concerné s'il suit le tronçon (à moins de 250 m, au moins la moitié de sa longueur) dans le sens fermé ; seul ce trajet est recalculé, avec les zones fermées interdites (GraphHopper, landmarks du profil « car », comme l'évitement des vignettes ; « sans péage » : péages interdits aussi) : 1 à 3 s de plus pour ces seuls trajets, réponse gardée en mémoire. Aucune route de remplacement : l'itinéraire d'origine est gardé, marqué closures_unavoidable. Seulement avec le moteur GraphHopper et ROAD_CLOSURES_ENABLED=true (faux par défaut). Tests : scripts/test_routes_fermees.py.pack_bornes_lentes_web et _appli). Un sélecteur Rapides · DC (plus de 22 kW) / Lentes · AC (22 kW et moins) ; la répartition se fait par type de prise (19/10/2026) : une station dont un point de 22 à 50 kW a une prise CCS ou CHAdeMO passe côté DC (table stations), le Type 2 seul reste en AC (le 22 kW est déclaré 22,08 : pas de seuil strict) ; « Lentes » remplace la carte des tarifs par celle du coût total d'une charge. Données séparées : tables slow_stations et slow_tariffs (migration 20261018_bornes_lentes.sql et 20261018_bornes_lentes_donnees.sql) ; l'onglet Rapides, le calcul des trajets et les pages de référencement ne les lisent jamais. Interrupteur SLOW_CHARGING_ENABLED (faux par défaut, config.py) : faux = aucune borne lente (sélecteur absent, routes /api/tariffs-map/slow/… en 404), le reste du site se met en production sans elles. « Ma charge » : véhicule, batterie de x à y % (20 → 80 par défaut), heures de branchement (début = l'heure de la pastille « Maintenant », fin = début + durée) ; chaque réglage recalcule tout de suite la carte, la liste et la fiche. Modèle de charge (backend/app/slow_charge.py) : puissance = min(puissance de la borne, chargeur embarqué du véhicule en courant alternatif vehicles.max_ac_kw — renseigné pour 543 véhicules sur 636 le 18/10/2026 (272 valeurs sûres, 271 « à confirmer » dans max_ac_source), 11 kW pour les autres —, modifiable par la personne dans la configuration avancée du véhicule (« Puissance AC max » : sélecteur 3,7 / 7,4 / 11 / 22 kW sous le maximum du modèle, la version la plus puissante présélectionnée (vehicles.ac_options_kw, 19/10/2026 : 130 véhicules ont plusieurs puissances, dont max_ac_kw = la plus haute) ; à l'ajout du véhicule, la question n'est posée que s'il y a plusieurs puissances ; envoyée avec « Ma charge » : veh_ac_kw et veh_usable_kwh, batterie utile dégradation comprise), « Puissance max. » choisie), constante (en alternatif la puissance baisse très peu avec le niveau) ; énergie facturée = énergie à mettre dans la batterie (batterie utile dégradation comprise × écart de niveau) ÷ rendement de charge (19/10/2026 : charge_efficiency, estimation — alternatif ≈ 84 % à 3,7 kW, 88 % à 7 kW, 90 % à 11 kW, 92 % à 22 kW ; courant continu d'une prise CCS ou CHAdeMO de la borne ≈ 94 %), c'est donc l'énergie tirée de la borne, celle que le réseau facture ; temps de charge = énergie tirée ÷ puissance ; si le créneau est plus court la charge s'arrête à sa fin et le niveau atteint tient compte du rendement (« Trop court pour 80 % : niveau max. atteint 62 % ») ; sinon le reste du créneau est du temps d'occupation. Coût d'un moyen de paiement = énergie × prix au kWh + minutes de charge × prix à la minute + frais d'occupation (minutes au-delà des minutes offertes × tarif, plafonnés, et seulement dans la plage horaire où le réseau les facture quand elle existe : idle_from_hour / idle_to_hour, 19/10/2026, ex. 7 h-21 h ; hors plage, rien n'est compté ; les minutes offertes comptent depuis le branchement pour les règles « idle_starts_after = connection » — charge comprise, 75 % des réseaux — et depuis la fin de charge sinon) ; part « temps » arrondie à la tranche entamée quand le réseau facture ainsi (billing_step_min, ex. heure entamée) + frais de session, de déverrouillage, de connexion, forfait + stationnement (par heure branchée) ; facturation minimale appliquée ensuite ; ligne valable à la date, à la puissance de la borne et à l'heure de branchement ; prix estimé affiché avec ≈. Moyens (19/10/2026 : les offres des bornes lentes, identifiants slow_…, ne sont plus mélangées à celles des rapides : GET /api/subscriptions les exclut par défaut, ?scope=slow les liste avec les offres rapides qui ont aussi un tarif lent ; abonnements et cartes restent dans deux onglets séparés ; une offre rapide possédée sert aussi en lent quand un tarif lent la mentionne) : abonnements et cartes cochés (« Mes abonnements / Mes cartes », même sélection que la pastille « Paiement »), appli de l'opérateur sans abonnement, carte bancaire ; le moins cher d'entre eux est retenu (c'est lui qui donne le prix de la bulle) ; les autres abonnements et cartes acceptés par la borne sont listés dessous, du moins cher au plus cher. Bulle : logo, prix en gras, puissance dessous ; couleur = prix moyen au kWh frais compris, même échelle que les rapides ; pastille « Coût total » : coût total ou prix moyen au kWh. Une fiche par site (site_key) : points additionnés. Calcul à la demande, sans effet sur la recherche de trajet. Tests : scripts/test_bornes_lentes.py.PlugFilter, api/tariffs_map.py), donc identique sur le site et les applis ; sans effet sur les trajets, qui ne retiennent que les bornes ayant la prise du véhicule.
services de PlugFilter, appliqué aux regroupements, aux bornes et à « Autour de moi ». Les catégories présentes autour de chaque borne sont précalculées dans la table station_amenities (même rayon et mêmes lieux OpenStreetMap que la fiche de borne, osm_amenities) par la fonction SQL refresh_station_amenities() (environ 3 s), relancée à chaque import des bornes (import_irve.py, etranger/publier_prod.py) et des lieux (import_osm_amenities.py) ; la version PAR PAYS refresh_station_amenities_pays('DE') sert au rechargement mensuel des lieux d'un pays (etranger/services_osm_pays.py : 0,7 s pour le Luxembourg, 40 à 46 s pour l'Allemagne en base locale, contre environ 100 s pour toute la base) ; le calcul à la volée prenait 0,3 à 0,7 s par requête de carte, le filtre précalculé environ 0,1 s. Migration 20261005_station_amenities.sql.pay_exclude (PayFilter). Le prix de chaque borne (repère, fiche, « Autour de moi ») ne se calcule plus qu'avec les moyens gardés et les abonnements cochés ; « appli sans abonnement » = appli de l'opérateur sans abonnement payant (compte gratuit ou abonnement à 0 €). Une borne sans aucun de ces moyens disparaît de la carte. Pour les regroupements (sans calcul de prix), une borne compte si elle a au moins une ligne de tarif d'un moyen gardé ou d'un abonnement coché. Le dernier moyen de paiement ne peut pas être retiré (la carte serait vide). Le calcul des trajets n'est pas concerné (row_filter de tariffs.best_tariff_for_station, absent pour les trajets).user_favorite_stations (migration 20261005_favorite_stations.sql), GET/PUT/DELETE /api/account/favorite-stations (500 au plus par compte) ; paramètre favorites=true (FavFilter, app/api/favorite_stations.py) sur les regroupements, les bornes et « Autour de moi » : seulement les bornes du compte connecté, aucune sans compte. Aucun effet sur le calcul des trajets.price_scales (app/price_scale.py, migration 20261005_price_scales.sql) ; lu par GET /api/tariffs-map/price-scale?zone=. Pays avec moins de 20 bornes chiffrées : échelle française. La légende affiche les seuils du pays dans la monnaie affichée.POST /api/tariffs-map/availability-batch, après les prix ; la bulle reçoit « ● libres/total » (pastille verte s'il reste une prise libre, rouge sinon), comptés sur les prises du filtre, sinon CCS et CHAdeMO (un point qui porte les deux compte une fois : type le mieux fourni). Dispo inconnue : bulle inchangée. Flux en mémoire lus directement ; Electra et Duferco (demandés à l'opérateur) : au plus 40 par appel, 8 à la fois, gardés 1 à 2 min. Aucun effet sur la recherche de trajets. Présenté comme un sélecteur « Affichage » (Prix seuls / Prix + dispo en direct) ; le choix est retenu pour un utilisateur connecté (profil, colonne users.default_bornes_live, migration 20260929_bornes_live_pref.sql).consider_cashback, mémorisée sur l'appareil et dans le profil) : si elle est cochée, 50 % du cashback (fixe, sans pondération par type de récompense) est retranché du coût utilisé uniquement pour comparer les tarifs et les plans (Dijkstra, choix de l'abonnement, « sans abonnement », top 3, abonnement manqué), car un cashback ne sert qu'une fois : recharge à 50 € avec 10 € de cashback comparée comme 45 €. Le moteur tient donc deux coûts : le coût de classement (rank_cost_eur) et le coût payé (total_cost_eur), qui ne déduit jamais le cashback ; les totaux affichés restent le coût payé. Seule une réduction immédiate (remise appliquée à la borne) est un vrai prix et se déduit du coût net ; la base n'en contient aujourd'hui aucune (toutes les récompenses connues sont des crédits futurs, cashbacks ou points). Avantages vérifiés : la table des cashbacks ne contient que des avantages publiés sur une page officielle de l'opérateur (conditions du programme, page de l'offre), avec la source et la date de relecture dans leurs conditions ; jamais de blog, de comparateur ni d'agrégateur. Parrainages, bonus d'inscription, offres réservées à un profil (Leasing social, propriétaires d'une marque) ou à un abonnement payant n'y entrent pas. Les montants sont revérifiés chaque nuit (vers 3 h 50 UTC, tâche « Avantages des opérateurs » de l'onglet Tâches planifiées) : le script lit le montant et la date de fin sur la page officielle et les compare à la table (rouge si un montant a changé, si l'avantage est terminé ou si la règle de lecture ne trouve plus rien) ; il surveille aussi les pages des opérateurs sans avantage connu pour repérer un nouveau passage cashback, fidélité ou parrainage. La table n'est jamais modifiée automatiquement : un écart se corrige à la main (liste des sources : scripts/avantages/sources.json). Admin, onglets Badges et Cashback (29/10/2026, refonte de l'admin) : « Badges » liste les cartes de recharge sans mensualité (subscriptions.kind = 'card', jamais un abonnement) des bornes lentes seulement, par pays, avec les réseaux couverts (opérateurs des lignes slow_tariffs liées à la carte) et une origine des prix déduite de la source de ces lignes (route GET /api/admin/badges, réservée aux administrateurs, résultat gardé une heure) ; « Cashback » donne à chaque avantage un pays (tarifs qui l'utilisent, sinon pays de la source de vérification de nuit), un mode de mise à jour (colonnes rewards.update_mode, updated_by, updated_at ; vide = déduit de la source) et des cases « tarifs concernés » (routes GET /api/admin/rewards/{id}/tariff-groups et PUT /api/admin/rewards/{id}/tariffs). Rien de tout cela n'est lu par le calcul des trajets : le cashback reste ignoré du total affiché.Trois modes de recherche existent côté calcul, et l'interface en présente quatre :
| Libellé dans l'appli | Mode de calcul | Critère minimisé |
|---|---|---|
| TOTAL LE MOINS CHER | balanced | coût net (recharges, hors cashback) + abonnement + 0,12 € par minute (soit 7,20 € de l'heure : valeur du temps supposée) |
| LE PLUS RAPIDE | fast | durée totale seule (le coût est ignoré) |
| SANS ABONNEMENT | balanced sans aucun abonnement/carte | même critère, abonnements exclus |
| PÉAGE INVERSÉ | balanced avec le réglage péage inversé | même critère, sur le tracé avec (ou sans) péage |
Le mode cost pur (coût seul, avec une durée infime en départage) existe dans le moteur mais n'est plus utilisé par l'interface : il ignore le temps et donnait par exemple un « sans abonnement » moins cher que « le moins cher », ce qui était déroutant.
scripts/import_irve.py) : un même site déclaré plusieurs fois est réduit à une fiche, la déclaration la plus récente : fiches à moins de 20 m, ou à moins de 500 m avec des prises en commun, ou (depuis le 29/09/2026) du même nom à moins de 60 m (Allego « Rouillé-Pamproux ASF » et « Abbeville », déclarés en 2023 puis en 2024). Une déclaration plus vieille de 6 mois est écartée sans additionner ses prises ; sinon les prises des bornes voisines sont réunies. Seules les fiches qui portent un tarif saisi à la main sont protégées d'une fusion : les tarifs repris automatiquement par position (Electra, IONITY, Driveco, Tesla, pages de paiement Powerdot, Lidl, Izivia) ne bloquent plus la fusion (29/09/2026 : ils l'empêchaient pour les doublons IONITY, Lidl et Powerdot), la fiche gardée les reçoit à la relecture suivante. Puis les corrections de la table station_overrides sont appliquées (fiches IONITY masquées ou renommées d'après l'API officielle, voir IONITY). Audit en lecture seule : scripts/audit_bornes.sql. Garde-fou (13/10/2026) : le fichier national du 13/10 était incomplet (79 Mo au lieu de 158 : Atlante, Fastned, Plug Inn, IECharge, Le Plein… absents) et l'import avait supprimé 1 557 bornes de la production, rétablies une heure plus tard avec le fichier de la veille. Désormais, si le fichier donne moins de 85 % des bornes rapides déjà en base, l'import s'arrête sans rien écrire ni supprimer, et le fichier précédent est remis en place (update_irve_loop.py). Bornes à l'étranger publiées dans le fichier national (identifiant BE…, LU…, DE…, CH…, IT…, NL…, GB… : 9 Driveco belges le 13/10) : jamais importées en France. Source (14/10/2026) : la consolidation des données statiques du Point d’accès national (transport.data.gouv.fr, validée et dédoublonnée, chaque nuit) remplace celle de data.gouv.fr, supprimée le 31/12/2026 et déjà incomplète les 13 et 14/10 (imports refusés). Sur la base du 14/10 : 7 432 → 7 376 bornes rapides, prix par borne conservés. Identifiants stables (adopt_existing_ids) : une station déjà en base republiée sous un autre identifiant garde le sien (au moins la moitié des prises en commun à moins de 500 m, sinon moins de 20 m, même opérateur) : Electra (sites FRELCPAA… devenus FRELCP129…, mêmes prises), Allego, TotalEnergies ; ses pages /borne/, prix par borne, exclusions et fiche Google restent valables. Réseau e-charge50 (Manche) publié sous l’exploitant « e-charge50 » : rangé chez Zunder comme avant. Les 46 bornes « Izivia » du réseau MObiVE (Gironde, Landes, Charente, Pyrénées-Atlantiques) disparaissent : MObiVE est désormais exploité par Citeos (bornes surtout à 36 kW), les fichiers Izivia dataient de mai 2024.scripts/etranger/publier_prod.py, depuis la zone de travail etranger (onglet « Pays voisins ») : seulement les tarifs d'un opérateur au statut « sûr » ou lus dans l'API officielle de l'opérateur, jamais un statut « incertain » ni un tarif expiré ; seulement les stations qui ont un de ces tarifs ; pas les stations à horaires limités (même règle que Driveco) ; une station à moins de 30 m d'une borne française du même opérateur est écartée (doublon de l'IRVE) ; un même site déclaré plusieurs fois dans un registre (même opérateur à moins de 50 m : Aral pulse, EnBW, E-Flux…) ne garde qu'une fiche, celle qui a un prix propre, puis la plus puissante (157 fiches écartées le 29/09/2026). Chaque borne et chaque ligne de tarif porte son pays (stations.country, tariffs.country, « FR » pour tout ce qui existait) : une ligne ne s'applique qu'aux bornes de son pays (Allego, Lidl, Electra… n'ont pas les mêmes prix en France et en Belgique). Les abonnements portent la liste des pays où ils valent (subscriptions.countries) ; une offre valable en France et ailleurs reste une seule ligne. Tout est enregistré en euros : les prix en francs suisses sont convertis au taux de référence BCE du jour de la publication (le prix d'origine et le taux restent dans la source de la ligne) ; seul le prix en euros est affiché. Trajets : seulement les bornes des pays couverts par le graphe routier, réglage TRIP_COUNTRIES_CSV (config.trip_countries, « FR » tant que le graphe ne couvre que la France ; filtre dans stations_query.py, et abonnements pertinents lus sur les tarifs de ces pays). Le graphe France + pays voisins est fabriqué à part par deploy/server/osm-europe-build.sh (extraits Geofabrik fusionnés, filtrés sur les routes, ferries et navettes ; moteurs en service inchangés tant qu'on ne bascule pas). Onglet Bornes : la carte montre toujours toutes les bornes (France et pays voisins) ; la bascule « France | Europe » (France par défaut) ne filtre que la liste des opérateurs et celle des abonnements (paramètre zone des points d'accès, « fr » par défaut pour les applis qui ne l'envoient pas encore). Les scripts de prix français (Electra, IONITY, Driveco, Tesla, Powerdot, Lidl, Izivia, grille Allego) ne lisent et n'écrivent que les bornes et tarifs français. Les lieux à proximité des bornes étrangères sont dans osm_amenities (country), que l'import des lieux français ne remplace plus ; depuis le 21/10/2026 ce sont les lieux du pays ENTIER, rechargés chaque mois pays par pays (voir « Fiche borne de l'onglet Bornes », services_osm_pays.py).scripts/update_electra_tariffs.py, relancé toutes les 24 h par le conteneur electra_tariffs_updater) : l'API publique du site d'Electra donne, pour chaque borne, ses paliers horaires ; le rapprochement avec nos bornes se fait par coordonnées (≈ 50 m) et les lignes « carte bancaire » de la borne sont entièrement remplacées à chaque passage. Le supplément de congestion (borne très occupée) n'est pas modélisé.regenerate_electra_plus). Les lignes génériques à 0,39 € / 0,29 € restent en repli pour les bornes sans relevé ; les tarifs Electra+ Smart à 0,49 € sur Atlante, Fastned et Ionity (réseau ChargeLeague) sont saisis à part et ne sont pas touchés.scripts/update_driveco_tariffs.py, relancé toutes les 24 h par le conteneur driveco_tariffs_updater) : Driveco ne publie pas de grille, chaque borne a son prix. Son appli officielle l'affiche point de charge par point de charge, même sans compte ; le script lit la même API que l'appli (exploitation.driveco.com/icpm, clé fournie par l'appli) : la carte, découpée en zones de 100 stations au plus, puis la fiche de chaque station (puissance, prix au kWh, frais fixes par session, gratuité). Prix vérifiés à la main dans l'appli le 28/09/2026 (Carrefour Market Cazouls-lès-Béziers : 0,51 €/kWh sur les points 50 kW, 0,55 €/kWh sur les points 200 kW). Une station qui mélange des puissances a une ligne par tranche de puissance (power_band_min_kw / power_band_max_kw, seuls les points de 43 kW et plus) ; à puissance égale, le prix le plus cher. Rapprochement par coordonnées (80 m) : les 356 bornes Driveco de l'IRVE retrouvées. 29/09/2026 : Driveco est aussi reconnu par son nom d'exploitant dans l'import IRVE (« DRIVECO », « DRIVECO Partner Network », surtout des concessionnaires) : 671 stations au lieu de 356 ; les stations qui ne sont pas ouvertes 24 h/24 d'après l'API (horaires d'atelier, quelques Carrefour Market fermés le soir) n'avaient alors aucun prix (décision de Jérémy : Kargit ne gérait pas encore les horaires d'ouverture) ; environ 430 stations chiffrées. 26/10/2026 (horaires gérés depuis le 20/10, décision de Jérémy relayée par la coordination) : le prix d'une borne à horaires limités est maintenant ÉCRIT, le site l'écarte lui-même des trajets (colonne stations.trip_open) et des charges hors heures d'ouverture ; seule exception, les bornes que l'API dit limitées alors que NOTRE base les croit ouvertes 24 h/24 ou sans horaires (12 bornes rapides) : le site ne pourrait pas les écarter, leur prix reste caché (561 stations rapides chiffrées dont 428 ouvertes aux trajets). Aucun prix selon l'heure. Un point marqué gratuit dans l'API n'est pas repris (29/09/2026 : concession Hyundai de Villeneuve-d'Ascq, en pratique réservée aux clients ; à 0 € elle aurait attiré tous les trajets du coin), ni un prix nul. Les frais de stationnement (0,30 €/min, 15 min après la fin de la charge) ne sont pas dans l'API et ne sont pas modélisés, comme la congestion d'Electra. L'ancienne ligne générique (prix d'agrégateur) est supprimée : une borne sans relevé n'est pas chiffrée. Enregistré comme paiement « carte bancaire / sans compte ». Bornes LENTES (26/10/2026, accord de Jérémy : « étends aux lentes ») : collecteur scripts/lentes/prix_driveco_lentes.py (même API, même clé, 0,4 s entre deux appels, étape de nuit api_driveco, écriture api:driveco par appliquer_prix_api_lentes.py) : un point de charge est rattaché par son IDENTIFIANT (« 11739*P1 » de l'API = fin de l'identifiant IRVE FRAIRE11739P1), à défaut par la station la plus proche à moins de 80 m (voisine réciproque, sinon rien) ; la station reçoit un prix seulement si tous ses points ont le même prix au kWh et les mêmes frais de session ; un point gratuit ou sans prix empêche tout prix. Frais d'occupation : absents de l'API, repris point par point du champ tarification de la base nationale (matrixOSF : 0,20 €/min après 15 min de grâce depuis la fin de la charge) ; un point absent de ce champ reçoit la valeur du réseau pour les bornes alternatives (708 points sur 710), dite dans les notes de la ligne. Le second palier à 0 € des bornes alternatives (« à partir de 15 min de dépassement ») est lu comme sans effet (premier palier appliqué sans plafond) : à vérifier dans l'appli. L'API remplace le JSON de la base nationale quand elle chiffre la station : sur les 354 Carrefour Market déjà chiffrés par la base nationale (0,39 €/kWh) l'appli affiche 0,35 €/kWh. Même règle d'horaires que les rapides : prix écrit, sauf borne que l'API dit limitée alors que notre base la croit 24 h/24 (13 stations lentes). 789 stations lentes chiffrées (440 nouvelles) ; 299 restent sans prix (204 absentes de l'appli, points gratuits, horaires). Disponibilité en direct : l'API de l'appli donne aussi l'état de chaque point (non branchée ici : seules 382 stations lentes Driveco sur 1 094 ont un état par le flux national).scripts/update_ionity_tariffs.py, relancé toutes les 24 h par le conteneur ionity_tariffs_updater) : la page tarifs d'IONITY n'affiche que des prix minimums (« les prix varient selon la borne »). Le script lit l'API de l'appli officielle (adhoc-bff.ionity.cloud, sans compte, avec les en-têtes de l'appli) : pour chaque point, le prix « IONITY DIRECT » (sans compte ni abonnement), rangé en carte bancaire, une ligne par tranche de puissance ; points haute puissance (150 kW et plus) d'un même site à des prix différents : le plus cher. Constaté le 28/09/2026 : 0,62 ou 0,55 €/kWh selon le site (l'ancien découpage « autoroute / hors autoroute » se trompait sur 22 bornes), anciens points 50 kW et 43 kW AC à 0,39 ou 0,35 €/kWh. Appli sans abonnement (« Go ») : l'API ne le donne qu'à un compte connecté ; seuls les paliers vus dans l'appli sont écrits (DIRECT 0,62 → 0,59 ; 0,55 → 0,52), sur la tranche haute puissance. Motion / Power : montants inchangés (0,39 / 0,31 au palier bas, lignes génériques 0,44 / 0,35 sinon), désormais rattachés au palier réel de chaque borne. Rapprochement par coordonnées (250 m). Fiches corrigées d'après l'API (29/09/2026) : le fichier national garde les déclarations IONITY de mars 2023 (FRIONE…) à côté de celles de septembre 2026 (FRIOYP…) ; 28 sites étaient en double et 32 fiches de 2023 portaient le nom et l'adresse d'un site avec les coordonnées exactes d'un autre (« Troyes Sud » dessiné à Tours). Chaque jour, avant les prix, chaque fiche est comparée au site de l'API le plus proche (250 m, même nom) : sur un site, la fiche la plus récente au bon nom est gardée, les autres sont masquées ; un site sans fiche au bon nom garde sa fiche la plus récente, renommée avec le nom et l'adresse de l'API ; une fiche loin de tout site de l'API est masquée (ex. « Val Gelon » près de Troyes). Corrections écrites dans la table station_overrides, que l'import IRVE quotidien applique aussi. Garde-fous : aucune correction si l'API renvoie moins de 500 sites, ou s'il faudrait masquer plus de 40 % des fiches.scripts/charger_prix_fr.py : rapprochement par coordonnées, une ligne par tranche de puissance, points de 150 kW et plus d'un même site au prix le plus cher, frais au temps « après N minutes » non modélisés ; la ligne générique de l'opérateur reste en repli pour les bornes non retrouvées) : Powerdot (page de paiement par QR code, scripts/prix_powerdot_fr.py : 1 117 bornes sur 1 136, surtout 0,59 / 0,56 / 0,60 €/kWh au lieu de 0,54 partout), Izivia (page de paiement par carte PayNow, scripts/prix_izivia_fr.py : 872 bornes sur 1 043 ; IZIVIA FAST à 0,35 €, 0,30 de 9 h à 11 h et de 15 h à 17 h, on garde le plus cher), Lidl (page de paiement direct ChargePoint, scripts/prix_lidl_fr.py : 874 bornes sur 880, 0,39 € presque partout). Rangés en carte bancaire (paiement sans compte).scripts/maj_grille_allego.py) : grille officielle par puissance (allego.eu/pricing, France) : jusqu'à 50 kW 0,49 carte / 0,47 Smart / 0,35 Plus ; au-delà 0,59 / 0,56 / 0,39.POST /api/admin/tesla-prices. Le rapprochement se fait par coordonnées : le site Tesla le plus proche de la borne, à moins de ≈ 400 m. Sites réservés aux Tesla (10/10/2026 : 16 Superchargers de la base, dont Rungis, Montélimar, Chambéry, n'avaient aucun prix, le relevé ne lisant que les sites ouverts aux autres marques) : relevés aussi, avec le seul prix des Tesla, rangé sous l'abonnement caché sub_tesla_reserve (gratuit, appliqué d'office aux véhicules Tesla, jamais proposé ni sélectionnable) ; une autre marque n'a donc aucun prix sur ces sites et n'y est jamais envoyée. L'onglet indique la date du dernier relevé et signale un relevé de plus de 14 jours ; depuis le 08/10/2026, un voyant « Tesla » en haut de l'admin (vert, rouge à partir de 14 jours) le rappelle, à côté de ceux des messages à traiter, des sauvegardes et de la météo. Chaque import ajoute une entrée à l'historique de l'abonnement Tesla. Depuis le 12/10/2026, le même relevé couvre aussi les pays voisins (Belgique, Suisse, Allemagne, Espagne, Royaume-Uni, Italie, Luxembourg, Pays-Bas, Portugal, Irlande (19/10/2026) ; demande de Jérémy, auparavant relevé à part le 28-29/09) : la liste des Superchargers est lue pays par pays, les prix hors zone euro (livre, franc suisse) sont convertis au taux de référence BCE du jour (sans taux : prix non écrit), les prix à la minute sont ignorés et, quand la grille change selon le jour, chaque heure prend le prix du jour le plus cher (règle d'Electra). Un prix converti hors de 0,10 à 1,50 €/kWh n'est jamais écrit. Le relevé dure plus longtemps (environ 1 h pour un millier de sites). Historique des relevés (29/10/2026) : chaque import réussi écrit une ligne dans la table tesla_releves (date, superchargeurs lus, lignes et bornes écrites, prix moyen €/kWh de ces bornes avant et après : l'écart moyen affiché) ; un relevé en échec n'atteint jamais l'import, le favori garde donc l'étape où il s'est arrêté et le message d'erreur dans le nom de l'onglet (window.name) et l'admin les enregistre au retour par POST /api/admin/tesla-prices/releves (lecture : GET sur la même adresse ; routes réservées aux administrateurs). Un relevé interrompu parce que l'onglet tesla.com a été fermé n'est pas enregistré.scripts/update_api_tariffs.py, conteneur api_tariffs_updater, chaque nuit vers 3 h 40) : Eneco, E-Flux by Road, Vattenfall InCharge (Pays-Bas), GOFAST (Suisse), Shell Recharge (Allemagne, Pays-Bas, Suisse, Royaume-Uni), Powerdot (Espagne), Be.EV, Fastned, GeniePoint, GRIDSERVE (depuis le 25/10/2026 par l'API officielle « Open Data » avec clé, GRIDSERVE_API_KEY : prix TTC exact de chaque prise = énergie hors TVA × (1 + TVA) du tarif OCPI de la prise, 2 appels séparés de 31 s car l'API n'en accepte qu'un toutes les 30 s ; avant : lecture du site, prix en fourchette), InstaVolt, IONITY, MFG EV Power, Osprey (Royaume-Uni), Allego, Duferco, Fastned, IONITY (Italie). Pour chacun, le collecteur officiel qui a servi à la première saisie (scripts/etranger/prix_*.py) est relancé, chaque site rapproché d'une borne de la base (même pays, même opérateur, 150 m), les lignes calculées comme à la première saisie (un moyen de paiement par libellé, une ligne par tranche de puissance, le prix le plus cher pour les points de 150 kW et plus d'un site), converties en euros au taux BCE, puis les lignes de la source sont remplacées. L'état du dernier passage est dans l'onglet « Tâches planifiées ».scripts/tarif_checks.py, appliqué aussi à Electra, Driveco et IONITY France) : une ligne hors de 0,01 à 2,50 €/kWh (plancher abaissé de 0,10 à 0,01 le 19/10/2026, Jérémy : « Atlante fait parfois des promos » ; plafond relevé de 1,50 à 2,50 le 21/10/2026, Jérémy), un prix à la minute au-delà de 1 €/min ou des frais de session au-delà de 15 € sont refusés et la borne garde ses anciens prix ; une source entière est refusée (anciens prix gardés partout) si plus de 10 % de ses bornes sont dans ce cas, si elle ne retrouve aucune borne, ou si le prix au kWh change de plus de 40 % sur plus de 30 % des bornes (API qui renverrait des centimes, devise changée...). Borne absente de la source (14/10/2026, Jérémy : carte Freshmile passée de ~320 à 186 sites rapides) : elle garde ses prix 3 nuits de suite, puis les perd si elle ne revient pas (ABSENCE_NIGHTS, premier jour d'absence dans data/tarifs_api/absences.json) ; les bornes retrouvées sont mises à jour même si moins de la moitié revient (simple avertissement). Freshmile : comme sa carte publique a perdu ~310 sites sans annonce alors que leurs fiches officielles répondent toujours, le relevé lit aussi la fiche de chaque site déjà connu (liste gardée dans data/tarifs_api/freshmile_locations.json ; sort de la liste une fiche disparue, privée ou en itinérance « Interop sortante ») : 494 stations rapides chiffrées au lieu de 186. Avant : source refusée en bloc sous la moitié (prix gardés sans limite), et borne absente sans prix dès la première nuit au-dessus. Driveco : une borne qui n'apparaît plus sur leur carte perd son ancien prix (seulement si le relevé est complet).data/lentes/grilles/enerstock.json (confiance 0,85, paiement appli ; 78 stations lentes de 22 kW ou moins, les 3 de 23-24 kW restent sans prix ; tarif DC 0,38 €/kWh non écrit, aucune borne rapide Enerstock en base), lien des applis dans operator_apps (scripts/donnees_20261023_enerstock_apps.sql). E-Totem / SEMOB (réseau de Saint-Étienne Métropole) : l'appli E-TOTEM donne, pour les bornes 22 kW de SEMOB, 0,45 €/kWh TTC, 15 min offertes après la charge puis 0,50 € par tranche de 15 min, « plafonné à 2 € entre 20 h et 8 h » ; le collecteur public prix_etotem_lentes.py refusait toute la borne à cause de cette dernière ligne : il l'accepte désormais comme une note (le plafond de nuit n'est PAS appliqué, donc le frais d'occupation n'est jamais sous-estimé), ce qui ajoute 105 stations à api:etotem (605 au lieu de 500) avec les mêmes valeurs que les trois relevés manuels. Appli Driveco (contrôle) : elle affiche aussi le prix des points lents (0,29 à 0,35 €/kWh en 22 kW, réseau partenaire compris) ; le collecteur Driveco ne lisait que les points rapides : étendu aux lentes le 26/10/2026 (voir la ligne Driveco ci-dessus). Prix d'itinérance, non repris : les applis Last Mile Solutions (Yaway Charge, Sorégies) et Ze-Watt affichent pour les bornes d'autres opérateurs un prix d'itinérance (Electra Montpellier 0,64 contre 0,61 €/kWh chez Electra) : jamais utilisé comme prix de l'opérateur. Illisibles sans compte : applis Monta en marque blanche (OZECAR, Mobilize EV Power, SOWATT, MyWAAT), ZEborne, Nexteneo, Yes55, Qovoltis, 50five, EVzen, Qwello, Smappee (compte à créer : laissées installées sur le téléphone de Jérémy, qui crée lui-même les comptes). 26/10/2026, relevés à l'étranger sur le téléphone de Jérémy : eCarUp (Suisse, Autriche, appli sans compte, fiche de borne > tarif horaire) : prix fixé par chaque exploitant, en CHF (Steffisburg : commune 0,39, Raiffeisenbank 0,35 CHF/kWh), pas de grille ; 2 bornes écrites (slow_tariffs.source = 'appli:eCarUp', TTC présumé, devise CHF). TotalEnergies Charge Europe (Pays-Bas) : prix publics « TVA comprise » par station (Bloemendaal 0,50 et 0,38 €/kWh) et, à Utrecht, des prix qui changent dans la journée (0,46 à 0,51) : aucune grille, rien écrit ; le prix NDW multiplié par 1,21 reste à recouper avec l'appli sur une borne qui a aussi un prix NDW (Augusto Sandinostraat 112, Utrecht). Allego : l'appli sans compte prévient que les prix affichés sont « génériques » : non repris. ENGIE Vianeo (Flandre) : l'appli du téléphone (connectée) n'affiche aucune borne flamande, un seul relevé invité (Zottegem 0,3812 €/kWh en 22 kW) : non chargé. sdea-aube.json. Citeos Vauclus'élec et Perpignan : prix du champ tarification de la base nationale considérés HORS TAXE (décision de Jérémy), TTC = x 1,2 (0,33334 -> 0,40 ; 0,2667 -> 0,32 €/kWh ; occupation hors charge 2,5 et 6 €/h HT) ; 66 stations sur 93 (une station n'est écrite que si tous ses points portent le même texte) ; « 2.5 € par heure de charge » non porté (doublon probable). Freshmile lentes : trois causes côté Kargit corrigées : fiches marquées is_private écartées à tort (311, dont tout le SDEA), outre-mer absent du fichier du 20/10, et un lecteur de texte limité à un montant par aspect. Nouveau lecteur freshmile_texte_lentes.py (utilisé quand l'ancien refuse) : kWh, connexion, minute pendant tout le branchement avec palier (X en stationnement à l'heure + surplus en occupation depuis le branchement), frais après la fin de charge (minutes offertes), plages jour / nuit, prix au temps seul, « sauf entre 20 h et 8 h » ; tout montant doit être expliqué, sinon refus (jamais de « kWh seul » quand le texte porte des frais) ; 1 216 -> 728 stations sans prix. « Gratuit » : prix 0 écrit seulement si la base nationale le dit aussi (0 station sur 38). Reste, côté calcul : « X €/min sans consommation après N h de branchement » (199 stations) et « prix du kWh qui change après une durée » (Plenitude 0,45 puis 0,61 €/kWh après 60 min de charge, 64 stations non chargées : ce n'est PAS un frais d'occupation), propositions dans docs/bornes_lentes/propositions_modele/. Driveco lentes : voir la ligne Driveco ci-dessus. Rapport : docs/bornes_lentes/PRIX_MANQUANTS_FRANCE_1026.md.inactive), anciens prix gardés, aucun contournement. Pologne (EIPA) : des plages d'ouverture qui se suivent (Hazel : 00:00-05:59, 06:00-21:59, 22:00-23:59) comptent maintenant comme 24 h/24 ; stacjeladowania.com a changé d'identifiants de stations dans le registre (renumérotation en base par scripts/donnees_20261020_pl_stacjeladowania.sql). Freshmile : le compte « bornes retrouvées sur N en base » ne compte plus que les stations que la source peut couvrir (50 kW et plus, métropole : base_min_kw / base_bbox), les 385 stations lentes et l'outre-mer n'en faisaient pas partie ; aucun prix modifié, aucune garde de prix touchée.update_api_tariffs.EURO_PAYS) ne sont relues que le dimanche ; la France, les pays hors euro (Royaume-Uni, Suède, Norvège, Danemark, Pologne, Tchéquie, Hongrie, Roumanie, Suisse…), le passage de midi et les grilles nationales restent chaque nuit. Raison (mesure du 17/10/2026, deux relevés à 25 h d'intervalle) : sur 32 208 lignes de relevé automatique, 11 730 prix « changeaient » dans les pays hors euro (taux de change du jour, quelques millièmes d'euro) et seulement 79 en zone euro, dont 77 changements réels ; 130 des 207 sources de nuit sont en euro. Effet : moins d'appels aux API des opérateurs ; aucun effet mesuré sur la vitesse des recherches (relecture à priorité minimale, 8 recherches chronométrées au repos et pendant une relecture : médiane 2,1 s dans les deux cas). Une source en euro peut donc garder un ancien prix jusqu'à 6 jours ; son décompte « nuits d'absence » et sa série d'échecs avancent d'une relecture par semaine. --source X relit toujours X.scripts/etranger/grilles_officielles.py relit leurs pages officielles par expressions régulières (montants EnBW : vignettes contrôlées par empreinte, illisibles en texte) et met à jour le prix des lignes génériques correspondantes déjà en base, jamais de création ni de suppression ; un changement est ajouté au texte de la source avec sa date. Page changée de forme, prix incohérent ou variation de plus de 40 % : rien n'est modifié et l'onglet « Tâches planifiées » l'indique, à vérifier à la main.slow_etranger_updater, état par tâche dans data/lentes/etranger/_boucle_etat.json). Cette dernière montre, par pays, le dernier passage de chaque tâche, le nombre de stations et de points, la part en direct et le nombre de stations avec un prix propre. Rouge : une mise à jour quotidienne n'a pas réussi depuis plus de 30 h, ou un relevé de disponibilité depuis plus de 6 h ; orange : plus de 26 h / plus d'1 h ; gris : pays chargé en base mais non activé dans SLOW_ETRANGER_PAYS (pas de mise à jour automatique, normal tant que sa licence n'est pas réglée).update_api_tariffs.py). Le service fast_etranger_updater (scripts/etranger/maj_rapides_boucle.py, pays activés dans FAST_ETRANGER_PAYS, une heure UTC par pays : registres entre 0 h 30 et 3 h 00) refait la chaîne PAYS PAR PAYS (maj_rapides_pays.py) : l'importeur officiel du pays retélécharge le registre (1 à 3 fichiers, sans parcours de pages) et le range dans la zone de travail etranger.stations (jamais lue par le site), puis publier_maj.py applique EXACTEMENT les règles de la publication initiale (borne rapide = comme en France depuis le 18/10 : au moins un point de 50 kW ou plus, OU une prise CCS / CHAdeMO de plus de 22,5 kW (24, 30, 47 kW… : « DC · plus de 22 kW ») ; le Type 2 seul reste lent ; puissance inconnue = jamais rapide ; une station qui a un point rapide et des points alternatifs reste UNE seule fiche, tous ses points décrits ; côté bornes lentes étrangères (21/10/2026), les points CCS / CHAdeMO de plus de 22,5 kW n'y sont plus : scripts/lentes/dc_vers_rapides.py, lancé après l'import lent et les prix de chaque pays, les rattache à la fiche rapide du même exploitant à moins de 100 m (linked_station_id) ou crée une fiche rapide dérivée (source etranger:slow_dc, identifiant PAYS:dc:…, prix « sûrs » repris en euros, sans plafond ni tranche horaire ; frais d'occupation comptés depuis le branchement non repris) ; une station lente sans aucun point lent disparaît des lentes, une station mixte garde ses seuls points alternatifs ; règle codée une seule fois, scripts/regle_rapide.py, reprise par import_irve.py et tous les importeurs étrangers ; types de prise connus, pas d'accès à horaires limités, pas de doublon d'une borne IRVE du même opérateur à moins de 30 m, une fiche par site, pas la même borne sous deux opérateurs, pas deux fiches du même opérateur à moins de 40 m, lieux à moins de 300 m). Ce qui change dans stations : les stations nouvelles sont ajoutées (sans prix : « tarif non communiqué » jusqu'à ce qu'une tâche de prix les reconnaisse) ; la puissance, les prises, le nombre de points, le nom, l'adresse, la position et les points de charge des stations déjà présentes sont mis à jour (jamais le prix ni l'opérateur) ; une station disparue de la source (ou qui ne passe plus une règle) est retirée la 3e nuit d'absence de suite, sauf si le fichier a perdu plus de 30 % des stations (garde-fou : rien n'est retiré, et un import qui ferait tomber une source sous 70 % est annulé et la zone de travail restaurée). Une station qui a des favoris, des exclusions ou une correction n'est jamais supprimée sans les reporter sur la fiche qui la remplace ; sans remplaçante elle est gardée et signalée en orange. Quand la source change l'identifiant d'une borne (nouvelle fiche du même opérateur au même endroit), l'ancienne est remplacée tout de suite, favoris, exclusions et prix propres reportés. La France n'est jamais touchée. Compte rendu dans la tâche Bornes rapides : pays étrangers (mise à jour automatique) (état data/fast_etranger/_boucle_etat.json : ajoutées, mises à jour, retirées, refusées par règle) : rouge = mise à jour pas réussie depuis plus de 30 h ou import refusé par le garde-fou ; orange = plus de 26 h, dernier essai en échec, retrait bloqué ou station gardée parce que des favoris s'y rattachent ; gris = pays non activé.api_…, « API GraphQL officielle Electra », « API de l'appli officielle… », tarif OCPI du NDW) ; Relevé de nuit = relevé automatique ou grille nationale relue chaque nuit sur la page officielle (Aral pulse, EnBW, EWE Go, SuperChargy, Q8, Engie Vianeo, Lidl et Kaufland en Allemagne) ; Approximatif = tarif d'exemple, estimé (bornes lentes) ou dont la source se dit indicative, estimée ou déduite ; Relevé ponctuel = tout le reste (relevé fait une fois, dont les relevés Tesla de Jérémy). La valeur par défaut est CALCULÉE à chaque affichage d'après le texte de la source des tarifs en vigueur de l'opérateur (mêmes expressions que le récapitulatif des bornes, scripts/stats/recap_bornes.py) : le mode qui couvre le plus de lignes de tarif l'emporte (à égalité : API, nuit, ponctuel, approximatif). Le choix de l'administrateur (sélecteur de la fiche de l'opérateur) est enregistré dans la table operator_price_mode (opérateur, rapides ou lentes, mode, auteur, date) et PRIME sur la déduction ; « revenir à la valeur déduite » l'efface. Aucun effet sur le calcul des trajets : c'est une information d'administration. Nouvelles routes réservées aux administrateurs : /api/admin/ops/… (méta des opérateurs, bornes et tarifs paginés, fiche d'une borne, historique d'un tarif, abonnements, « tarif propre » d'une borne lente). Un « tarif propre » d'une borne lente est une ligne de slow_tariffs de source « Saisie manuelle (admin) » : les scripts de scripts/lentes ne suppriment les lignes que par préfixe de source (grille:, api:, irve:tarification, estimation:…), elle survit donc aux régénérations ; les lignes générées restent en lecture seule dans l'admin. Le commentaire libre garde « modifié par » (colonne revue_jeremy.maj_par) et l'abonnement un champ « Avantage » (subscriptions.advantage), vides tant qu'ils ne sont pas renseignés.scripts/veille_pages_tarifs.py, une fois par mois avec le passage de nuit) : les pages officielles citées par les tarifs génériques en vigueur (adresse de la colonne source_url ou citée dans le texte, hors presse et comparateurs, hors pages d'accueil) sont relues ; seuls les passages qui contiennent un montant sont comparés au mois précédent (prix affichés seulement par le JavaScript : montants au kWh cherchés dans le code de la page). Page modifiée : alerte rouge dans la provenance de l'opérateur (onglet Opérateurs) avec les montants apparus et disparus, pendant 35 jours (jusqu'à la veille suivante ; avant le 13/10/2026 : jusqu'à ce que la case « Vérifié par Jérémy », retirée depuis, soit recochée). Aucun prix n'est modifié par la veille. Page sans aucun montant (13/10/2026) : comptée injoignable, l'état précédent est gardé ; cas constaté : enodrive.lu (grille SuperChargy) sert au serveur une page anti-robot (captcha) au lieu des tarifs, la grille SuperChargy n'est donc pas relue automatiquement (prix en base gardés, échec signalé dans Tâches planifiées) ; aucune protection anti-robot n'est contournée.scripts/audit_coherence_bornes.py (doublons d'un même opérateur à moins de 150 m, même site sous deux opérateurs, bornes hors de leur pays, puissances, coordonnées, bornes sans prix, prix hors bornes ou à plus de 50 % de la médiane de l'opérateur). Règles de « même site » dans scripts/meme_site.py : jamais les deux côtés d'une autoroute (Nord / Sud, Ost / West, « N-S » / « S-N », « (LIS/POR) » / « (POR/LIS) », « Dir. Barna. » / « Dir. Puigcerdà », izquierda / derecha) ; pays voisins (publication et dédoublonnage sur place, dedoublonner_etranger.py : 273 fiches retirées le 13/10, surtout en Allemagne où le registre déclare souvent un site par type de chargeur) : même site à moins de 75 m, ou même adresse écrite autrement (« Str. » / « Straße », tirets, fautes de frappe) jusqu'à 150 m, ou même rue / même nom significatif jusqu'à 100 m ; France (import IRVE, règle stricte) : même nom significatif ou même adresse jusqu'à 150 m seulement — des commerces voisins d'une même zone (concessions Driveco BMW / Hyundai…) ont des prix différents et restent séparés. La fiche gardée reçoit les points de charge, favoris et exclusions des autres. Italie : une station « Enel » à moins de 25 m d'une station Ewiva est la même borne (la plateforme nationale range Ewiva sous Enel X) : retirée. Noms qui ne sont qu'un code (« Yusco/… », « Freshmile France/LLJ… », Lidl « LFR3627EVCP03 ») : « Opérateur - Ville » d'après l'adresse, ou le nom officiel de la carte de l'opérateur (Le Plein, Freshmile : station_overrides). Aral pulse Luxembourg : le tarif Klassik (0 €/mois) est enregistré comme appli sans abonnement (aucune borne Aral ne s'affichait). Résultat de l'audit après corrections : plus aucun doublon selon ces règles hors 5 cas limites ; prix atypiques tous expliqués par leur source (tarifs HPC E.ON en Tchéquie et Hongrie, tarifs propres de sites Zunder à Bilbao, Driveco chez trois concessionnaires…).charger_prix_json.bands_by_hour, utilisé par l'import des pays voisins et par update_api_tariffs.py) : chaque point vaut sur sa plage (« 07:00-20:00 », avec ou sans jours) ou suit sa liste de plages (Duferco, E-Flux, Enel) ; découpage à la demi-heure ; à chaque demi-heure, le prix du jour le PLUS CHER (même règle qu'Electra et Tesla : Kargit ne connaît que l'heure, pas le jour) ; plages identiques fusionnées, nuit à cheval sur minuit en une seule ligne (valid_from_hour / valid_to_hour). Concernés : InstaVolt, Be.EV, Osprey, Duferco, E-Flux, Source… Le contrôle des sauts de prix compare désormais le prix le plus cher de chaque borne avant et après (sinon le tarif de nuit passait pour une baisse de 40 %). Une borne dont le nouveau relevé est refusé garde ses anciens prix, sauf ceux qui sont eux-mêmes hors bornes (moins de 0,10 ou plus de 1,50 €/kWh : une borne Driveco à 0 € relevée le 29/09, avant les contrôles).scripts/import_irve.py, prix de scripts/grilles_rapides_supplement.py) : Easy Charge Services, eborn et AVIA VOLT (groupe EASYCHARGE), Plenitude On The Road (appli), Sowatt (prix TTC de chaque station, jeu de données publié par Sowatt sur data.gouv), Mobive (Citeos Nouvelle-Aquitaine), Ouest Charge (grille par département : 49, 44, 35, 29, 22 ; +1 € de frais pour les non abonnés), Métropolis et Oscéa 64 (SPIE CityNetworks), Shell Recharge, Obornes (Oreve), Morbihan énergies. Une puissance que la grille ne couvre pas reste sans prix (la borne n'est pas proposée pour cette puissance) ; la post-charge, l'occupation et les plafonds ne sont pas encore calculés (rappelés dans la source de chaque ligne). La carte bancaire n'est écrite que si le fichier national la déclare ou si la page officielle l'affirme. Disponibilité en direct : flux national, borne par borne (aucune borne « libre » sans mouvement depuis 24 h). Deuxième passe (même jour) : bp pulse (grille nationale par puissance), Partage Ma Borne, TE80 (Somme), WiiiZ (réseau d'EVzen), Autorecharge et Yaway (stations dont le prix est déclaré au Point d'accès national ou sur la page officielle), Révéo (grille par département et classe de puissance), Sorégies (heures creuses / pleines, 0,99 € par recharge ; les 103 stations d'environ 50 kW dont la classe « accélérée » ou « rapide » n'est pas tranchée restent sans prix), les réseaux de Territoire d'Énergie Bourgogne-Franche-Comté (une grille par syndicat), eCharge54, Ecocharge77, reGARD, AQUÍ, SYDEV, Béziers et Chargezy ; les prix par station sont dans scripts/grilles_rapides/par_station.json. Un réseau importé dont une station n'a pas de prix sûr garde cette station sans prix (elle n'est pas proposée). Restent hors de Kargit faute de prix sûr : EVzen hors WiiiZ (0,49 « selon la station »), Dream Energy / Qowatt, Bump, Qovoltis, Eranovum, e-motum, Eoliberty, Road, Geeve, Enerstock, Picoty, ChargeGuru / Tohmo, Le Plein Tarnais, Toulibeo, Alizé Liberté 2 ; Milence (poids lourds) et Greenspot (décision du 12/10) restent exclus.tariffs porte désormais idle_fee_eur_min, idle_free_min et idle_fee_max_eur (migration 20261020_tariffs_idle.sql) ; api/tariffs_map._row_session_cost ajoute frais × max(0, durée − minutes offertes) (plafonné s'il y a un plafond) au coût de la session de la FICHE et du prix de la carte (exemple : Mobive Gujan-Mestras, 0,12 €/min après 1 h 30, charge de 135 min : 27,05 € → 32,45 €). La pastille de la carte suppose une session de 25 min (aucun frais d'occupation), et le calcul des TRAJETS (module natif, recherche) ne lit pas ces colonnes : aucun effet sur la recherche. Les bandes « DC 22-39 / 40-60 / > 60 kVA » d'une grille rapide sont des classes de puissance de la BORNE : une charge sur prise AC d'un site mixte se chiffre avec la grille des bornes lentes (slow_tariffs), pas avec la fiche rapide. (2) /tariffs-map/stations, /clusters et /nearest-radius acceptent prix_inconnu=true (« Afficher les bornes sans tarif ») : les bornes sans aucun tarif connu sont renvoyées avec no_tariff_known et un prix nul ; une borne dont le tarif est seulement écarté par un filtre (paiement, heure) n'en fait pas partie. Faux par défaut : aucune différence pour la recherche. (3) /suggest/street (mise en évidence d'une rue cherchée) : IGN en France, puis les tuiles vectorielles Kargit (mvt_street.py, couche transportation_name) dans toute l'Europe.opening_hours.py lit le format OSM opening_hours (24/7, « Mo-Fr 08:00-12:00,Mo-Fr 14:00-18:00 », listes jour par jour, off, plages passant minuit) ; tout ce qui en sort (mois, jours fériés, texte libre) est inconnu et n'affiche rien. L'état (« Accessible 24/24 », « Ouvert · ferme à… », « Fermé · ouvre… ») est calculé à l'heure LOCALE de la borne (fuseau du pays) et renvoyé dans le champ hours des fiches. Colonnes : slow_stations.hours (existait) et stations.hours + colonne calculée stations.trip_open (horaires inconnus ou 24 h / 24). Filtre « Actuellement ouvertes » (paramètre ouvertes, coché par défaut sur le site et les applis) : rapides = borne écartée si ses horaires connus la disent fermée à l'heure considérée ; lentes = écartée si une partie de la charge (branchement + durée branchée) tomberait hors des heures d'ouverture connues ; horaires inconnus : la borne reste. Trajets : seules les bornes ouvertes 24/24 (ou d'horaires inconnus) sont proposées, comme avant ; le prédicat trip_open dans la requête du planificateur attend l'accord de Jérémy (garde-fou de latence).SEO_SLOW_PAGES (variable d'environnement, ou ligne app_settings « seo_slow_pages ») : vrai en production depuis le 20/10/2026 ; faux = adresses en 404, plan du site et instantané non calculés. Les fiches de bornes lentes restent en noindex et hors du plan du site tant que seo_fiches_lentes_indexees n'est pas vrai (prévu 2 à 3 semaines après l'ouverture). Lien « Prix des bornes lentes par ville » dans le pied du site. Détails : deploy/REFERENCEMENT.md.slow_map.COUNTRY_CURRENCY donne la devise de chaque pays hors zone euro (GB, CH, PL = zł, RO = lei, DK / SE / NO / IS = kr, HU = Ft, CZ = Kč, MK = ден) ; BG, HR, GR, PT, FI, AT, LT, SI, IE sont en euros. Le serveur renvoie les prix dans leur devise (jamais convertis ni additionnés côté serveur). Nouveau champ avg_ref_kwh de /tariffs-map/slow/stations : le prix au kWh en euros au taux du jour (api/fx.py, valeur de secours sans appel réseau pendant la requête), utilisé seulement pour classer, colorer et regrouper les bulles (une zone peut mêler euros et couronnes) et trier les bornes de la voiture ; l'affichage, lui, est dans la monnaie de la personne (28/10/2026, Jérémy : « tout dans la devise sélectionnée par l'utilisateur, à défaut celle du site » ; euros sur kargit.fr, couronnes sur le site suédois) : site (kargit-slow.js) et applis convertissent le prix d'origine en euros au taux BCE du jour (/api/fx, secours intégré) puis le mettent dans la devise affichée (« Mon affichage » du profil, sinon celle du site) comme tous les autres prix. Les clients sans devises=toutes ne reçoivent pas ces bornes. Mentions de source par pays dans _attribution (AT E-Control, FI Digitraffic, LT Via Lietuva, PT MOBI.E, GR ministère grec, PL UDT / EIPA, RO E.ON Drive et Renovatio, BG Fines Charging, SI IDRO, HR HEP ELEN et Petrol, IE EZO, DK Spirii, SE et NO NOBIL). Macédoine du Nord : à ne pas publier pour l'instant.ROUTING_ENGINE=graphhopper en production ; ORS est arrêté (conteneur et données gardés pour un retour arrière). Deux graphes GraphHopper avec altitude : le principal (15 Go, profils car et car_no_toll à raccourcis précalculés, plus des landmarks pour les itinéraires à exclusions) et l'étendu (11 Go, pour les trajets qui sortent de la carte principale). Réglages de vitesse et de choix d'itinéraire dans fast.json (calibrés sur des durées Google : distance_influence 5 s/km, vitesse x 0,84 sur les pentes d'au moins 3 % et encore x 0,84 au-delà de 6 %, hors autoroute). La consommation lit, par tronçon, la limite de vitesse du pays (130 France, 120 Belgique…) et l'altitude du graphe, au lieu des 130 km/h fixes et de l'altitude empruntée à ORS. Alternatives : meilleur itinéraire plus des itinéraires forcés par des points de passage décalés. Tronçon de péage exclu : GraphHopper calcule le contournement de la zone. Repli si GraphHopper ne répond pas : serveur public OSRM (sans évitement de péage ni altitude). Les graphes se construisent sur le PC (pas assez de mémoire sur le serveur), puis se copient dans /opt/kargit/graphhopper/data-eu3d et data-ext ; tout changement des réglages de vitesse oblige à les reconstruire. Mesures du 20/10/2026 sur 310 cas avant et après : tous faisables, durée −2,9 % en moyenne (plus proche de Google en montagne et en Corse), énergie par 100 km +0,4 %, vitesse de réponse inchangée (5 allers-retours simultanés : 3,8 s). Détail : rapport de bascule.routing.DROM_BOXES, GRAPHHOPPER_DROM_URL), avec des alternatives par la méthode des tracés complets. Conteneur graphhopper_drom (profil routing_drom, 1 Go de mémoire). Trajets entre territoires ou vers la métropole : impossibles par la route, non gérés. Les bornes d'outre-mer n'ont pas encore de tarif : le planificateur les écarte, donc seuls les trajets courts (sans recharge) se calculent tant que les tarifs ne sont pas relevés. Bornes et tarifs d'outre-mer (21/10/2026, base locale, mise en production à décider) : les stations viennent du fichier IRVE national (la Guyane n'en a qu'une et Mayotte aucune : les opérateurs ne publient pas leurs sites) ; en Guadeloupe, 75 stations lentes G-MOB reçoivent le prix officiel du réseau Sy.MEG (0,58 €/kWh TTC, page gmob.fr de 2024). TVA : 8,5 % en Réunion, Guadeloupe et Martinique, 0 % en Guyane et à Mayotte ; la règle « TTC prouvé » du matin est remplacée en outre-mer par « TTC présumé » (décision de Jérémy du 21/10/2026 : « ce qui est affiché est TTC » ; confirmé par écrit par Freshmile le 27/10/2026 : tous les tarifs de l'appli sont TTC, avec une TVA de 8,5 % pour l'outre-mer) : tout prix lisible est écrit comme TTC, confiance 0,5, avec la mention « TTC présumé » (Freshmile et E.Leclerc de la Réunion par KARGIT_DROM_TTC_PRESUME=1 dans les collecteurs ; EZdrive, Car2Plug et YES55 par appliquer_tarifs_drom.py, étape de nuit « tarifs_drom »), et seul un prix écrit HT est converti, avec la TVA du territoire (Vito Saint-André : 0,45 € HT x 1,085). Les 15 bornes rapides de la Réunion (TotalEnergies, Engen, OLA) ont maintenant leur prix, saisi à la main d'après le texte du tarif Freshmile (grilles_rapides_drom.py, étape de nuit « rapides_drom ») : prix au temps seul (énergie nulle), « 0,30 €/min puis 0,40 après 30 min » = temps + frais d'occupation depuis le branchement, tranches de puissance et plages horaires ; avec eux, Saint-Denis > Saint-Pierre et > Saint-Joseph à 30 % de batterie sont faisables. Le calcul des trajets ignore encore les frais d'occupation, ne facture pas les 4 minutes de branchement et choisit la tranche de puissance sur la borne : ses totaux au temps sont sous-estimés (fiche exacte), écarts remis à Kargit. Détails : docs/bornes_lentes/METHODE.md § 10.kargit_backend) et trois répliques (kargit_backend_calc1 à 3, KARGIT_ROLE=calc). Caddy répartit seulement /api/plan, /api/trip-plan et /api/multi-plan entre les quatre, par adresse IP du visiteur (lb_policy ip_hash : un visiteur retombe toujours sur le même processus, ses limites de débit tenues en mémoire restent exactes ; une réplique qui ne répond pas est écartée automatiquement). Tout le reste (site, administration, pages de référencement, cartes, tâches de fond) va au leader seul. Le leader publie toutes les 15 s les bornes hors service dans /app/data/live_oos.json (les répliques le relisent) ; le cache de tarifs de chaque processus se resynchronise par un compteur en base (app_settings « tariff_cache_gen », relu au plus toutes les 3 s au début d'un calcul) : après un chargement de données ou une modification de tarif, lancer tariffs.invalidate_cache() (les quatre processus se rafraîchissent) ou redémarrer les quatre conteneurs, pas seulement kargit_backend. Pourquoi : un seul processus Python ne dépasse pas un cœur pour la coordination d'un calcul ; mesuré le 20/10/2026, 16 requêtes simultanées : médiane 5,4 à 7,0 s avec un processus, 1,8 à 3,2 s avec quatre (maximum 13 s contre 6,6 s). Résultats identiques sur les 310 cas de la suite. Mémoire : environ 2 Go par processus.scripts/lentes/construire_nobil.py ajoute ses stations aux fichiers de Spirii (≈ 6 000 stations suédoises et ≈ 2 800 norvégiennes, dont ≈ 900 sites mixtes ; doublons avec Spirii écartés par identifiant EVSE ou à moins de 40 m du même opérateur ; aucun prix dans NOBIL, donc « Tarif non communiqué » ; horaires non inventés) ; la même source alimente les bornes rapides (scripts/etranger/import_NOBIL.py, voir docs/bornes_lentes/NOBIL.md) ; l'Islande n'a aucune source ouverte. Disponibilité en direct de NOBIL (flux WebSocket NOBIL Real-time) : compte data.enova.no à créer par Jérémy, non branchée. Disponibilité en direct : états de Digitraffic (FI, toutes les 10 min) et de Spirii (DK, SE, NO).iles_tva_reduite_gr.py). ČEZ / futurego (Tchéquie) : tarif PDF du client non enregistré du 01/04/2026 (version tchèque de la page ; la version anglaise pointe encore vers un PDF périmé à 10 / 15 / 20 CZK), 11,90 / 16,90 / 22,90 Kč/kWh TTC selon la puissance (≤ 49, ≤ 149, ≥ 150 kW), carte bancaire (blocage de 1 500 CZK), converti en euros au taux du jour comme les autres bornes rapides tchèques ; 404 stations rapides ; la bande est celle de la puissance maximale de la station (une borne moins puissante du même site coûte moins cher). Elektro Ljubljana (Slovénie, « Gremo na elektriko ») : tarif général en vigueur depuis le 01/07/2023 (toujours affiché), TTC : AC 0,35, « EKO polnjenje » 0,22 (liste officielle de prises du 22/04/2024 : une station dont toutes les prises y sont ; une station mixte n'a pas de prix), DC 0,45 €/kWh, frais par charge entamée 0,50 (AC) / 1,00 € (DC), supplément 0,03 €/min après 180 min (AC, de 6 h à 19 h) ou 60 min (DC) ; hypothèse : tout le CPO « Elektro Ljubljana » du registre appartient à l'exploitant ; 289 lentes sur 293 et 8 rapides ; pas d'état en direct (flux DATEX II derrière un compte B2B). PowerGo (Pays-Bas) : flux AFIR public de Spirii (sans clé), prix « sans contrat » TTC 21 %, au quart d'heure : même règle que Spirii au Danemark (plus cher de chacune de quatre plages), seulement pour les stations sans prix NDW : +9 lentes, +34 rapides (13 rapides restent sans prix : le flux n'en publie aucun). Chargy (Luxembourg) : état en direct des bornes lentes lu dans le KML officiel (CC0, 5 min, état observé entre deux lectures) ; un point d'une station reçoit l'état de la k-ième prise du site (l'état de la station est exact, pas l'ordre des prises) ; le robots.txt du serveur de ce fichier dit « Disallow: / » : mise en production à confirmer. MOBI.E (Portugal) : les lignes de tarif sans type d'EDP ne sont pas un prix payé : ce sont les tarifs de l'exploitant facturés aux fournisseurs (mêmes montants que les lignes « REGULAR », bien inférieurs aux prix ad hoc : 100 % des 3 119 comparaisons) ; rien de plus n'est chargé. Road / E-Flux (Belgique) : non chargé (conditions d'usage et TVA à confirmer, bornes de particuliers « Thuis » à filtrer : road_be_filtre_prive.py prêt, non branché). Détail : docs/bornes_lentes/METHODE.md § 13.scripts/etranger/import_EnergyVision_BE.py, source be_energyvision, table lue une fois par jour, cache du fournisseur 24 h). Règle « rapide » française (regle_rapide.py) : 29 sites ont un point CCS de plus de 22,5 kW (dont 2 de 49 kW, gardés tels que déclarés) ; les sites en Type 2 seul sont des bornes lentes, laissées à la chaîne des bornes lentes. Le flux ne dit rien de l’accès (horaires, semi-public) : une station reprend celui du registre régional qui déclare la même borne (mêmes points ou même opérateur à 40 m au plus), et une borne à horaires limités n’est pas publiée. Prix : prix ad hoc du flux, écrits tels quels dans etranger.tarifs_registre (TTC et TVA 21 % dits par le flux) ; pour les bornes rapides 0,61 EUR/kWh + 0,33 EUR/min dès la 45e minute, confirmés par la page tarifs d’EnergyVision (« carte de débit », frais de rotation après 45 min) ; le prix avec la carte ou l’appli EnergyVision (0,37 EUR/kWh) n’est pas dans le flux : non chargé. Un site sans tarif, ou dont la TVA n’est pas dite, n’a pas de prix (« Tarif non communiqué »).
Prix rapides dans les tarifs (scripts/donnees_20261026_energyvision_rapides.sql, base locale seulement) : le prix ad hoc est écrit en moyen de paiement « carte bancaire sans contact » sur les stations EnergyVision déjà publiées (identifiants des registres de Flandre et de Wallonie gardés), rapprochées du site du flux le plus proche à 150 m au plus, comme la mise à jour par API des autres opérateurs (source [BE] api_energyvision, collecteur prix_energyvision_be.py) ; les 0,33 EUR/min à partir de la 45e minute sont des frais d’occupation comptés depuis le branchement ; les deux CCS de 49 et 49,6 kW (Gand, Vilvoorde) restent tels que déclarés, sans ces frais (le flux n’en publie pas pour eux). 4 stations nouvelles (Jette, Herstal, Meerhout, Genk) sont ajoutées par les règles de publication habituelles (aucune station EnergyVision à moins de 40 m).
Bornes lentes (scripts/lentes/energyvision_be.py, chaîne belge de 5 h 10 UTC) : les sites du flux sans point rapide (Type 2, 3,7 à 22 kW) sont ajoutés comme stations lentes « BE:energyvision:… » ; un site déjà présent (mêmes identifiants de point que les registres régionaux, ou station EnergyVision à moins de 30 m) n’est pas recréé, et un site dont un autre exploitant est à moins de 30 m n’est pas ajouté (l’exploitant déjà présent gagne). Prix : le tarif ad hoc du flux (TTC, TVA 21 %), seulement s’il est unique pour le site et sans prix au kWh variable ; les frais à la minute après 360 ou 1 080 minutes (de 9 h à 22 h pour les premiers) sont des frais d’occupation depuis le branchement, 2,04 EUR de frais de session pour 148 sites ; une station à prises continues n’a pas de prix lent (autre tarif en rapide). Le relevé est repris chaque nuit avec la clé gratuite ; sans clé, avec une clé expirée ou un relevé de moins de 70 % du précédent, l’ancien relevé est gardé. Disponibilité en direct des points lents : le même flux d’états que les bornes rapides, par identifiant de point, toutes les 10 minutes (un état de plus de 6 heures est inconnu).appliquer_grilles_rapides_etranger.py, Allemagne, Italie, Suisse : mêmes règles que les lentes, lignes limitées à certains jours exclues, francs suisses convertis au taux BCE du jour) ; une ligne de grille à date future ou échue n'est pas écrite ; le champ « jours » d'une grille (lundi-samedi, dimanche) devient des jours de validité. Espagne, registre REVE (carte publique du ministère, 14 754 emplacements) : lecture prête mais aucun prix écrit : la documentation officielle de l'API dit « prix hors TVA » alors que plusieurs exploitants y mettent leur prix TTC ; activation seulement par décision (variable KARGIT_REVE_TTC_PRESUME=1), courriels de confirmation prêts. Détails, avant / après par exploitant et paquets de production : docs/bornes_lentes/PRIX_MANQUANTS_PAYS_1023.md, METHODE.md § 12.publier_prod.py (qui sait désormais ne publier QUE certains opérateurs, --seulement "GB:Believ,IE:Source", sans toucher au reste ni aux prix mis à jour la nuit). France : Le Plein (bornes publiées sous l'exploitant « Yusco » dans l'IRVE, rapprochement déduit des positions), IECharge (« NW IECharge », grille unique 0,25 €/kWh appli ou carte bancaire), e-Vadea (enseigne « e-Vadea » de SPIE CityNetworks, seul réseau SPIE importé), Zunder, E-Totem (réseaux E-TOTEM et SEMOB) ajoutés aux règles de l'import IRVE ; prix par station relus chaque nuit pour Le Plein, e-Vadea et E-Totem (même prix appli et paiement sans compte : une ligne pour chacun), lignes de scripts/donnees_20261013.sql pour IECharge (générique) et Zunder (par station, sites sûrs seulement : les 12 sites tiers ou à tarif horaire n'ont pas de prix). Royaume-Uni : Applegreen Fast Charge (prix des comptes UK inconnu, noté), Arnold Clark (concessions aux horaires du garage écartées), Believ (sans contact = plafond PAYG, pris au prix PAYG), Source (TotalEnergies + SSE), Smart Charge (prix maximum publié, décision de Jérémy), ChargePlace Scotland (TVA non écrite, noté) ; Irlande (nouveau pays) : Applegreen (0,68 € appli) et Source. Belgique : IONITY (5 sites absents du registre, dont Anderlecht), Powerdot, Q8, Driveco (prix par station), Shell et Boostcharge (grilles nationales), Gabriëls et Engie Vianeo (prix incertains, notés). Luxembourg : grille Aral pulse (0,65 € appli, relue chaque nuit avec la grille allemande), Tesla Cloche d'Or. Espagne : prix sans abonnement du registre ministériel REVE, relus chaque nuit (Wenea, Zunder ; Atlante et Iberdrola : TVA non écrite, noté). Pays-Bas : LEAP24 (aussi GB, DE), Tango (prix du registre DOT-NL). Italie : Plenitude (relu chaque nuit), Enel (grille du 01/10/2026) et Powy (liste des bornes tirée de la plateforme nationale PUN par l'accès anonyme de sa carte publique). Allemagne : E.ON Drive (aussi GB, NL, IT), Mer, Vattenfall (relus chaque nuit), Autostrom plus ; Kaufland (horaires inconnus, noté) et Citywatt (« meilleur prix », noté) ; ALDI SÜD non importé (bornes aux heures du magasin). TotalEnergies (14/10/2026, Jérémy : « regarde pour TotalEnergies pour tous les pays ») : prix sans contrat (« ad hoc ») que le règlement AFIR oblige à publier dans le registre national, relus chaque nuit — Allemagne (MobiData BW / Eco-Movement, 104 stations sur 122 : 0,79 €/kWh sur les grands sites, 0,59 sur 14, bornes de concessionnaires 0,65 + 0,24 €/min après 60 min), Pays-Bas (registre DOT-NL complété par la page tarifs officielle par concession, 57 stations, 0,54 à 0,80 ; zone MRA-E « prix variables selon la borne » et Utrecht en désaccord registre / page : le plus cher, doute noté), Luxembourg (flux national, 6 stations sur 8). Prix publiés hors TVA : TVA du pays ajoutée par Kargit (19 / 21 / 8 %), écrit dans la source. Paiement : carte bancaire ou QR code sans compte. France, Belgique, Espagne : aucun prix publié dans les registres (relevés de l’appli gardés en France ; Belgique en attente de la clé Eco-Movement). Collecteurs prix_totalenergies_de/nl/lu.py. Autriche (14/10/2026, clé API E-Control de Jérémy) : toutes les bornes rapides du registre officiel du régulateur E-Control (Ladestellenverzeichnis, où chaque opérateur doit déclarer le prix ad hoc, sans contrat, de chaque point : kWh, minute, frais de démarrage et de blocage) — 2 109 sites de 61 opérateurs (EVN, Smatrics, da emobil, MOON POWER, OMV, Salzburg AG, Wien Energie, IONITY, Tesla…), en plus de Mer (source officielle Mer, gardée) ; stations qui déclarent des horaires limités écartées (787, dont 92 Lidl sur 98), bornes déclarées une à une regroupées en un site (règles « même site »), points facturés seulement à la minute écartés ; Tesla compté dans l’appli Tesla ; Wels Strom ajoute 0,50 à 1 €/min au prix au kWh sur plusieurs bornes (repris tel quel, noté) ; collecte prix_econtrol_at.py, import importer_econtrol_at.py ; prix pas encore relus chaque nuit. Pays pas encore gérés (Danemark, Suède, Tchéquie, Hongrie, Roumanie, Pologne) : E.ON Drive, Mer, Powerdot, Fastned publiés, prix convertis au taux BCE (etranger.taux_change). Abonnements de ces réseaux, frais de dépassement ou d'occupation : relevés dans les notes de l'opérateur, non modélisés. Disponibilité en direct (flux national, borne par borne) ajoutée pour Plug Inn Fast Charge, Le Plein, IECharge, Zunder et e-Vadea ; IONITY à la demande en Belgique, Allemagne, Espagne, Royaume-Uni, Luxembourg et Irlande (pays sans flux ouvert qui le couvre) : à l'ouverture de la fiche, lecture du site IONITY dans le serveur de l'appli IONITY Direct (le même que pour l'Italie, sans compte), gardée 1 min ; station Kargit rattachée au site IONITY le plus proche à moins de 300 m (liste des 881 sites relue une fois par jour, eu_live.ionity_availability) ; sans effet sur les trajets (bornes hors service non écartées) ; Luxembourg : SuperChargy (17 stations) par le fichier KML officiel de Chargy (data.public.lu), relu toutes les 5 min, changements notés par Kargit comme en Suisse (« OFFLINE » ne compte pas), eu_live.refresh_lu. 13/10/2026 (Jérémy : « pourquoi à l'étranger j'ai aussi peu de dispo ? » ; mesure : Allemagne 5 %, Italie 4 %, Espagne 2 % des bornes) : Allemagne par MobiData BW (API publique de la NVBW, Land de Bade-Wurtemberg, qui republie pour toute l'Allemagne les flux temps réel AFIR de la Mobilithek : Eco-Movement, EnBW, chargecloud, Tesla… ; CC0 / CC BY 4.0) — emplacement rattaché à la borne Kargit du même opérateur à moins de 80 m, relu en entier toutes les 6 h puis seulement les points changés toutes les 2 min (depuis le 20/10/2026, lu dans son propre fil, et rapprochement d'abord par IDENTIFIANT de point : l'EVSE-ID de nos points CCS / CHAdeMO retrouvé dans MobiData BW, puis par position et opérateur pour les autres ; les points de la borne absents de MobiData BW comptent « inconnus », la règle de la moitié des points connus s'applique comme en France ; +195 bornes allemandes suivies) ; 4 322 bornes suivies au premier essai (EWE Go, Pfalzwerke, E.ON Drive, Autostrom absents de cette source) ; Espagne à la demande, carte officielle du ministère (REVE, la même que pour les prix) : emplacement le plus proche à moins de 120 m, points sans changement depuis 24 h ignorés ; Luxembourg aussi par le flux national Eco-Movement (Aral, Q8, Lidl, TotalEnergies… ; même jeton public que le jeu data.public.lu), réseau par réseau ; Italie par la plateforme nationale PUN (accès anonyme de sa carte publique, pun.py) pour Plenitude, Powy, Ewiva et Enel : comme beaucoup de points y paraissent figés (42 % des points rapides Enel « hors service »), un point ne compte que si Kargit l'a vu changer d'état depuis moins de 24 h — jamais une panne figée affichée, la couverture se construit au fil de la journée. Plus de remise à zéro au redémarrage : l'état « dernier changement observé » (Suisse, Italie, Luxembourg, Portugal) est enregistré toutes les 5 min dans /app/data/eu_live/ et relu au démarrage (avant : six mises en prod le 13/10 = plus rien d'affiché en Suisse, en Italie et au Luxembourg avant un premier changement, parfois une heure la nuit). Autriche à la demande par le même registre E-Control (état de chaque point, emplacement le plus proche à moins de 120 m dans un rayon de 200 m, gardé 1 min, eu_live.at_availability). Irlande : ESB ecars et Applegreen en flux de fond (portails Driivz), IONITY et Circle K à la demande ; EZO à la demande depuis le 19/10/2026 (eu_live_europe.od_ezo) : à l’ouverture de la fiche, lecture de la fiche du site sur la page de paiement officielle EZO Charge Map (sans compte ; jamais les recherches « nearby-locations », limitées à ~80 appels par heure), état de chaque borne rapide, gardée 1 min ; les états « pas de communication », « hors horaires » et « pas raccordée » restent inconnus, jamais « libre » ; la plateforme n’horodate pas ses états (système central de l’exploitant). Weev (clé sur formulaire), InstaVolt (contrat limité au Royaume-Uni), Source, Tesla : aucune source publique, demandes préparées dans docs/DEMANDES_ACCES_DONNEES.md. Belgique (hors Electra, IONITY) et Royaume-Uni (hors MFG, IONITY) : clé gratuite Eco-Movement demandée par Jérémy (docs/DEMANDES_ACCES_DONNEES.md). 14/10/2026 (Jérémy : « regarder aussi pour les dispo live » ; mesure en prod : 62 % des 31 900 stations rapides avec un état en direct — Portugal 93 %, Autriche 96 %, Luxembourg 97 %, Pays-Bas 81 %, Suisse 79 %, Espagne 77 %, France 76 %, Allemagne 64 %, Italie 55 %, Royaume-Uni 18 %, Belgique 9 %) : Electra en Italie (82 stations, aucune dispo avant) par la même API publique de leur carte que dans les autres pays, à la demande : ces stations n'ayant pas d'identifiants de points dans la base (importées de la carte Electra), les points et leur type de prise sont lus dans la réponse elle-même (electra_live.QUERY_TYPES). Listes des stations suivies relues toutes les 30 min (et non plus toutes les 6 h, ni 24 h pour les positions espagnoles et autrichiennes des sources à la demande) en France (live_status.MAPPING_REFRESH_S) comme à l'étranger (eu_live.MAPPING_REFRESH_S, POSITIONS_REFRESH_S) : un serveur redémarré pendant l'import des bornes gardait 6 h une liste incomplète (Le Plein sans dispo en prod le 14/10) ; requêtes légères, dans le fil d'arrière-plan ou à l'ouverture d'une fiche, jamais pendant une recherche de trajet. Trous restants, sans source ouverte sans compte : Royaume-Uni (InstaVolt, Osprey, GRIDSERVE, Shell, bp pulse : accord, identifiants ou clé à demander ; GeniePoint publie mais refuse l'adresse du serveur), Belgique (clé Eco-Movement), Allemagne (EWE Go, Pfalzwerke, E.ON Drive : seulement sur la Mobilithek, avec un compte), Pologne (registre EIPA, compte « lecteur »), France (Freshmile et Leclerc : flux national par paquets environ toutes les heures, pas du direct ; concessions « powered by Driveco » et Izivia « OTH » absentes du flux dynamique ; E-Totem encore en observation).etranger, pas encore en production) : 10 479 stations rapides dans 23 nouveaux pays, dont 8 956 avec un prix (borne par borne ou grille nationale de l'opérateur), plus des compléments en Autriche (13 abonnements, 33 lignes de tarif réseau). Sources officielles, par pays : registres nationaux complets en Pologne (EIPA de l'UDT, carte publique : 1 412 stations, 111 opérateurs ; prix déclarés au registre, contrôlés sur les grilles officielles de GreenWay et d'ORLEN Charge), Finlande (point d'accès AFIR de Fintraffic / Digitraffic : 947), Lituanie (Via Lietuva, OCPI : 546), Grèce (registre ΜΥΦΑΗ du ministère, prix « ad hoc » AFIR : 591), Tchéquie (registre du ministère MPO, sans prix : 720) et Slovénie (point d'accès PROMETEJ : 170) ; ailleurs, cartes et pages tarifs officielles des opérateurs : Slovaquie (ZSE Drive, GreenWay, MOL Plugee : 367), Hongrie (MVM Mobiliti, MOL Plugee, TEA, OMV : 355), Croatie (HEP ELEN, Petrol, MOL Plugee, GreenWay : 177), Roumanie (Renovatio e-charge et ses sites partenaires, MOL Plugee), Bulgarie (Fines Charging, EVPoint, OMV, Wink : 400), Irlande (ESB ecars, EZO, Weev, InstaVolt, Tesla : 15 Superchargers lus le 19/10/2026 ; liste des opérateurs : registre IDRO de TII du 16/09/2026 ; 19/10/2026 : EZO passe de 168 à 187 sites — le tarif « sans mention TVA » (EZO DC Tariff 70, EZO Lidl DC Tariff) est pris au montant des lignes du tarif, celui que le système facture TVA comprise (0,763 = 0,70 × 1,09 ; Lidl 0,4992 pour « 0,50 € / kWh » écrit sur lidl.ie), jamais au montant du titre ; un tarif à frais RÉPÉTÉ toutes les 60 min (« Access fee 9,08 € / 60 min » : Supermac’s Kinnegad, Bon Secours Cork et Galway) n’est pas modélisable et le site est écarté ; ESB : les 3 sites à horaires limités restent exclus, la carte officielle est complète ; non importés faute de prix public : Circle K (≈ 20 chargeurs propres, prix sur la borne), Maxol, ePower, Fuuse, EC Charging), Danemark, Suède, Norvège (points AFIR publics de la plateforme Spirii), Islande (InstaVolt), Macédoine du Nord (EVN), Albanie (VEGA Charging), Chypre (pcharge), Bosnie-Herzégovine (Elektroprivreda BiH), Liechtenstein (GOFAST Vaduz, registre suisse de l'OFEN). Réseaux pan-européens (IONITY, Mer, Circle K, Recharge, E.ON Drive, Allego, Shell Recharge, Powerdot, Lidl, Kaufland, Neste…) : 3 981 stations dans 16 de ces pays, prix par l'API ou la carte officielle de chaque réseau. Règles : jamais un comparateur comme source (seulement pour repérer une incohérence) ; type de prise obligatoire, pas de borne privée ni à horaires limités (246 stations grecques écartées), pays contrôlé par les frontières (voir « Bornes étrangères : prise, pays et doublons ») ; prix TTC : quand la source publie hors taxes (Finlande, Grèce, une partie de la Lituanie ; en Slovénie, où le registre mélange HT et TTC, la page tarifs officielle prime), la TVA du pays est ajoutée et écrite dans la source de la ligne (K-Lataus : 0,3586 € HT + 25,5 % = 0,45 €, comme sa page officielle) ; un prix dont la source n'écrit pas la TVA n'est repris que si une page officielle le confirme (Lituanie : 46 stations chiffrées sur 546) ; prix gardés en monnaie locale et convertis en euros au taux BCE du jour (etranger.taux_change ; MKD, ALL, BAM, RSD non publiés par la BCE) ; tout ce qui vient d'une source officielle est saisi, les doutes vont dans les notes de l'opérateur ; abonnements de chaque opérateur avec leurs prix abonné et leurs pays (GreenWay Energia, PRE Charge, ZSE START, Petrol OneCharge, ESB Membership, Recharge MOVE, NRG Premium, Mer Energy, offres autrichiennes…), IONITY Motion / Power étendus aux 14 nouveaux pays où IONITY est présent ; frais à la minute notés à part pour en reparler, saisis seulement quand ils sont sûrs (blocage, frais de session). Liens d'appli : scripts/donnees_20261015_apps_europe.sql (47 applis, identifiants vérifiés sur les stores). Ce qui manque et pourquoi : Norvège et Suède hors réseaux pan-européens (registre NOBIL : clé reçue le 23/10/2026, import local du 26/10/2026 qui ajoute ≈ 1 160 stations rapides norvégiennes et ≈ 1 480 suédoises à celles des flux d'exploitants, sans prix ; pas encore en production) ; Estonie et Lettonie (ni registre ni carte lisible) ; ČEZ en Tchéquie, 409 stations sans prix (sites inaccessibles, prix dans l'appli) ; Clever, OK et Q8 au Danemark (point d'accès national avec compte) ; PPC blue et Rompetrol en Roumanie (prix et stations seulement dans l'appli), Eldrive en Roumanie et en Bulgarie (carte sans type de prise) ; Serbie, Monténégro, Kosovo, Malte (aucune liste ouverte, ou carte derrière un formulaire) ; Monaco et Andorre (bornes visibles seulement dans une appli) ; Saint-Marin et Vatican (aucune borne rapide publique) ; Tesla dans les nouveaux pays (relevé de tesla.com interrompu) ; prix publiés seulement dans une appli avec compte (liste des inscriptions dans les rapports de la campagne). Dispo en direct : sources officielles (registres PL, FI, LT, GR, SI ; cartes des opérateurs ailleurs) branchées le 15/10/2026 dans eu_live_europe.py (voir le paragraphe « dispo en direct » du 15/10/2026).import_operateur_json.py --manquantes). Prix avec appli (Lidl Plus, appli Kaufland : gratuites, paiement Lidl Pay / Kaufland Pay, donc « appli sans abonnement ») : grille nationale lue sur la page officielle de l’enseigne, relue chaque nuit (Lidl : courant alternatif 0,29, courant continu 0,44, à partir de 150 kW 0,47 € le kWh ; la FAQ de Lidl dit encore 0,48 en courant continu, seule source discordante, non retenue). Prix sans appli (carte bancaire sur la borne ou code QR) : tarif « ad hoc » AFIR de chaque prise, publié hors TVA, TVA allemande de 19 % ajoutée ; chez Kaufland, le plus cher de ce tarif et de la grille « Ad-hoc » de sa FAQ (0,55 / 0,78 à partir de 150 kW) ; des lignes génériques de la même grille servent de secours aux sites sans tarif AFIR. ALDI SÜD : prix par point lu sur l’API de la carte officielle e-ladestation.aldi-sued.de (0,44 / 0,47 € le kWh, à payer à la borne par carte bancaire ; une requête par site, une seconde d’intervalle, chaque nuit) ; 40 sites du registre absents de la carte ne sont pas publiés. ALDI Nord : aucun prix publié (affiché sur la borne seulement) et aucune source de disponibilité : stations importées en base de travail, non publiées. Les chargeurs de ces enseignes ne fonctionnent qu’aux heures d’ouverture du magasin pour une partie des sites : ils sont affichés quand même (décision du 20/10/2026), la disponibilité en direct les montre « hors service » la nuit. Disponibilité en direct : Lidl et Kaufland par le flux MobiData BW déjà lu pour toute l’Allemagne (rattachement par position et nom d’opérateur, état daté de chaque prise, rien au-delà de 24 h) ; ALDI SÜD à la demande (à l’ouverture de la fiche, une requête sur l’API de la carte officielle, état et heure de chaque point, jamais lu pendant une recherche de trajet). Mise à jour automatique : sources lidl_de, kaufland_de, aldi_sud_de de scripts/update_api_tariffs.py et grilles lidl_de, kaufland_de de grilles_officielles.py.EXCLUDED_OPERATORS dans scripts/import_irve.py).https://kargit.fr/ocpi/versions avec un jeton A secret (variable OCPI_TESLA_TOKEN_A, jamais dans le dépôt), puis les jetons B (celui de Tesla, gardé en clair en base) et C (le nôtre, gardé haché) sont échangés (table etranger.ocpi_tokens, récepteur backend/app/api/ocpi_tesla.py : rien n'est servi sans jeton, jeton A brûlé après usage, verrouillage après 10 jetons faux, aucun jeton dans les journaux). Collecte de nuit (scripts/etranger/tesla_ocpi_collecte.py, 05:10 UTC) : chaque site Tesla est rapproché d'une station « Tesla Supercharger » déjà en base à 100 m au plus (aucun doublon de fiche ni de prix) ; sans station à 100 m, une fiche est créée dans la zone de travail (source tesla_ocpi, jamais en France où la fiche vient de l'IRVE) ; site non publié, horaires limités ou sans point rapide écarté. Prix : le tarif OCPI est lu hors TVA (le taux dit par Tesla est appliqué ; sans taux dit, le prix n'est pas écrit), pour chaque heure on garde le prix du jour le plus cher (règle d'Electra), un prix lié à la consommation ou à la durée compte au plus cher ; le prix à l'heure est converti en minute (temps de charge, stationnement) ; les frais de congestion de Tesla (par minute, au-delà d'un seuil de batterie et d'affluence) sont gardés dans le registre mais pas modélisés dans les totaux ; prix « sans abonnement » de l'appli Tesla, bornes de contrôle de 0,10 à 1,50 €/kWh après conversion en euros. Pour les stations qui reçoivent un prix de l'API, le relevé manuel est remplacé. Disponibilité en direct (backend/app/eu_live_tesla.py) : lecture complète de tous les sites au démarrage puis toutes les 6 h, entre-deux lecture différentielle d'un appel (au plus toutes les 2 minutes), mêmes règles de fraîcheur que les autres flux horodatés (réseau figé depuis 1 h ou station figée depuis 24 h : rien). Détails, formulaire et déploiement : docs/TESLA_OCPI.md.scripts/add_plug_inn_tariffs.py.nom_operateur exactement « R3 » : EXACT_OPERATOR_RULES dans import_irve.py) : carte bancaire ou QR code 0,55 €/kWh, sans frais supplémentaires, tarif unique sur toutes les stations publiques (dbt.fr/comprendre-sa-recharge, relevé le 29/09/2026, concordant avec le champ tarification IRVE). La recharge normale (jusqu’à 22 kW, 0,36 €/kWh) n’est pas chargée, car Kargit ne garde que les bornes rapides. R3 n’a ni application ni abonnement : une seule ligne. Script : scripts/add_r3_tariffs.py.mail_exchanges_sync, app/mail_exchanges.py, tables mail_messages, mail_attachments). La boîte est ouverte en lecture seule (rien n'est marqué lu, déplacé, supprimé ni envoyé) et l'admin n'envoie aucun courriel. Chaque message est rattaché à un opérateur d'après le domaine de l'adresse de l'interlocuteur (expéditeur d'un message reçu, destinataires d'un message envoyé ; table modifiable operator_mail_domains, amorcée avec les contacts des demandes de données) ; un message sans correspondance est conservé sans opérateur. Affichés dans l'onglet « Échanges » de la fiche opérateur, réservé aux administrateurs ; ils n'entrent dans aucun calcul de prix ni de trajet. Texte brut seulement, pièces jointes conservées jusqu'à 10 Mo.Les états du graphe sont des (borne, niveau de batterie). Deux types d'arêtes : conduire jusqu'à une borne suivante (ou la destination) sans coût, en consommant de la batterie ; recharger à la borne courante jusqu'à un palier de 5 % plus haut, au coût du tarif retenu. Une recherche de plus court chemin (Dijkstra) trouve la combinaison la moins coûteuse selon le mode.
feasible faux, raison par trajet) et porte une erreur « Calcul impossible… » dans warnings de /plan et /trip-plan, et dans errors[i] de /multi-plan (le résultat reste renvoyé) ; si la batterie de départ est au plancher (ex. départ à 10 % avec 10 % minimum à chaque arrêt), le message le dit ; la batterie théorique à l'arrivée sans recharge est affichée à titre indicatif.Offres gratuites (15/10/2026, Jérémy : « EWE Go Ladetarif qui coûte 0 €, faut pas le considérer comme un abonnement », puis « toutes les gratuites ») : une offre à 0 € qui ne demande qu'un compte dans l'appli de l'opérateur n'est pas un abonnement ; ses prix sont des lignes « Appli, sans abo », valables pour tous (EWE Go Ladetarif, Aral pulse Klassik, EnBW Ladetarif S, Allego Smart, E.ON Drive Public Lite au Danemark, OMV eMotion Premium en Hongrie). Restent des abonnements les offres gratuites à condition d'accès (client énergie PRE, compte fidélité MOL, clients Renault Plug Inn, Tesla réservé). Migration db/migrations/20261015_offres_gratuites.sql (abonnements archivés dans subscriptions_archive, clics « S'abonner » dans subscribe_clicks_archive) ; sources de nuit corrigées (grilles_officielles.py, maj_grille_allego.py). Une ligne propre à une borne prime toujours sur la ligne générique du même moyen de paiement (Allego en Italie : prix par borne de l'API, sinon grille Smart).
Dijkstra minimise le coût par session : le frais mensuel d'un abonnement n'apparaît qu'une fois, au total. Pour ne pas rater un abonnement rentable sur l'ensemble du trajet, on compare aussi des recherches « avec cet abonnement gratuit » et « sans aucun abonnement », puis on garde le meilleur total (frais compris, cashback ignoré par défaut : un abonnement n'est recommandé que si l'économie sur le prix des recharges dépasse son frais, par exemple Atlante Go n'est plus retenu sur un trajet isolé quand seul son crédit futur le rendait rentable ; avec l'option « Prise en compte du cashback » cochée, le total de classement — dont 50 % du cashback est déduit — sert à ces comparaisons, mais le total affiché reste le coût réellement payé : Atlante Go redevient alors choisi sur Paris → Marseille et Lille → Nice). Pour limiter le nombre de recherches complètes :
MAX_TESTABLE_SUBSCRIPTIONS). Compromis explicite : un abonnement au-delà du top 5 pourrait, très rarement, être le vrai gagnant sans être testé. Effet constaté : sur 70 voyages tests, 2 résultats changent, tous deux en mieux (Paris ↔ Strasbourg : 55,80 € → 51,26 € avec Electra+ Smart, jamais testé dans le top 3).update_electra_tariffs.py et juste après chaque publication de l'étranger), Fastned Gold (BE CH DE ES GB IT NL), Allego (BE DE IT NL), Atlante Go (tarifs réduits en France, Italie et Espagne seulement), Enel PlugAndGo (bornes Enel X et Ewiva), Zunder Easy / Pro (aussi en France). Le cashback n'est jamais compté.Quand l'interface calcule au moins deux trajets en mode « le moins cher » (POST /api/multi-plan), le forfait d'un abonnement est compté une seule fois par mois pour l'ensemble des trajets, et non en entier à chaque trajet : Atlante Go (9,99 €/mois) perd sur chaque trajet pris seul mais peut gagner sur la somme. La fréquence de chaque trajet (« N fois par mois ») et le cashback pondéré (coût de classement, voir section 6) sont déjà dans le coût mensuel de chaque calcul ; le choix minimise le coût de classement total, les montants affichés restent ce que l'on paie.
subscriptions_required garde le vrai forfait, comme un calcul seul, à dédoublonner par identifiant) ; pooled_subscriptions (forfaits comptés une fois) ; totaux mutualisés et « trajets seuls » pour mesurer l'apport. Une erreur sur un trajet (adresse introuvable...) ne fait pas échouer les autres (results[i] vide, errors[i] renseigné).Vitesse (serveur OVH, aller-retour de ≈ 500 km par sens) : 2 trajets 4,5 s, 5 trajets 7,5 s (24 à 65 calculs en parallèle) ; c'est ce que l'interface mettait déjà en calculant les trajets l'un après l'autre. Cinq utilisateurs lançant en même temps 2 trajets : 9,7 à 11,5 s au pire.
/api/plan) : il répond « impossible » aussitôt, avec required_start_percent, la batterie de départ nécessaire -- même condition que la recherche (départ − pct_at(d) − 2 × détour × avg_rate ≥ seuil, même profil d'énergie au départ réel, même liste de bornes), calculée sur chaque tracé, coupure seulement si tous sont hors d'atteinte et la destination aussi (planner/graph.required_start_percent). L'interface relance alors le trajet précédent avec une batterie minimale à l'arrivée de cette valeur + 1 point (une recharge de plus ou plus longue avant Cadzand), puis le trajet suivant ; une seule tentative, l'erreur d'origine reste si le trajet précédent ne peut pas arriver plus chargé. Coût : la réponse « impossible » en ~1 s au lieu de ~4 s, plus un recalcul du trajet précédent (~2 s), seulement dans ce cas. Exemple (prod, Mégane 60) : Rennes → Cadzand arrive à 21,7 % au lieu de 10,4 % (+2,31 €)./api/plan) : si aucun plan n'est possible sur le tracé le plus rapide, les autres tracés du routeur sont essayés dans l'ordre et le premier faisable est retenu (coût : une recherche par tracé essayé, seulement quand la réponse serait sinon « impossible »). Aller-retour et étapes (/api/trip-plan) : si le tracé le plus rapide a plus de 150 km sans borne candidate (MAX_STATION_GAP_KM), le premier autre tracé sans un tel trou est pris (jamais en France, où les bornes sont bien plus rapprochées). Pas de changement quand l'utilisateur a choisi lui-même un tracé.toll_tariffs : prix pour une paire de gares.Tollways ajouté à ors-config.yml ; GraphHopper : détail « toll »). Le graphe ORS a été reconstruit le 20 septembre 2026 (environ 40 min ; une première tentative le 19 avait été interrompue par la mise en veille du PC : la reconstruction doit se faire PC éveillé). L'ancien graphe est conservé dans J:\kargit-ors-data\graphs\car.bak-20260920 (retour arrière : le remettre sous le nom car et restaurer ors-config.yml.bak-20260919). Un trou estimé n'est facturé que sur ses kilomètres payants : l'A47 gratuite entre Saint-Étienne et Givors était comptée ≈ 8,5 € sur Rennes → Nice (131,99 € → 124,10 € ; Mappy annonce 115 €). Sans profil (routeur ne le fournissant pas), la distance entière est estimée comme avant. Aucun coût de calcul mesurable (ORS ≈ 70 ms avec ou sans) ; l'évitement des péages fonctionne toujours (0 km payant sur l'itinéraire « sans péage »)._pair_plausible : distance trop longue tolérée si l’une des gares est une « BARRIÈRE ») ; la portion ainsi payée d’avance (du côté de la barrière, jusqu’à la distance tarifaire moins 5 km) ne se paie pas une seconde fois : ni les barrières fixes qui s’y trouvent (Dordives à 300 m de la sortie, Le Tourneau sur l’A77), ni une estimation (l’A77 près de Gondreville). Myennes, au-delà, reste due. Sur les 20 trajets de test, seul Annecy → Grenoble change : 13,02 € estimés → 12,30 € exacts (Annecy Centre → Chambéry Nord 5,60 € + Chignin barrière → Crolles barrière 6,70 €).toll_gares.cap_payant (degrés, 0 = nord ; NULL = les deux sens). La gare n’est retenue que si le trajet la traverse à ±90° de ce cap (tolls._heading_matches, direction du tracé entre 200 m avant et après la gare).toll_mandatory, affiché « péage obl. ») au lieu d’une erreur 400 (routing._mandatory_toll_routes).pack_exclusion) : menu « ⋯ » sur chaque ligne de borne et de péage du détail, confirmation, puis recalcul du seul trajet concerné. Borne exclue : excluded_station_ids (déjà existant). Tronçon exclu : excluded_toll_zones (/api/plan, /api/trip-plan, chaque trajet de /api/multi-plan) = 4 points pris par le site sur le tracé réel du tronçon (à 20, 40, 60 et 80 % entre ses gares, 1 point pour une barrière) ; le routeur (ORS) évite un petit carré de 300 m autour de chacun (geo.points_avoid_polygon, au plus 24 carrés), donc l’autoroute à ces endroits sans couper les routes voisines, contrairement à l’ancien couloir en ligne droite entre les gares. Dans ce cas ORS quitte son mode précalculé : 2 à 3 s par itinéraire en prod (13 à 21 s sur le PC le 29/09 : Rennes → Paris, Paris → Lyon), GraphHopper n’est pas interrogé (il ne sait pas éviter une zone ; essayé avec des zones interdites : aussi lent et contournement incorrect). Depuis le 29/09 au soir, les itinéraires habituels (précalculés, instantanés) sont essayés d’abord : si l’un d’eux ne traverse aucune zone, il est retenu sans calcul lent (routing._routes_avoiding) ; sinon ORS calcule avec un délai porté de 30 à 120 s (avant : Rennes → Nice et Lille → Marseille finissaient en erreur 400 « timed out » sur le PC). Mesures du calcul lent avec 4 zones au milieu du trajet : prod 24,6 s Rennes → Nice, 18,1 s Lille → Marseille ; PC 30 s et plus. Avec de simples carrés, GraphHopper les contournait de justesse (+1,2 km sur Paris → Lyon, en restant sur le tronçon). Depuis le 29/09 au soir (accord de Jérémy), étape intermédiaire GraphHopper (routing._gh_route_excluding) : sur le tracé habituel qui passe par le tronçon, un couloir de 250 m de part et d’autre (un rectangle par km) va d’un bout à l’autre du tronçon ; la requête « car » y interdit les routes à péage et les autoroutes (in_excl && (toll == ALL || road_class == MOTORWAY), priorité 0), les routes locales qui le traversent restent permises. Préparation « landmarks » ajoutée au profil car (profiles_lm de gh-config.yml, 22 min sur le PC pour la France ; étape activée seulement si GRAPHHOPPER_LM_EXCLUSION=true ; en prod depuis le 30/09/2026 : landmarks du graphe Europe préparés en 31 min sur une copie puis bascule, GraphHopper à 22 Go de mémoire ; exclusion mesurée en prod 1,5 s Paris → Lyon, 2,9 s Lille → Marseille, contre 18 à 25 s avec ORS sur les longs trajets) pour que cette requête sans raccourcis CH reste rapide ; les recherches habituelles restent en CH, inchangées. ORS n’est plus qu’un dernier recours (GraphHopper absent ou sans résultat, ou mode sans péage). Un seul calcul lent par trajet (le retour reprend l’aller à l’envers) ; depuis le 29/09 ces itinéraires sont gardés en mémoire (200 au plus, routing._avoid_cache) et deux requêtes identiques simultanées n’en lancent qu’un (les modes « le moins cher » et « le plus rapide » du même recalcul, un changement de mode ensuite). Exemple : Paris → Lyon sans le tronçon Fleury-en-Bière → Villefranche-Limas passe par l’A77 et l’A79 (499 km au lieu de 461, péage 28,67 € au lieu de 41,30 €). Exclusions propres au trajet, gardées dans l’adresse de la page et dans les favoris, oubliées après « Modifier mes trajets ou options » + nouveau calcul. Plusieurs exclusions de suite : chaque tronçon est cherché sur les itinéraires habituels, puis sur celui obtenu après les exclusions déjà posées. Un trajet avec une étape imposée garde son étape : le couloir exclu s’applique aussi à l’itinéraire passant par elle (29/10/2026). Au passage, les gares d’un tronçon exact sont affichées dans le sens du trajet (la grille étant symétrique, Paris → Lyon affichait « Villefranche-Limas → Fleury-en-Bière »).scripts/fix_aprr_gare_names.py (simulation par défaut, --vraiment ; --gares --vraiment pour les gares). Restent sans gare : Amboise, Châlons La Veuve / Mourmelon, Fontenay-sur-Loing, Gidy, Gondreville A77, « La Folie-B/Paris », Reims Sud, Savigny, Saint-Germain-les-Vergne, Saint-Martin-Bellevue, Saint-Michel-de-Maurienne, Isle-d'Abeau Centre, Tours-Monnaie. A79 (ALIAE, Montmarault → Digoin, flux libre) importée le 29/09/2026 : la grille officielle (data/tolls/ALIAE-A79-Tarifs-2026.pdf, page 3, tableaux en image relevés à la main) donne les prix classe 1 entre ses 9 points (extrémités, échangeurs du Montet, de Montbeugny et de Molinet, bords des deux sections gratuites), modélisés comme un système fermé : 9 gares aux positions OpenStreetMap des portiques et échangeurs, 21 paires. Traversée complète 4,20 € (= portiques « Transit » 1,00 + 1,90 + 1,30 de la page 2). Tarif de base ; le tarif réduit des véhicules très faible émission (Crit'air 0, 3,20 € la traversée) n'est pas appliqué, comme ailleurs. Script : scripts/import_toll_a79.py. Jonction entre réseaux : un trou de moins de 5 km entre gares d'exploitants différents sans paire commune (fin de l'A79 à 2 km de la barrière APRR de Deux-Chaises) n'est pas estimé. Kembs → Limoges : 86,20 € avant ces corrections, 31,30 € en tarifs exacts (Fontaine-Larivière 3,10 + Saint-Maurice → Chalon Sud 20,70 + A79 4,20 + Deux-Chaises → Montluçon 3,30).scripts/import_toll_cofiroute.py : 3 236 paires ajoutées, 0 écart sur les 7 178 déjà en base, barrière d'Ancenis (A11) à prix fixe 10,40 €, alias vérifiés à la main (aucun rapprochement par préfixe) ; (2) alias faux de la grille ASF « maille » corrigés (fix_toll_vinci_aliases.py) : « La Monnaie » est l'échangeur de l'A87 à Angers, pas la barrière de Monnaie (A10) — 72 tarifs faux retirés —, « Péage de Tours centre » est la barrière de Sorigny — 38 retirés —, bloc A63 Biriatou → Saint-Geours lu, 224 paires ASF en attente des gares APRR importées ; (3) traversées gratuites à 0 € justifiées une à une (import_toll_free_crossings.py : Tours Monnaie ↔ Sorigny, Condrieu → Vienne, Aix Lançon ↔ Le Canet, Neuillé → Saint-Christophe, Angers Corzé ↔ Saint-Jean-de-Linières ; la traversée de Lyon par l'A46/A432 écartée, elle aurait fait disparaître Quincieux, Genay et Mionnay). Audit de 174 trajets : tronçons affichés « ≈ » 28 → 2, part estimée 176,59 € → 45,36 € ; recoupé avec les liaisons officielles Vinci 2026 (Paris–Nantes 43,30, Paris–Tours 26,00, Paris–Poitiers-sud 40,90, Nantes–Angers 10,40, Lyon–Marseille 28,10). Restent estimées : gares de bretelle en système ouvert (Cagnes, Senlis, Arles, Capbreton, souvent moins de 1 €) et Tours-Centre → Neuillé.tolls._pair_plausible) : ce contrôle évite une surfacturation, impossible à 0 € (Corzé → Saint-Jean-de-Linières, contournement d'Angers, n'est plus estimé : Paris → Nantes 43,30 €, Le Mans → Nantes 20,60 €) ; (2) gares de bretelle (toll_gares.bretelle, migration 20261014_toll_gares_bretelle.sql, liste justifiée gare par gare dans scripts/mark_toll_bretelles.py, toutes « Ech » échangeur dans le référentiel du ministère) : péages d'échangeur que seuls paient ceux qui entrent ou sortent là — Saint-Witz N°7 (1,00), Senlis-Bonsecours (2,20, désormais tarifé), La Neuvillette N°15 (0,90), Vallée de la Hem N°2 (1,30), Audomarois, Capbreton, Genay (1,70) et Mionnay (1,00) sur l'A46 nord, Chesnes (2,30) sur l'A43, Chambourcy (2,60, sortie 6a de l'A14). Une telle gare n'est comptée que si le trajet prend la bretelle : passage payant ↔ gratuit au droit de la gare d'après le profil péage du routeur, ou début / fin du trajet à moins de 15 km avec un tracé qui passe sur la gare (à moins de 100 m). Sinon elle est ignorée et le chaînage tombe sur la vraie paire voisine : Paris → Lille 18,90 € (au lieu de 20,14 : Saint-Witz facturé au trafic direct), Paris → Rouen / Caen −2,60 (Chambourcy), Paris → Calais 25,20, Lyon → Grenoble 13,70, Lyon → Paris par l'A46 −2,70 ; Paris → Senlis passe de 1,00 à 2,20 € (cellule « Paris / Roissy (péage de Chamant) ↔ Senlis N°8 » de la grille Sanef). Nouvelles traversées à 0 € : Arles ↔ Saint-Martin-de-Crau (A54, liaison Vinci « Arles / Marseille-Aix » 5,20) et Courcy ↔ Thillois (traversée de Reims, A26 / A4). Non marquées exprès : Cagnes et Sophia (A8, au-delà de la barrière d'Antibes : l'estimation actuelle approche le vrai coût de cette barrière), Antibes, Ondres et Grans (vrais billets), Dordives et Le Tourneau (A77 : le trafic direct paie les trois barrières). Audit de 174 trajets avec profil péage : tronçons estimés 35 → 24, part estimée 45,36 → 36,99 €, un seul « ≈ » restant (Tours-Centre → Neuillé) ; sans profil : tronçons estimés distincts 52 → 33 ; temps de calcul inchangé (aucune requête de plus).backend/app/tolls_foreign.py, schéma etranger, relevés officiels scripts/etranger/peages/<PAYS>.json ; 10 pays voisins en production depuis le 29/09/2026). Trois mécanismes : passages payants (tunnel, pont, barrière de système ouvert : payés quand le tracé passe à moins de 80 m des deux points A et B de l'ouvrage ; « un seul sens » : seulement de A vers B), prix officiels par couple de gares (Espagne, Italie ; gares à moins de 300 m, couple le plus long), tarif au km (Italie sans couple officiel, marqué ≈). Serbie (15/10/2026, décision de Jérémy) : la grille officielle ne donne de prix que depuis 6 gares d'origine ; quand le tracé continue sur la même section fermée au-delà de la dernière gare tarifée (Beograd → Aleksinac, puis Niš sever), le prix officiel le plus long est gardé et marqué ≈. Montants convertis en euros au taux du jour (etranger.taux_change). 15/10/2026 (base locale seulement, pas en production) : 28 nouveaux pays relevés (Autriche, Norvège, Suède, Danemark, Islande, Pologne, Croatie, Serbie, Grèce, Macédoine du Nord, Monténégro, Irlande… ; vignettes de tous les pays) ; points et gares projetés sur les segments du tracé (grille de cellules) et plus seulement sur ses sommets ; rayon propre à certains points (barrières de bretelle grecques 8 m, bomstasjoner norvégiennes 40 m : une rampe parallèle n'est plus comptée) ; règle horaire norvégienne (un groupe de barrières payé une fois par fenêtre de 60 min, lue comme 60 km de trajet) ; échantillons du tarif au km limités aux cellules de 0,1° proches d'une autoroute italienne (etranger.peages_km_cellules). Vignettes jamais ajoutées au coût (décision de Jérémy du 15/10/2026 : alerte « vignette nécessaire » à l'affichage, table etranger.vignettes). Site (15/10/2026, pas en production) : au moment des résultats, une fenêtre par pays à vignette emprunté (prix, durées et lien d'achat de GET /api/vignettes), « Éviter ces routes » relance le calcul avec avoid_vignettes (jamais le pays de départ ou d'arrivée, rien pour la Suisse sur kargit.ch), choix mémorisable (vignette_choices) ; puis la fenêtre Norvège « êtes-vous enregistré ? » (autopass, autopass_choice) ; une ligne par pays sous les résultats (« Vignette évitée / possédée », « Péages Norvège »). Réductions pour voitures électriques liées à une inscription (Italie, Espagne) non comptées. Norvège (15/10/2026, base locale) : prix selon la réponse à « êtes-vous enregistré ? » (paramètre autopass) — non enregistré : tarif le plus haut ; Epass24 : aucune réduction sauf tarif électrique d'Oslo, Bergen et Kristiansand ; badge AutoPASS : tarif électrique « avec contrat » de la grille du projet (colonnes prix_ve_*) ; station sans grille relevée : tarif plein marqué ≈. Tarif plein : le plus récent de la NVDB et de la grille 2026 de la société de péage (45 stations corrigées le 15/10/2026 : Oslo, Nedre Glomma, E134 Buskerud, Bodø, Kleggerud). Deux stations côte à côte sur deux routes parallèles (ancienne et nouvelle E6 à Forsmo) : seule la plus proche du tracé est payée. Relevé manuel du 15/10/2026 au soir (base locale) : Albanie (barrière de Kalimash, 5 €), Bosnie-Herzégovine (A1 FBiH et autoroutes de la Republika Srpska, prix par couple en KM ; barrière de Svilaj), Grèce (Moreas, Egnatia aux tarifs 2026), Croatie (AZM et Bina Istra, prix par couple), pont du Danube Ruse → Giurgiu (2 €, sens unique) ; rovinieta roumaine en lei depuis le 1/10/2026. Temps ajouté : 0 à 1 ms pour un trajet français, 5 à 35 ms pour un trajet étranger courant, 50 à 110 ms pour 1 400 à 2 000 km, environ 250 à 280 ms pour Paris → Athènes (2 900 km, mesure du 15/10/2026 sur une machine chargée), par itinéraire.peages_maj_updater (scripts/peages_maj/, profil Docker à part : jamais démarré par un déploiement), conception dans docs/PEAGES_MAJ_TRIMESTRIELLE.md. Rien ne change dans le calcul des péages (tolls.py, tolls_foreign.py) : seules les valeurs des tables sont mises à jour. Sept collecteurs lisent une source structurée et comparent chaque prix à la base : taux de change (etranger.taux_change, BCE et banques centrales, via app/api/fx.py ; un taux qui varie de plus de 10 % est refusé), APRR et AREA (toll_tariffs, jeu data.gouv.fr « Tarifs autoroutes APRR » lu par l'API tabulaire : un nouveau barème = une nouvelle ressource du jeu), Cofiroute et Arcour (fichier .txt officiel « Tarifs Cofiroute / Arcour » publié par VINCI Autoroutes, équivalent lisible du guide PDF ; une ligne atteinte par deux prix différents est écartée, jamais devinée), Escota (guide tarifaire PDF de VINCI Autoroutes, tableau « classe 1 » lu avec l'analyse de import_toll_vinci.py), A'Liénor (A65) (PDF officiel du concessionnaire, lien lu sur sa page), HAC (Croatie, 2 482 couples, pages officielles hac.hr) et NVDB (Norvège, 440 stations ; une station corrigée à la main d'après la grille de sa société de péage n'est jamais écrasée). Les prix qui changent ne sont appliqués qu'après les garde-fous : variation d'une ligne au-delà de ±15 % refusée ; source tronquée (moins de 90 % des lignes de la base retrouvées) ou suspecte (plus de 30 % des lignes qui changent alors que le millésime est le même ; pour un nouveau millésime : variation moyenne hors de −2 % / +8 %, règle validée par Jérémy le 22/10/2026 : la hausse annuelle modifie presque toutes les lignes) = rien d'appliqué et voyant rouge ; copie COPY des tables avant toute écriture, journal ancien → nouveau (etranger.peages_maj_changements), retour arrière complet possible (aucun cache de prix de péage à invalider : les tables sont relues à chaque calcul). Toutes les autres sources (Espagne, Italie, Royaume-Uni, Irlande, Grèce, Pologne, Serbie, vignettes, ASF, ATMB…) sont relues (page ou PDF, 1 requête par seconde, robots.txt respecté, rien de contourné) : si les chiffres de la page changent, si un prix du relevé n'y figure plus ou si l'adresse a disparu, la source passe « à revoir » (voyant orange) avec la différence ; aucun prix n'est deviné ni modifié. Restent manuels (page ou fichier surveillé, rappel orange, rien de modifié tout seul) : ASF (grille PDF « maillée » aux libellés inclinés, dont 4 triangles relevés à la main ; son fichier .txt ne couvre que 28 % de la base avec des noms de gares ambigus), Sanef / SAPN (sanef.com est derrière Cloudflare : jamais contourné) et les petits concessionnaires de l'arrêté du Journal officiel (ATMB, SFTRF, ALIS, Atlandes…, tableaux ressaisis). Pour ceux-là, les importeurs import_toll_sanef.py et import_toll_decree.py ont une option --maj-prix qui met à jour les prix EXISTANTS (avant, ils n'ajoutaient que les paires manquantes) avec les mêmes garde-fous, la même sauvegarde COPY et le même retour arrière. Le rappel « barème de l'année pas chargé » devient orange à partir du 15 mars (deux semaines après le premier passage de l'année, le 1er mars : ce qui manque encore est manuel ou pas encore publié). Les vignettes ne sont toujours jamais ajoutées au coût.tolls.py ne change, seules des données. (1) A20 Montauban → Brive : le triangle de la page 5 de la grille ASF « maille » (écarté jusqu'ici, une étiquette débordait) est lu avec ses libellés relevés à la main (import_toll_vinci.py, MANUAL_TRIANGLES) : 33 paires officielles (Caussade, Cahors, Labastide-Murat, Souillac, Gignac…), la ligne « indicative » Gignac → Montauban nord (12,10 €) est remplacée par 15,90 € ; Toulouse ↔ Brive passe de 15,30 à 19,10 €, Toulouse → Paris de 37,10 à 40,90 €. (2) Traversées à 0 € (import_toll_free_crossings.py, justification officielle écrite à côté de chaque ligne) : jonction de Brive (Saint-Germain-les-Vergnes ↔ Mansac-Terrasson et Gignac : Bordeaux/Clermont 42,70 € = 20,20 + 22,50 dans les liaisons Vinci 2026), échangeur de Tours-Centre ↔ barrières de Monnaie, Sorigny et Saint-Christophe (Paris/Tours-centre 26,00 € = prix de la barrière de Monnaie ; les péages de bretelle de Tours-Centre restent pour ceux qui y entrent ou sortent). Audit de 174 trajets avec profil péage : trajets avec tronçon estimé 27 → 18 ; 12 trajets modifiés, tous attendus (Tours → Paris 27,90 → 26,00 € = liaison Vinci ; Lyon ↔ Bordeaux 60,89 → 58,80 € pour 59,00 € côté Vinci). (Résolu le 22/10/2026, voir la passe suivante.) Restaient estimés, faute de source officielle sûre : traversée de Lyon (Villefranche-Limas ↔ Condrieu, ≈ 2,5 €), sorties de Cagnes / Sophia et La Barque (A8, ≈ 3 €), Saint-Saëns ↔ Aumale (A28, ≈ 2,1 €), Amiens Dury ↔ Jules Verne (≈ 0,5 €), A64 Muret ↔ Martres-Tolosane (≈ 0,4 €), Chatuzange ↔ Valence sud (≈ 0,2 €).source de chaque ligne. Sur 144 trajets (87 paires de l'audit et 60 trajets autour de Lyon, deux sens) : 66 → 98 trajets sur 122 comparables à moins de 0,30 € du simulateur ; trajets de l'audit avec tronçon estimé 17 → 0 (mêmes tracés rejoués). (1) Règle de tolls.py (_segments_for) : une ligne à 0 € (« aucun billet entre ces deux gares ») n'est plus utilisée quand une barrière à prix fixe se trouve entre ses deux gares : le trajet par l'A46 garde Quincieux (3,60 €), Genay (1,70 €) et Mionnay (1,00 €), le trajet par la ville (A6 → A7) paie 0 € entre Villefranche-Limas et Condrieu (Paris → Marseille 69,40 € = 41,30 + 28,10 au simulateur). Coût mesuré sur 292 trajets mis en cache : aucune différence mesurable (médiane ≈ 34 ms avec et sans la règle), les 292 résultats sont identiques avec la règle seule. (2) Traversées à 0 € prouvées par le simulateur (import_toll_free_crossings.py) : Villefranche-Limas ↔ Condrieu, contournement est de Lyon (Beynost, Saint-Priest ↔ Condrieu), Veauchette ↔ Condrieu (A47 gratuite), Crolles ↔ Voreppe, Chatuzange ↔ Valence sud, Saint-Saëns ↔ Aumale (Neufchâtel → Dury 6,80 € = Haudricourt → Dury : la section A28 entre Saint-Saëns et Neufchâtel est libre), Amiens Dury ↔ Jules Verne ; la traversée Lançon ↔ Le Canet de Meyreuil à 0 € (14/10) est supprimée (voir 4). (3) A64, barrière de Muret à prix fixe 1,80 € (import_toll_fixed_sim.py : Toulouse → Muret 1,80 ; → Martres-Tolosane 1,80 ; → Pau 13,80 = 1,80 + 12,00). (4) Grille Escota : colonnes mal nommées, corrigées : « Aix (A51) », « La Cadière », « Villeneuve-Loubet/Cagnes-ouest » et « Nice Aéroport » ont une ligne mais pas de colonne dans le triangle du guide, et l'importeur nommait les colonnes dans l'ordre de tous les libellés (décalage de 1 à 4) : le « A8 Aix / Nice 21,20 » du guide était lu « Pas-de-Trets → Nice-ouest », Fréjus était lu Le Muy, Cannes Fréjus. Corrigé dans import_toll_vinci.py (NO_COLUMN_LABELS) ; 18 relevés indépendants (2 liaisons du guide, 16 du simulateur) tombent juste au dixième. scripts/fix_toll_escota_a8.py remplace les lignes Escota fausses (345), ajoute celles de Lançon, Le Canet de Meyreuil et La Barque (ligne « Aix » ; Lyon → Nice 43,67 → 49,30 €, Paris → Nice 87,38 → 90,60 €, Marseille → Nice 16,57 → 21,20 €) et des gares Cagnes Est / Cagnes Ouest (qui tiennent lieu des sorties de Saint-Laurent / Nice-ouest, sans gare dans toll_gares). Restent estimés ou approximatifs : trajets qui partent entre Mandelieu et Antibes (gares sans péage absentes de toll_gares : Cannes → Nice 0,80 € chez Kargit, 3,50 € au simulateur), contournement ouest de Lyon (Tarare, Balbigny, Saint-Quentin-Fallavier), Toulouse ↔ Dijon (ligne ASF « maille » cumulée Saint-Germain-les-Vergnes → Dijon sud 93,20 € prise pour un autre itinéraire : 120,77 € affichés contre 53,50 € au simulateur). (Tout cela est résolu le 23/10/2026, voir la passe suivante.)scripts/fix_toll_vinci_cumulative.py, 2 506 lignes) : la grille ASF « maille » (pages 8 et 9) et le barème APRR contiennent des prix entre l'A89 / A20 / A71 d'Auvergne et l'A6 / A31 / A40 (Dijon, Chalon, Mâcon, Besançon, Genève) qui sont ceux d'un trajet en boucle par le nord (A71, Bourges, Orléans, A19 : 600 à 800 km tarifaires pour 150 à 350 km à vol d'oiseau), pas du trajet réel par l'A79 ou par Lyon, et que tolls.py applique dès que le trajet croise leurs deux gares. Saint-Germain-les-Vergnes ↔ Dijon sud 93,20 = Saint-Germain ↔ Manzat 18,00 + Manzat ↔ Dijon sud 75,00 (APRR, 616 km pour 290). Toulouse → Dijon 120,77 € → 53,30 € (simulateur 53,50) = 19,10 + 18,00 + 6,10 + 4,20 (A79) + 5,90 ; Brive → Dijon 93,20 → 34,20 (34,40) ; Bordeaux → Dijon 113,40 → 54,40 (54,60) ; Brive → Chalon 93,00 → 28,30 (28,50) ; Tulle / Ussel → Dijon 90,80 / 82,80 → 32,00 / 24,00 (32,00 / 24,00) ; Limoges → Lyon et Bordeaux → Lyon inchangés (38,60 / 58,80 pour 38,80 / 59,00). Trois contrôles : R1a lignes APRR dont la distance tarifaire dépasse 1,8 fois la distance à vol d'oiseau (≥ 100 km) et qui touchent une gare « barrière » (le contrôle de distance de _pair_plausible les laissait passer : Clermont-Barrière → Dijon sud 78,10) ; R1b lignes Vinci sans distance égales à la somme de deux lignes via une gare commune dont l'une est un tel détour ; R2 lignes ASF au-dessus de 0,25 € par km à vol d'oiseau (≥ 100 km) et Cofiroute au-dessus de 0,22 (billet via Troyes pour Orléans → Reims 51,30 € ; Orléans / Bourges / Tours / Limoges → Reims 53,70 / 66,70 / 67,40 / 62,50 → 24,60 / 37,20 / 38,60 / 34,40 au simulateur : A10 + A4 par Paris). Les liaisons réellement parcourues restent chaînées à partir des gares voisines, dont les prix directs sont gardés : retirer une ligne légitime ne coûte que l'arrondi (0,1 à 0,3 €), en garder une fausse 10 à 70 €, d'où des seuils bas (taux légitimes mesurés sur 179 tronçons validés : au plus 0,20 € par km parcouru). Les importeurs import_toll_vinci.py (ASF) et import_toll_cofiroute.py refusent maintenant les mêmes lignes ; relancer le script après tout import de grille. (2) Deux changements de tolls.py, hors boucle de recherche : (a) la marge d'affinage des gares sur le tracé était fixe (1,5 km) alors que les points d'un tracé sont très inégalement espacés (1 point échantillonné sur 19 peut être à 10 km du suivant) : Gignac, à 37 m du tracé mais à 1,89 km du point échantillonné le plus proche, n'était jamais retenue et Paris → Toulouse perdait 15,90 € d'A20 selon le tracé (25,00 € au lieu de 40,90 €, simulateur 40,90) ; la marge suit maintenant l'écart local entre échantillons (au moins 1,5 km). 4 trajets sur 394 changent : Paris → Toulouse 25,00 → 40,90, Limoges → Dijon 13,30 → 13,40 (13,40), Limoges → Strasbourg 31,20 → 31,30 (31,30), Moulins → Marseille 36,40 → 37,30 (32,60 : autre tracé et contournement de Lyon). (b) une ligne de 3 € ou moins n'est plus soumise au contrôle « moitié de km payants » (PAIR_SMALL_PRICE_EUR) : le profil péage d'OpenStreetMap ne marque pas payants les kilomètres des rocades de Lyon (41 % seulement entre Tarare Est et Saint-Quentin-Fallavier), et le contrôle protège d'une surfacturation, pas de 2 ou 3 €. Aucune différence mesurable de temps de calcul (voir le rapport de la passe). (3) A8 entre Fréjus et Antibes : les échangeurs 40 (Mandelieu), 41 (La Bocca) et 42 (Mougins) manquaient dans toll_gares et dans OpenStreetMap (nœuds sans nom, numéro de sortie seul) : ajoutés avec les coordonnées des nœuds highway=motorway_junction ; leurs prix sont la ligne « Mandelieu » de la grille Escota, confirmée par une quarantaine de relevés (Cannes / Mandelieu / Mougins → Nice 3,50 ; → Antibes 1,70 ; → Menton 8,40 ; Fréjus → Cannes 3,00 ; Cannes → Brignoles 12,00 ; Toulon 13,60 ; Aix 17,70). fix_toll_escota_a8.py. Cannes → Nice 0,80 → 3,50 €, Marseille → Nice et Lyon → Nice inchangés. Attention : la colonne « Mougins » de la grille (1,70 jusqu'à Saint-Laurent) n'est pas l'échangeur n° 42 mais l'entrée côté Sophia / Valbonne (8 villes autour d'Antibes → Nice : 1,70 au simulateur contre 0,80 chez Kargit pour Valbonne et Vallauris, à revoir). (4) A89 → Lyon (import_toll_a89_lyon_sim.py) : la grille s'arrête à la limite de La Tour-de-Salvagny ; liaisons relevées Tarare Est (barrière « ouvert ») ↔ Villefranche-Limas 0 €, ↔ Condrieu 0 €, ↔ Saint-Quentin-Fallavier 2,60 € (Clermont → Mâcon 26,31 estimés → 22,30 ; Roanne / Clermont → Grenoble 23,74 / 33,77 → 21,90 / 32,60) ; prix des gares de l'A72 corrigés (Feurs, Montbrison, Veauchette : le libellé « Tarare est (système fermé) » avait été rapproché aussi de la barrière « ouvert » : 5,40 / 6,40 / 8,70 → 8,40 / 9,80 / 11,70 comme au simulateur). Non chargé : l'entrée fermée de Tarare (1,80 vers Lyon), que le trafic de passage croise sans payer (Tarare → Lyon / Valence / Marseille : 1,80 de moins qu'au simulateur). (5) Marseille → Toulon (A50) : 5,20 → 8,50 € = 5,20 + tunnel du Prado Carénage 3,30 € (prix fixe, payé dans les deux sens, six guichets OpenStreetMap, absent de toll_gares ; Marseille → Aubagne / Toulon / La Ciotat / Bandol à 0,00 d'écart). Pau ↔ Toulouse : 12,70 / 13,80 conformes (22/10). (6) Audit des triangles de la grille ASF / Escota (scripts/audit_toll_triangles.py) : défaut « libellés de ligne sans colonne » absent de 8 triangles sur 11 (lignes = colonnes, pas de décalage ; chacun recoupé par au moins 3 relevés) ; présent en fin de page 6 (Blaye, Saint-André, Ambès…, aucune gare de toll_gares concernée), page 8 (Lentilly, Pont-de-Dorieux, L'Arbresle, Tour-de-Salvagny, Clermont limite, Pont-du-Château : aucune gare concernée ; une cellule à texte coupé retirée : 5,14 €) et Escota page 7 (corrigé le 22/10). Restent : entrées d'Antibes Ouest / Sophia (voir 3), Saint-Cyr-les-Lecques, Six-Fours et Toulon-ouest (A50, mêmes prix que leurs voisins sauf Saint-Cyr, 1,20 € de moins), traversée de Lyon pour les tracés qui ne passent pas par Tarare Est (Nevers / Moulins → Marseille +3,80 à +4,70 € : Mâcon Sud → Condrieu 6,00), Clermont → Troyes 64,80 € (simulateur 33,20, tracé différent).toll_tariffs.source. (1) A8 Antibes - Nice - Menton : l'entrée d'Antibes (Ouest et Est) coûte 0,90 € de plus que la colonne « Antibes » de la grille Escota (qui est celle de Villeneuve-Loubet / Cagnes-Ouest) : Valbonne, Vallauris, Golfe-Juan, Juan-les-Pins, La Brague, Sophia → Nice 1,70 € (et 6,60 vers Menton, 5,60 vers Monaco) ; entrée de Saint-Laurent-du-Var avant la barrière de Saint-Isidore → Menton 4,90 / Monaco 3,90 (barrière de Saint-Isidore 1,50 €) ; Nice → Monaco 2,40 € et Nice → Menton 3,40 € = barrière de La Turbie à prix fixe (2,40 €, LA TURBIE BPV) + ligne La Turbie → Menton ; Cagnes-sur-Mer → Menton 5,60 €. (2) A13 / A154 : « Incarville n°19 / A154 » de la grille SAPN rapproché de la barrière « Incarville BPV » (Rouen → Évreux 2,40 € au lieu de ≈ 0,92 €) ; « Cambrai n°14 » = échangeur de Fontaine-Notre-Dame (Lille → Cambrai 3,60 €). (3) A40 : barrière du Fayet (ATMB, Cluses → Le Fayet 2,50 €) ajoutée : Genève / Lyon / Paris → Chamonix +2,50 €. (4) Lyon : barrière de Villefranche-Limas à prix fixe 2,80 € pour qui entre ou sort à Villefranche (Villefranche → Lyon) ; Tarare Est → Villefranche-Limas 1,80 € (entrées de Tarare vers Mâcon / Bourg-en-Bresse), le trafic de passage de l'A89 gardant ses lignes de bout en bout (Clermont → Mâcon 22,30 €). (5) A77 : les lignes Cofiroute « Gondreville-la-Franche → gares de l'A6 » (billet A6 puis A19) reçoivent la distance tarifaire de la grille APRR, ce qui les écarte pour un trajet qui reste sur l'A77 (Paris → Nevers 14,20 € au lieu de 22,40 €). (6) A11 → A87 : Corzé → Beaulieu-sur-Layon gratuit (contournement d'Angers : Paris → Cholet 37,30 €). (7) A50 : échangeur n° 11 de Saint-Cyr-sur-Mer (2,60 € depuis Aubagne, Cassis ou Toulon). tolls.py : une barrière à prix fixe posée sur la gare d'entrée ou de sortie d'un tronçon exact n'est plus facturée en plus (bornes arrondies à 0,1 km, tolérance de 0,05 km ; mesure du temps : médiane inchangée). Restes documentés : sorties de villes (Cagnes, Cassis, Saint-Cyr) où notre routeur prend une autre bretelle que le simulateur, A41 nord Annecy → Genève (5,20 € manquants entre Cruseilles et Saint-Julien), Le Havre (gare « Le Havre » à 1,5 km du tracé), A89 → A46 (−1,80 €).tolls.py ajoute maintenant ce prix fixe, et seulement lui, près du début ou de la fin du trajet (moins de 15 km) : (1) barrière ordinaire couverte seulement par des lignes à 0 € (une ligne à 0 € dit « aucun billet », pas « aucune barrière ») : Villefranche-Limas ; (2) gares-alias « Tarare Est Prix Fixe » et « Cagnes Est Prix Fixe » (colonne toll_gares.entree_ville, posées sur la barrière comme « Villefranche-Limas Prix Fixe ») : facturées si aucun tronçon à tarif exact payant ne les couvre et si aucun tronçon sans tarif (estimé ou écarté) ne les touche (Tarare → Genève par l'A46 reste à 23,90 € comme au simulateur). Sur 840 trajets en mémoire (les 710 de la passe 4 et ceux de ces villes), 66 changent : les 11 trajets relevés au simulateur (écart de 0,70 à 2,80 € → 0) et 55 trajets au départ ou à l'arrivée de Tarare sans relevé (+1,80 €) ; aucun trajet déjà juste ne bouge. Temps de calcul : médiane inchangée (≈ 18 ms par trajet, 3 passes alternées).estimated_price_eur) et n'affiche « ≈ » que si cette part atteint 10 % du montant (Lyon → Marseille : 0,42 € estimés sur 28,52 € s'affiche donc comme un tarif exact). Groupes : Vinci Autoroutes (ASF, Cofiroute, Escota, Arcour, Atlandes, Arcos, Albea — leurs grilles se recouvrent et l'import étiquette « ASF » des gares Cofiroute), APRR / AREA, Sanef / SAPN, les autres à part. Un tronçon estimé prend le groupe de sa gare de sortie (ou d'entrée) si elle a des tarifs, sinon celui du tronçon suivant. Exemple Rennes → Nice : 23 petits tronçons avant, 3 après (Vinci, APRR, Vinci), pour le même total (132,03 €) ; Paris → Marseille : 2 tronçons.| Technique | Gain observé | Impact sur le résultat |
|---|---|---|
| Regroupement des bornes par tranche de 3 km × opérateur | ≈ 4-5× sur la recherche de base (Lille → Aurillac : 577 → 271 bornes, 28,3 s → 6,7 s) | Heuristique : coût et abonnements identiques sur 11 trajets tests, mais une borne moins chère du même groupe peut être écartée. Premier suspect si un résultat semble étrange. |
| Étiquettes de Pareto + tolérances de dominance | évite l'explosion combinatoire (recherches de plus de 120 s ramenées à quelques secondes) | Résultat vérifié identique à une recherche quasi exacte tant que les tolérances (0,1 ct / 0,5 point) ne sont pas élargies. |
| Coupe des arêtes hors de portée | de dizaines de secondes à quelques secondes sur les longs trajets | Aucun : borne physique. |
| Consommation par tronçon : énergie cumulée précalculée à chaque borne (l'énergie d'un arc = une soustraction) ; profil de vitesse lu dans la réponse du routeur déjà demandée | aucun surcoût mesuré : recherche de plan Rennes → Lille 443 ms avec et sans profil ; requête ORS 24 ms contre 26 ms ; construction du profil ≈ 6 ms par itinéraire (mis en cache ensuite) | C'est le modèle de consommation lui-même qui change les résultats (section 3), pas l'optimisation. |
Optimisations de code (étiquettes à slots, comparaison par identité, tables précalculées, recherche binaire dans les courbes de charge, module natif Cython pour la sélection des tarifs) | quelques dizaines de % cumulés | Aucun : résultats vérifiés identiques (parité testée sur 200 000 cas pour les courbes). |
| Calculs en parallèle (processus séparés, jusqu'à 8 à la fois) | un aller-retour prenait ×6,7 le temps d'un aller simple avant ce changement ; nettement réduit depuis (mesure à refaire à chaque évolution) | Aucun : mêmes recherches, lancées ensemble (abonnements testés, variantes « sans abonnement », alternatives, nuit…). |
| Calculs d'un voyage hors du processus de l'appli (aller-retour, étapes ; cas de l'interface, sans alternatives de coût) : le calcul de base et le calcul « sans nouvel abonnement » partent en même temps dans deux processus, puis les abonnements testés, puis les comparaisons « sans abonnement / sans carte » (les calculs déjà faits, à jeu d'exclusions équivalent, ne sont pas refaits). Le processus de l'appli ne fait plus qu'orchestrer. | une recherche est du Python pur, tenue par le verrou global (GIL) : plusieurs requêtes simultanées faisaient la queue dans un seul processus alors que les autres cœurs restaient libres. Serveur OVH RISE-S (Ryzen 7 9700X), aller-retour de ≈ 500 km par sens : 5 requêtes simultanées 24 s → 6,6 s au pire, 10 simultanées 20 s → 3,4 s, un aller-retour seul 4,5 s → 2,2 s | Aucun : mêmes calculs, mêmes résultats (comparés sur 70 trajets : coûts, arrêts, abonnements, péages, durées identiques). Seul le calendrier change ; le calcul « sans nouvel abonnement » est lancé sans attendre le résultat de base (quelques secondes de processeur en plus quand il n'est finalement pas retenu). Avec les alternatives de coût demandées (compute_alternatives), l'ancien chemin séquentiel est conservé. |
Boucle de la recherche de plan en Cython (app/_search_native.pyx, chargée par planner/graph.py ; l'ancienne boucle Python reste comme repli et pour la recherche multi-abonnements) : exploration des arêtes de conduite, détection de dominance, tas et puissance moyenne de charge en C ; la sélection du tarif reste en Python, appelée une fois par recharge envisagée, et le TariffQuote n'est construit que pour les arrêts du plan final | recherche ×7 à ×8 plus rapide (aller-retour de 1 000 km : 2,7 s → 0,4 s sur le PC de développement). Serveur OVH, ~500 km par sens : aller simple 0,8 s → 0,4 s, aller-retour seul 2,3 s → 1,1 s, 5 allers-retours simultanés 5-7 s → 3,1 s au pire, 10 simultanés 3,5 s → 1,8 s (avant le passage à 5 abonnements testés) | Aucun : mêmes opérations flottantes dans le même ordre (compilation sans contraction FMA, sans -march=native), même ordre de sortie du tas, mêmes tolérances de dominance. 432 recherches complètes (3 véhicules, 8 trajets, 3 modes, exclusions/possessions d'abonnements, heure de départ, nombre d'arrêts) : 432 identiques, et 70 réponses d'API identiques. Le module compilé se reconstruit avec python setup_native.py build_ext --inplace (dossier app, paquet libc6-dev requis). |
| Appariement des gares de péage mémorisé par tracé (et projection précalculée) | ≈ 0,4 s de moins par calcul d'un aller-retour (10 appariements identiques par calcul ramenés à 2) | Aucun : même résultat, mémo de 2 minutes au plus. |
| Arrêt anticipé de la recherche du meilleur plan dès qu'aucun label restant ne peut arriver moins cher que la meilleure arrivée déjà trouvée | ≈ −40 % de temps et de processeur par recherche (aller simple Paris → Strasbourg : 3,0 s → 1,8 s sur le PC de développement) | Aucun : les labels sont traités par coût croissant et un coût ne fait que croître le long d'un chemin ; un label à coût égal reste exploré (départage par le niveau de batterie). Avant, la recherche continuait jusqu'à vider la file. |
| Recherche combinée mémoïsée par requête (mêmes exclusions d'abonnements, une fois normalisées) | une recherche complète de moins (≈ 2 s) quand le « sans abonnement » gagne et est recalculé pour l'affichage | Aucun : même clé = même résultat. |
| Gagnant de la recherche d'abonnements manqués : recherche réutilisée (le job qui a trouvé l'abonnement rentable transmet sa recherche ; seul l'assemblage affichable — péages, détails — est refait) | ≈ 1 s de moins sur les voyages où un abonnement « manqué » gagne (Lyon ↔ Toulouse sur le serveur : 5,1 s → 4,2 s), autant de processeur économisé | Aucun : même recherche, mêmes résultats (42 voyages comparés avec et sans réutilisation : 0 différence dans la réponse complète). |
Projection des bornes sur le tracé précalculée (geo.RouteProjector) | × 3,7 sur cette étape (≈ 0,25 s de moins par itinéraire) | Aucun : le point le plus proche est retrouvé par un classement approché puis départagé avec le vrai calcul de distance ; 0 différence sur 4 985 bornes testées. |
| Démarrage des workers par un serveur de forks préchargé (forkserver) au lieu d'un interpréteur neuf par worker (spawn) | chaque lot repayait ≈ 1 s de réimport de l'appli : 3 jobs d'un voyage Rennes → Lille passent de 2,8-3,4 s à 1,4 s | Aucun : mêmes calculs. Le serveur de forks ne garde aucune connexion base et s'arrête avec l'appli ; le pool reste créé et refermé par lot. Repli sur spawn si indisponible. |
| Itinéraires, bornes candidates et cache de tarifs transmis aux workers (un voyage calcule ses itinéraires une fois, pas une fois par abonnement testé) ; le cache envoyé à chaque worker est limité aux bornes du trajet (17/10/2026 : l’envoyer en entier, 39 000 bornes, coûtait plusieurs secondes de CPU par recherche ; une borne absente est relue en base) | ≈ 0,7 s de routage + requête spatiale évitées par job ; ≈ 1 s de CPU de sérialisation évitée par worker avec le cache limité | Aucun : ces données ne dépendent pas de l'abonnement testé. |
| Péage non calculé pour les calculs « de test » (comparaisons de coût jetées) | ≈ 0,1 s par calcul | Aucun : le péage n'entre dans aucune de ces comparaisons ; il est calculé pour le résultat affiché. |
| Recherche fusionnée des trajets enchaînés | 1 recherche au lieu de N | Peut améliorer le résultat (optimum global), jamais le dégrader. |
| Retour = aller inversé | pas de nouvelle recherche d'itinéraire ni de couloir | Le retour ne peut plus choisir un autre corridor (voulu). |
| Alternatives sans coût | ≈ −35 % sur un aller-retour avec 4 itinéraires | Le coût des alternatives n'est plus affiché. |
| Caches : géocodage, itinéraires, lignes de tarifs par borne, résultats d'un même voyage | évite requêtes réseau et SQL répétées | Aucun, sauf tarifs modifiés en cours de journée (le cache est vidé à chaque redémarrage). |
| Bornes sûre / heuristique / top 5 pour les abonnements | de 15-20 recherches en série (plusieurs minutes) à un lot parallèle de quelques secondes | Bornes sûres : aucun. Borne heuristique et top 5 : rares abonnements manqués (voir section 8). |
| Variantes calculées à la demande côté interface | seules les variantes consultées sont calculées | Aucun. |
Le temps d'attente annoncé à l'écran est une estimation calibrée sur la durée des calculs précédents et sur la complexité du voyage (un aller-retour ou une étape allonge le calcul), pas une prédiction exacte.
Module app/ratelimit.py. Tout est en mémoire (un seul processus en production : compteurs partagés par construction, remis à zéro à chaque redémarrage ou mise en service). Coût par requête ≈ 20 µs, aucun effet mesurable sur la vitesse (serveur : aller simple 0,5 s, aller-retour 1,5 s, 5 allers-retours simultanés 3,7 s avant comme après). Un refus renvoie 429 (limite de débit) ou 503 (plafond de calculs simultanés) avec le corps {"detail", "retry_after"} et l'en-tête Retry-After (secondes).
X-Forwarded-For n'est cru que si la connexion directe vient d'un proxy de confiance (Caddy, réseau Docker) ; Caddy y met l'adresse du visiteur, on lit la dernière entrée. Un client qui l'envoie lui-même ne peut pas se faire passer pour une autre adresse. Les adresses de RATELIMIT_EXEMPT_CIDRS (boucle locale par défaut : tests de charge, sondes) ne sont jamais limitées ; en développement, y ajouter le réseau Docker./plan, /trip-plan, /multi-plan) : budget en jetons pondérés par le coût réel (1 par /plan, le nombre de trajets pour /trip-plan, la somme pour /multi-plan ; un aller-retour en 4 variantes = 8 jetons). Visiteur anonyme : 60 jetons/min et 600/h par adresse ; connecté : 180/min et 1 800/h par compte ; au plus 300/min par adresse dans tous les cas (plusieurs comptes depuis une même connexion).MAX_CONCURRENT_CALCULATIONS) ; au-delà, attente de 10 s au plus, puis 503. Sous charge normale (5 allers-retours simultanés) il n'intervient jamais./tariffs-map/station/{id}/tariffs, inutilisé par le site) est réservé à l'administrateur./multi-plan, pseudo 40, prénom 60, e-mail 254, mot de passe 128, nom d'un favori 120, nom du formulaire de contact 120, message 5 000 ; la configuration d'un favori et celle enregistrée dans le journal des recherches sont bornées à 20 000 caractères.Mesure d'audience sans cookie ni identifiant conservé (donc sans bandeau de consentement : conditions d'exemption de la CNIL). Code : app/stats.py (collecte, catégories, purge) et app/api/stats.py (POST /api/collect public, GET /api/admin/stats réservé à l'administrateur). Le script du site envoie un signe par changement de vue (Trajets / Bornes), un signe de vie toutes les 15 à 30 s et quelques événements (calcul lancé, résultats affichés, clic « S'abonner », favori enregistré, inscription, borne ouverte, lien partagé) ; il ne stocke rien sur l'appareil. Les applis Android et iPhone envoient les mêmes signes (AppStats.kt, AppStats.swift) : le serveur les reconnaît à leur identifiant « KargitApp-Android/… » / « KargitApp-iOS/… » et range leurs visites à part (colonne platform = android / ios, web pour le site ; version de l'appli dans app_version) ; les boutons Tout / Site / Appli Android / Appli iPhone filtrent toute la page (sauf inscriptions, favoris et trajets les plus calculés, dont l'origine n'est pas connue). L'opposition faite depuis les mentions légales ouvertes dans l'appli y arrête aussi la mesure.
a-z 0-9 _ — et des compteurs sont gardés). Par visite : appareil (mobile / tablette / ordinateur), navigateur, système, langue, domaine du referrer (jamais le chemin), vue d'entrée et vues consultées, pages vues, temps actif cumulé (au plus 30 s par signe de vie), compteurs d'événements.X-Forwarded-Host (sinon Host) de la requête de mesure, sans « www. » ni port et seulement s'il ne contient que a-z 0-9 . - ; « (appli) » pour les applis (elles appellent toutes le même serveur) ; colonne stats_sessions.site_host (migration 20261018_stats_site_host.sql), vide pour les visites d'avant (affichées « (avant le 18/10) »). Carte « Domaine du site » dans « D'où et quoi ? » (vues Tout et Site), cumulée aussi dans stats_daily à la purge. Le trafic international est compté comme le reste : aucun filtre par pays ni par langue, seules les exclusions ci-dessous s'appliquent ; les noms de domaine du site ne comptent plus comme « autre site » dans l'origine.stats_salts) et supprimé après 48 h : deux jours ne peuvent plus être reliés, même avec la base. Le « visiteur » des totaux est donc la somme des visiteurs uniques par jour. Visite = même visiteur, coupée après 30 min sans signe.hi du site), un temps de présence ou un événement. Les autres (robots qui exécutent le JavaScript avec un user-agent de navigateur, aperçus de liens, onglets ouverts sans être regardés) sont gardées à part (engaged = false) et seulement comptées dans « Visites écartées ». Le temps actif ne démarre lui aussi qu'au premier geste (avant le 05/10, les 30 premières secondes comptaient d'office, robots compris : visites antérieures reclassées d'après leur présence, leurs pages et leurs événements). Le script du site ne mesure rien quand le navigateur est piloté par un outil de test (navigator.webdriver).DNT: 1 ou Sec-GPC: 1 ; robots connus (user-agent) ou absence d'Accept-Language ; émulateurs Android et simulateurs iOS (nos propres tests de l'appli) ; corps de plus de 1 Ko ou illisible ; plus de 120 signes/min pour une même adresse (limite propre, hors budget des calculs). L'opposition explicite passe par l'indicateur local kargit_no_stats=1 lu par le script du site.stats_sessions) 13 mois ; à la purge quotidienne (retention.py), les jours plus vieux sont cumulés dans stats_daily (chiffres par jour et répartitions, sans limite de durée) puis supprimés. Au-delà de 13 mois, la médiane de durée est approchée d'après les tranches de durée et il n'y a plus de détail par visite.POST /api/subscribe-click, qui incrémente un simple compteur par jour et par abonnement (subscribe_clicks) — aucune donnée sur la personne (ni IP, ni empreinte, ni visite). Mêmes filtres que la collecte (DNT / GPC, robots, opposition locale, corps de plus de 1 Ko) et au plus 20 clics/min comptés par adresse ; un identifiant inconnu est ignoré. La liste de l'onglet affiche, pour chaque abonnement, deux colonnes : le total et les 7 derniers jours (aujourd'hui compris, fuseau Europe/Paris) via GET /api/admin/subscribe-clicks. Compteurs démarrés le jour de la mise en ligne (pas de reprise des clics passés, qui n'étaient pas rattachés à un abonnement).compute), pas le journal des recherches. Un jour (« Aujourd'hui » ou une date) est comparé au même jour de la semaine précédente (la journée entière d'il y a 7 jours, y compris pour « Aujourd'hui » : décision de Jérémy du 30/10/2026, plus de comparaison « jusqu'à la même heure ») ; une période de plusieurs jours, à la période juste avant, de même durée (champ compare de la réponse, avec sa série pour le contour pointillé ; previous reste la période juste avant, pour l'appli). Visiteurs comptés sur toutes les pages publiques : en plus de l'accueil, stats.js est chargé par les pages de référencement des bornes (villes, opérateurs, fiches, autoroutes, bornes lentes), les pages légales, l'espace presse, la page introuvable, la réinitialisation du mot de passe et linktree, qui annoncent leur nature par l'attribut data-kg-page de leur balise script (JavaScript externe : la CSP de production bloque les scripts dans la page) ; une visite commencée sur l'une d'elles puis poursuivie dans l'appli reste une seule visite et garde sa source (Google…). Sites par pays de kargit-ev.com séparés dans « Par site » (kargit-ev.com/sverige…) par la colonne site_variant (migration 20261029_stats_site_variant.sql), lue dans le premier mot du chemin et acceptée seulement s'il est l'un des chemins connus (ev_sites.json). Langue des applis : elles envoient « fr-FR » en dur ; le serveur lit désormais l'en-tête X-Kargit-Lang, puis Accept-Language, et range les visites d'applis d'avant sous « Applis (langue inconnue) ». « Origine », « Par site » et « Par langue » comptent des visiteurs (un par jour).grid), « Liste » (documents) ou « Grande image » (large, deux colonnes à aperçu agrandi sur kargit.fr/presse) ; « Remplacer le fichier » envoie le nouveau fichier avec PUT /api/admin/presse/fichier?remplace=<id> : il prend la place (ordre, format principal, libellé) de l'ancien, qui est supprimé. Les formats d'un visuel restent déposés à la main (rien n'est généré par le serveur). Route réservée aux administrateurs et gestionnaires (require_staff).desinscription, balise <script src="/assets/stats.js" data-kg-page="desinscription"> ajoutée à la page servie par app/api/admin_email.py) ; (2) un signe de visite qui échoue côté serveur n'est plus avalé en silence : un avertissement est écrit dans les journaux (au plus un toutes les 10 minutes, app/api/stats.py) ; (3) la comparaison d'un jour porte sur la journée entière de la semaine précédente (voir ci-dessus) ; (4) Migrations pas encore jouées : les colonnes ajoutées par la refonte (subscriptions.advantage, rewards.update_mode / updated_by / updated_at) sont en chargement différé dans app/models.py (le moteur de trajets ne les lit jamais : une recherche ne dépend pas de ces migrations) ; un écran d'admin qui en a besoin répond 503 « Migration à jouer » avec le nom de la colonne ou de la table manquante (main.py, uniquement sous /api/admin), les échanges de courriels répondent une liste vide marquée available: false et la carte « Tâches planifiées » reste grise ; (5) la photo de nuit des bornes (app/recap_photo.py) est démarrée au lancement du backend principal, jamais sur les répliques de calcul ; (6) Emails : le gestionnaire peut tout faire sauf envoyer ou programmer une campagne, et annuler une programmation (require_admin sur /send et /unschedule) : il modifie les modèles, crée, modifie et supprime des brouillons, utilise l'aperçu et envoie un courriel de test, toujours à sa propre adresse ; une campagne déjà programmée reste réservée aux administrateurs ; (7) Espace presse : le gestionnaire garde l'écriture ; le fond d'un visuel doit être une couleur hexadécimale, les titres, légendes, étiquettes et noms ont une longueur maximale et sans caractère de contrôle (app/api/admin_presse.py) ; côté page, les textes saisis sont échappés guillemets compris (escapeHtml) ; (9) Recherches de test (décision de Jérémy, 30/10/2026) : « Recherches effectuées » (filtre « Masquer les tests », activé par défaut) et « Trajets les plus recherchés » ne comptent plus les tests internes = appels directs au serveur sans proxy, recherches faites avec un compte administrateur ou gestionnaire (cookie de session décodé sans requête SQL, comparé à un ensemble d'identifiants en mémoire rechargé toutes les 60 s : app/request_origin.py ; aucun identifiant de compte n'est enregistré, seulement le booléen internal) et recherches d'un navigateur qui a le drapeau d'opposition à la mesure (localStorage kargit_no_stats = 1 : le site ajoute alors l'en-tête X-Kargit-Test: 1 aux 3 routes de calcul, assets/i18n.js). Les signes envoyés à /api/collect avec une session d'équipe (visites, présence et événements, dont « calcul lancé » de la carte « Recherches » des statistiques) sont ignorés (204) : les visites de l'équipe ne comptent plus non plus ; les autres visiteurs ne sont pas touchés. Les recherches et visites de l'équipe enregistrées AVANT cette date restent comptées (rien ne permet de les reconnaître) ; (8) la recherche globale (Ctrl K) échappe correctement les caractères %, _ et \.Le géocodage transforme l'adresse tapée en coordonnées, une fois par adresse (résultat gardé en mémoire puis dans la table geocode_cache, donc une adresse déjà vue ne rappelle aucun service). Code : app/routing.py (geocode, geocoder_primary, _geocode_local).
/api/suggest, /api/suggest/reverse) finissent par le pays dans la langue du site (« Rennes, Ille-et-Vilaine, Frankrike » sur le site suédois, « Storbritannien » pour « United Kingdom »), pour tous les pays du monde (table app/country_names_i18n.json, noms CLDR générés par scripts/gen_country_names.mjs, lue une fois en mémoire : aucun appel réseau). À l'inverse, avant de géocoder, geocode() ramène le dernier élément (s'il y a au moins deux éléments séparés par une virgule) au nom français quand c'est un pays d'Europe écrit dans une langue que Photon ne connaît pas (« Frankrike », « Tyskland », « Česko »...) : une adresse française garde exactement la même clé de cache qu'avant ; « Deutschland », « Germania », « Sverige »... restent tels quels (clés de cache existantes). Coût : une recherche dans un dictionnaire en mémoire (quelques microsecondes) avant la consultation du cache ; un nom de pays jamais vu sous cette forme (site suédois, danois, norvégien, finnois ou polonais, pays étranger) coûte un géocodage la première fois, puis reste en cache.X-Kargit-Lang : site, Android, iPhone) par country_names.localize_tail, pour les noms et adresses des bornes de la carte (/api/tariffs-map/stations, /api/tariffs-map/slow/stations, fiche lente), les arrêts d'un plan (/api/plan, /api/trip-plan, /api/multi-plan : « Montmarault, France » → « Montmarault, Frankrig ») et les pages SEO (nom et adresse de chaque borne). Règles de sûreté : seul un pays d'Europe reconnu sans ambiguïté est remplacé ; sur la carte, il doit être le pays de la borne (« Repsol, Granada » en Espagne ou « ZAC, Gibraltar » sur une borne française ne changent pas) ; les régions britanniques (« England »...) restent des noms de lieux ; un texte déjà dans la bonne langue ne change pas. Coût : une coupe de chaîne et une recherche dans un dictionnaire en mémoire par nom et par adresse (≈ 1 à 2 µs, mesuré : ≈ 1 ms pour 500 bornes sur la carte, ≈ 10 µs pour un plan) ; aucune requête, aucun appel réseau, rien dans le calcul d'un trajet ni dans les clés de cache (appliqué sur la réponse, après le calcul).app/sub_names_i18n.json ancrés au début d'un nom (« Carte de recharge », « Réseau de recharge du groupe », « Formule »...) acceptent maintenant ces préfixes (capturés puis rendus tels quels, le préfixe est traduit par les modèles « Appli, abo » de chaque langue) : « Appli, abo Carte de recharge DATS 24 » devient « App, abo Laadpas DATS 24 » en néerlandais, comme les onze autres langues. Même source pour le site, Android, iPhone (scripts/i18n_build.py, scripts/i18n_apps.py) ; contrôle : scripts/tests/i18n_serveur.py dico.kargit_photon ; garde-fou (29/09/2026) : si l'adresse finit par un pays étranger (« Genève, Suisse », « Salou, Tarragonais, Espagne ») et que Photon répond dans un autre pays, on passe au géocodeur suivant, LocationIQ, qui couvre le monde (routing._requested_foreign_country ; avec l'index France seule, « München, Bayern, Deutschland » tombait dans le Pas-de-Calais) ; à l'inverse, sans pays nommé, si la première réponse est étrangère et qu'une ville française du même nom figure parmi les 5 premières, elle l'emporte (« Valence » → Drôme et non Valencia ; un simple village homonyme ne suffit pas : « Cologne » reste Köln, « Mons » la ville belge), gratuit, sans quota). Le choix est enregistré en base (app_settings, clé geocoder) et pris en compte en moins de 30 s par tous les processus (relecture périodique) : repasser d'un service à l'autre = un clic.GEOCODER_DEFAULT_PRIMARY). L'admin refuse de choisir « Local » si Photon ne répond pas (/status) ou si GEOCODER_LOCAL_URL est absent.deploy/server/photon-install.sh (téléchargement du dump OpenStreetMap publié par GraphHopper, vérification md5, import France + les 10 pays voisins (COUNTRY_CODES, noms en français, anglais, allemand, italien, espagnol et néerlandais ; France seule jusqu'au 29/09/2026 : « Bruxelles », « Munich », « Londres » donnaient un hameau français homonyme) avec un nouvel index construit à côté, puis vidage du cache des adresses (table geocode_cache et mémoire du backend, redémarré), qui gardait les réponses de l'ancien index, puis bascule sans coupure). Le service se déclare dans docker-compose.prod.yml (profil geocoding, ajouté à COMPOSE_PROFILES dans /opt/kargit/.env). Automatique tous les 3 mois (cron posé par deploy/release.sh, 1er janvier/avril/juillet/octobre à 1h15) ; visible avec sa date, sa durée et le nombre de lieux dans l'onglet « Tâches planifiées » de l'admin (état écrit par le script, jamais déclenché par le backend -- même principe que les sauvegardes). Priorité volontairement basse (site en production sur le même serveur) : téléchargement bridé en débit, décompactage et import à priorité CPU/disque minimale -- s'effacent automatiquement dès qu'une recherche a besoin de la machine, la tâche prend juste plus longtemps (1h30 à 2h30 environ) plutôt que de ralentir un visiteur.scripts/compare_geocoders.py) : 42 résultats identiques au mètre près, la plupart des autres à moins de 1 km ; Photon répond en 16 ms en médiane contre 200 ms pour LocationIQ (jusqu'à 1,2 s pour ce dernier). Photon est meilleur sur les fautes de frappe (« Marseile », « Bordeau », « Strasbourgh » trouvés, LocationIQ se trompe ou échoue), les codes postaux seuls (« 35000 » → Rennes ; LocationIQ répondait en Algérie) et « Gare de Rennes » (10 km d'écart, Photon est sur la gare). Points faibles de Photon : une adresse dont le numéro n'existe pas dans OpenStreetMap tombe sur la rue ou sur un commerce voisin (1 à 5 km d'écart sur les gros numéros de rues très longues : « 20 rue Nationale, Lille », « lyon 3eme » → une agence). Index France : 19,5 millions de lieux, 5,8 Go sur disque, environ 4 Go de mémoire pour le service.scripts/compare_geocoders.py compare les deux services sur une soixantaine d'adresses.GET /api/suggest, GET /api/suggest/reverse -- app/api/suggest.py) : Géoplateforme + notre géocodeur local en parallèle pour la France (même charge sur ces deux services qu'avant, juste depuis notre IP) ; Photon PUBLIC (reste de l'Europe) en plus, en parallèle, mais en meilleur effort non bloquant (sauté sans attendre si sollicité il y a moins de 300 ms) pour ne jamais multiplier par le nombre de visiteurs le trafic vu par ce service public gratuit depuis une seule IP serveur ; Nominatim en tout dernier recours. Limite dédiée (SUGGEST_RULES, app/ratelimit.py) : 12 requêtes/10 s puis 60/min par IP. Pas encore branché côté site (le sélecteur de suggestions appelle encore les services tiers directement) -- CSP (connect-src) à resserrer une fois que ce sera fait. Pays du site ou de l'appli (05/10/2026) : Photon public reçoit la position et le zoom de la carte du pays (locales.json mapView, sites.Site.map_view côté serveur ; avant, position de la France sans zoom : sur kargit.uk, « brist » donnait des rues de Blackburn au lieu de Bristol), et une commune de la Géoplateforme ne compte comme « ville » (mise en tête) que si son nom correspond à la saisie (« utrecht » ne fait plus passer Autrèche devant Utrecht). Même règle sur le site (kargit-v3.js) et pour l'appli (/api/suggest). Notre Photon pour tous les pays (06/10/2026) : son index couvre la France et ses voisins (FR, BE, LU, DE, CH, IT, ES, PT, AD, MC, GB, NL ; noms en fr, en, de, it, es, pt, nl) et répond en 10 à 50 ms ; /api/suggest l'interroge avec l'IGN et n'appelle plus Photon public (0,6 à 2 s), gardé seulement sans géocodeur local (développement). Sites étrangers : leurs suggestions passent par /api/suggest (une seule liste déjà classée, environ 0,1 à 0,4 s) ; avant, la liste de l'IGN, française, s'affichait seule 1 à 2 s en attendant Photon public. kargit.fr aussi depuis le 15/10/2026 (notre Photon couvre désormais toute l’Europe ; le Photon public mettait 10 à 14 s ce soir-là et « Vérone » ne proposait que des rues françaises) ; serveur en échec (limite de fréquence, panne) : repli sur l’IGN et le Photon public interrogés par le navigateur. Une ville-comté ou une commune que Photon classe « city » compte comme une ville (Oslo, place=county), et une ville au nom exact de la saisie passe en tête quel que soit le pays (« oslo » : Oslo avant Oslon).« Aller à cette borne » et « Envoyer à Maps » désignent chaque borne par sa fiche Google Maps quand elle est connue : Maps ouvre la fiche (prises disponibles en temps réel quand l'opérateur les transmet à Google) au lieu d'un simple point. Identifiant de la fiche (Place ID) dans la table station_google_places, cherché par scripts/google_places_fill.py chaque jour après l'import IRVE (toutes au premier passage, 1 à 2 h ; ensuite : nouvelles bornes, puis « rien trouvé » depuis plus de 90 jours, puis identifiants de plus d'un an) : recherche texte Places API (New) « <opérateur> borne de recharge », dans un rectangle d'environ 150 m autour de la borne, type « borne de recharge » strict, identifiant seul (SKU Text Search Essentials (IDs Only), gratuit sans limite ; ne jamais ajouter de champ au masque, la recherche deviendrait payante). Clé GOOGLE_PLACES_API_KEY (projet Google Cloud kargit, limitée à Places API (New) et à l'adresse du serveur). Test du 08/10/2026 : 15 bornes sur 16 trouvées, toujours le bon réseau, même avec un autre réseau à moins de 150 m. Lu pour les seuls arrêts du résultat (google_place_id de chaque arrêt, app/google_places.py, une requête sur la clé primaire, moins d'une milliseconde, après la recherche) et dans la fiche d'une borne. Lien : nom de la borne + destination_place_id (avec des coordonnées, Maps sur ordinateur garde parfois l'adresse de la rue) ; étapes : waypoint_place_ids seulement si toutes les étapes ont une fiche. Repli (règle de Jérémy) : sans fiche connue, table vide ou en erreur, les liens restent ceux d'avant (coordonnées). Waze, Plans, le lien Android geo: (menu « Ouvrir avec ») et Android Auto ne savent pas désigner une fiche Google : inchangés ; dans le menu Android « Aller à cette borne » de l'appli, Google Maps reçoit le lien de la fiche. Trajet « sans péage » (17/10/2026, retour d'utilisateurs : l'option était perdue dans Maps) : quand le trajet affiché est réellement sans péage (pas quand le serveur a dû garder le péage), les liens des boutons « Envoyer à » portent l'option d'évitement des péages : Google Maps &avoid=tolls (paramètre officiel des liens Google Maps, avec les fiches des bornes conservées), Plans (Apple) &avoid=tolls (liens unifiés, iOS 18.4 et plus), Waze &avoid_tolls=true (une seule destination). Testé le 17/10/2026 dans l'appli Google Maps Android (émulateur) : avec l'option, Paris → borne de Troyes → Lyon passe de 5 h 51 (péages) à 7 h 44 (sans). Limite constatée sur le téléphone de Jérémy : si l'appli Google Maps est réglée sur un véhicule électrique (« Renault », « Commencer avec 80 % »), elle ignore l'option du lien : l'utilisateur doit cocher « Éviter les péages » dans les options de trajet de Maps. Firefox pour Android demande « Ouvrir ce lien dans l'application Maps ? » à chaque lien (case « Toujours ouvrir les liens dans des applications »). Applis Android et iPhone (et Android Auto, CarPlay) : à reporter (P-121).
Prix d'un plein d'essence pour la distance du résultat le moins cher (8 L/100 km, prix moyen du SP95-E10 relevé par /api/fuel-price), moins le prix brut de la recharge (avant cashback) et la charge initiale (11/10/2026, Jérémy) : l'énergie prise dans la batterie du départ, non achetée aux bornes, comptée à 0,20 €/kWh (recharge à domicile ; hypothèse volontairement non affichée). Par sens : (batterie au départ − batterie à l'arrivée) × capacité retenue du véhicule, jamais négatif ; aller-retour : les deux sens ; plusieurs trajets : chacun ; fréquence : multipliée par le nombre de trajets du mois (sumMode, HOME_CHARGE_EUR_KWH dans kargit-v3.js). Exemple Rennes → Nantes aller-retour en Mégane 40, départ à 80 %, arrivée à 14,4 % : 24,1 kWh, soit 4,83 € de plus que la recharge.
Kargit en anglais, allemand, espagnol, italien et néerlandais (kargit-ev.com, kargit.uk, kargit.de, kargit.es, kargit.it, kargit.nl, kargit.be/fr|nl, kargit.ch/de|fr|it) -- détails dans docs/LOCALISATION.md. Le calcul est le même partout : seul l'affichage change.
GET /api/fx, app/api/fx.py, gardé 6 h, taux de secours si la BCE ne répond pas). Un montant converti est donc arrondi à l'affichage ; il n'influence jamais le choix des bornes.GET /api/admin/email/history accepte en plus status, q (adresse ou objet), days et offset ; GET /api/admin/email/stats ajoute les chiffres sur 30 jours (envoyés, échecs, campagnes, automatiques, dernier envoi), envois de test non comptés ; DELETE /api/admin/email/campaigns/{id} n'accepte plus qu'un brouillon ou une campagne programmée (la programmation disparaît avec elle). Les heures de programmation se saisissent et s'affichent en heure de Paris. Les copies du formulaire de contact ne sont pas journalisées dans l'historique./borne/…, backend/app/api/seo_pages.py, 17/10/2026) : les mêmes lignes de tarif que la fiche de l'onglet Bornes, sans abonnement coché ni puissance (pire cas horaire). Particularité de ces pages (Jérémy : « manque les infos de prix appli sans abo sur les fiches de bornes servant au référencement ») : la ligne « Appli, sans abo » est affichée même quand elle coûte la même chose que la carte bancaire (E-Totem, Le Plein, e-Vadea, Zunder, IECharge), alors que la fiche de l'onglet Bornes la masque dans ce cas pour éviter un doublon. Un opérateur dont seul le prix par carte bancaire est enregistré (Izivia, Lidl, Driveco, Powerdot, TotalEnergies, Atlante…) n'a pas de ligne appli : son prix par l'appli sans abonnement n'est pas connu.slugs de frontend/i18n/locales.json). Les versions en français (kargit.fr, kargit.be/fr, kargit.ch/fr) gardent /bornes et les pages en .html. Chaque onglet Bornes est une vraie page (titre, adresse canonique et liens hreflang vers les autres langues dans le HTML), y compris kargit.fr/bornes. Les anciennes adresses françaises d'un site étranger (kargit.it/bornes, /mentions-legales.html…) sont redirigées en 301 (règles Caddy générées par scripts/i18n_build.py, ajoutées par deploy/release.sh). Une adresse référencée ne se change plus sans redirection.locales.json (site) et de I18n.kt (appli) : un pays ajouté plus tard est pris en compte tout seul. Le filtre des opérateurs de la carte envoie ops_home (pays du site, France par défaut) : décocher un opérateur sur kargit.it agit sur ses bornes italiennes, plus sur les françaises.operator_apps (un moyen de paiement « appli » par ligne), modifiable dans l'onglet Opérateurs ; sans lien renseigné, pas de lien. Aucun effet sur le calcul.country des points d'accès de l'admin) : les bornes, tarifs et abonnements étrangers publiés en production se gèrent là, comme ceux de France ; un tarif ou un abonnement ajouté prend le pays choisi. L'onglet « Pays voisins (chantier) » ne s'affiche que là où sa zone de travail existe (base locale). Mise à jour du 29/10/2026 (Jérémy : « pays voisins sert plus à rien ») : le volet « Pays voisins (chantier) » a été retiré de l'administration ; les bascules de pays des autres onglets restent./api/loading-tips), sans mise à jour des applis.assets/i18n.js) ne tourne que sur les sites étrangers ; kargit.fr ne fait rien de plus.