triggerMessage BootNotification
/**
- Script POC pour envoyer un triggerMessage BootNotification à toutes les bornes
- UTILISATION:
-
- Ouvrir la console du navigateur (F12) sur votre application Angular
-
- Copier-coller ce script dans la console
-
- Appeler: triggerBootNotificationToAllStations()
- OU avec URL personnalisée: triggerBootNotificationToAllStations("https://portail.we-go.pro/api")
- POUR ARRÊTER LE SCRIPT:
-
- Appeler: stopBootNotificationScript()
-
- OU rafraîchir la page (F5)
- IMPORTANT: Assurez-vous d'être connecté et que le token d'authentification est présent */
// Variable globale pour contrôler l'arrêt du script let stopBootNotificationScriptFlag = false;
// Fonction pour arrêter le script function stopBootNotificationScript() { stopBootNotificationScriptFlag = true; console.log("🛑 Arrêt du script demandé. Le script s'arrêtera après la requête en cours..."); }
async function triggerBootNotificationToAllStations(customApiUrl = null) { // Réinitialiser le flag d'arrêt stopBootNotificationScriptFlag = false; console.log("🚀 Démarrage de l'envoi de BootNotification à toutes les bornes..."); console.log("💡 Pour arrêter le script, appelez: stopBootNotificationScript()");
try { // Récupérer le token d'authentification depuis localStorage const token = localStorage.getItem("token");
if (!token) {
throw new Error("Token d'authentification non trouvé dans localStorage (clé: 'token'). Assurez-vous d'être connecté.");
}
// Récupérer le groupe sélectionné
const selectedRoleStr = localStorage.getItem("selectedRole");
if (!selectedRoleStr) {
throw new Error("selectedRole non trouvé dans localStorage. Assurez-vous d'avoir sélectionné un groupe.");
}
let selectedRole;
try {
selectedRole = JSON.parse(selectedRoleStr);
} catch (e) {
throw new Error("Erreur lors du parsing de selectedRole: " + e.message);
}
if (!selectedRole?.groupId) {
throw new Error("groupId non trouvé dans selectedRole. Structure: " + JSON.stringify(selectedRole));
}
// Détecter l'URL de l'API
let API_URL;
const currentOrigin = window.location.origin;
if (customApiUrl) {
// URL personnalisée fournie
API_URL = customApiUrl;
console.log(`🔗 URL de l'API (personnalisée): ${API_URL}`);
} else if (currentOrigin.includes("localhost") || currentOrigin.includes("127.0.0.1")) {
// Mode développement
API_URL = currentOrigin.replace(":4200", ":3000").replace(":4201", ":3000") || "http://localhost:3000";
console.log(`🔗 URL de l'API (développement): ${API_URL}`);
} else {
// Mode production - utiliser le même domaine avec /api
API_URL = currentOrigin + "/api";
console.log(`🔗 URL de l'API (production): ${API_URL}`);
}
console.log(`👤 Groupe ID: ${selectedRole.groupId}`);
console.log(`🔑 Token présent: ${token ? "Oui (longueur: " + token.length + ")" : "Non"}`);
const groupId = selectedRole.groupId;
const pageSize = 100;
let page = 1;
let allStations = [];
let hasMorePages = true;
// Récupérer toutes les bornes
console.log("📡 Récupération de la liste des bornes...");
while (hasMorePages && !stopBootNotificationScriptFlag) {
if (stopBootNotificationScriptFlag) {
console.log("🛑 Arrêt demandé pendant la récupération des bornes");
break;
}
const offset = (page - 1) * pageSize;
const url = `${API_URL}/charging-stations/group/${groupId}/list?offset=${offset}&limit=${pageSize}`;
console.log(`📡 Requête: ${url}`);
// Ajouter un timeout pour la récupération des bornes aussi
const fetchController = new AbortController();
const fetchTimeoutId = setTimeout(() => fetchController.abort(), 10000); // 10 secondes pour la liste (peut être plus long)
let response;
try {
response = await fetch(url, {
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
signal: fetchController.signal,
});
clearTimeout(fetchTimeoutId);
} catch (fetchError) {
clearTimeout(fetchTimeoutId);
if (fetchError.name === "AbortError") {
throw new Error("Timeout lors de la récupération des bornes (plus de 10 secondes)");
}
throw new Error(`Erreur réseau lors de la récupération: ${fetchError.message}`);
}
// Vérifier le Content-Type avant de parser
const contentType = response.headers.get("content-type");
if (!contentType || !contentType.includes("application/json")) {
const responseText = await response.text();
console.error("❌ Réponse non-JSON reçue:");
console.error(` Content-Type: ${contentType || "non défini"}`);
console.error(` Status: ${response.status} ${response.statusText}`);
console.error(` Début de la réponse: ${responseText.substring(0, 200)}...`);
if (responseText.includes("<!doctype") || responseText.includes("<html")) {
throw new Error(
`L'API a retourné du HTML au lieu de JSON. Cela indique probablement:\n` +
`- L'URL de l'API est incorrecte (actuelle: ${API_URL})\n` +
`- Une redirection vers une page d'erreur\n` +
`- Un problème d'authentification\n\n` +
`Vérifiez que l'URL de l'API est correcte. Si vous êtes en production, l'API devrait être sur: ${currentOrigin}/api`
);
}
throw new Error(`Réponse non-JSON: ${response.status} ${response.statusText}`);
}
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Erreur HTTP: ${response.status} ${response.statusText}\n${errorText}`);
}
let data;
try {
data = await response.json();
} catch (parseError) {
const responseText = await response.text();
console.error("❌ Erreur lors du parsing JSON:");
console.error(` Réponse reçue: ${responseText.substring(0, 500)}...`);
throw new Error(`Erreur parsing JSON: ${parseError.message}`);
}
const totalRecords = parseInt(response.headers.get("X-Total-Count") || "0");
allStations = allStations.concat(data.records || data);
const totalPages = Math.ceil(totalRecords / pageSize);
hasMorePages = page < totalPages;
page++;
console.log(`✅ Page ${page - 1}/${totalPages} récupérée (${allStations.length} bornes trouvées)`);
}
console.log(`\n📊 Total: ${allStations.length} bornes trouvées\n`);
// Filtrer les bornes avec un ID valide
const stationsWithId = allStations.filter((station) => station.id !== undefined && station.id !== null);
console.log(`📋 ${stationsWithId.length} bornes avec ID valide\n`);
if (stationsWithId.length === 0) {
console.warn("⚠️ Aucune borne avec ID valide trouvée!");
return [];
}
// Demander confirmation
const confirmMessage = `Voulez-vous envoyer BootNotification à ${stationsWithId.length} bornes?\n\nLe script continuera même si certaines bornes échouent.`;
if (!confirm(confirmMessage)) {
console.log("❌ Opération annulée par l'utilisateur");
return [];
}
// Envoyer le triggerMessage BootNotification à chaque borne
const results = [];
let successCount = 0;
let errorCount = 0;
let timeoutCount = 0;
let networkErrorCount = 0;
console.log(`\n🚀 Démarrage de l'envoi à ${stationsWithId.length} bornes...`);
console.log(`💡 Le script continuera même si certaines bornes échouent ou ne répondent pas.\n`);
for (let i = 0; i < stationsWithId.length; i++) {
// Vérifier si l'arrêt a été demandé
if (stopBootNotificationScriptFlag) {
console.log(`\n🛑 Arrêt du script demandé. Arrêt après ${i}/${stationsWithId.length} bornes traitées.`);
break;
}
const station = stationsWithId[i];
const stationId = station.id;
const stationName = station.name || station.chargeboxIdentity || `Borne ${stationId}`;
try {
console.log(`[${i + 1}/${stationsWithId.length}] Envoi BootNotification à ${stationName} (ID: ${stationId})...`);
const triggerUrl = `${API_URL}/ocpp/trigger-message/${stationId}`;
// Créer un AbortController pour gérer les timeouts
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 1000); // Timeout de 5 secondes
let triggerResponse;
try {
triggerResponse = await fetch(triggerUrl, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
command: "BootNotification",
connector: 0,
}),
signal: controller.signal, // Ajouter le signal pour le timeout
});
clearTimeout(timeoutId); // Annuler le timeout si la requête réussit
} catch (fetchError) {
clearTimeout(timeoutId);
if (fetchError.name === "AbortError") {
timeoutCount++;
throw new Error("Timeout: La requête a pris plus de 1 secondes");
}
networkErrorCount++;
throw new Error(`Erreur réseau: ${fetchError.message}`);
}
if (!triggerResponse.ok) {
const errorText = await triggerResponse.text();
throw new Error(`HTTP ${triggerResponse.status}: ${errorText}`);
}
// Vérifier le Content-Type
const triggerContentType = triggerResponse.headers.get("content-type");
if (!triggerContentType || !triggerContentType.includes("application/json")) {
const errorText = await triggerResponse.text();
throw new Error(`Réponse non-JSON (${triggerContentType}): ${errorText.substring(0, 200)}`);
}
let triggerResult;
try {
triggerResult = await triggerResponse.json();
} catch (parseError) {
const errorText = await triggerResponse.text();
throw new Error(`Erreur parsing JSON: ${parseError.message}. Réponse: ${errorText.substring(0, 200)}`);
}
// Afficher la réponse complète pour les 3 premières requêtes (débogage)
if (i < 3) {
console.log(` 🔍 Réponse complète (débogage):`, JSON.stringify(triggerResult, null, 2));
}
// Extraire le statut selon différentes structures possibles
let status = null;
let errorMessage = null;
if (Array.isArray(triggerResult)) {
// Si c'est un tableau, prendre le premier élément
const firstResult = triggerResult[0];
if (firstResult) {
status = firstResult.result?.status || firstResult.status;
errorMessage = firstResult.error || firstResult.result?.error;
}
} else if (triggerResult && typeof triggerResult === "object") {
// Si c'est un objet
status = triggerResult.result?.status || triggerResult.status;
errorMessage = triggerResult.error || triggerResult.result?.error;
// Vérifier aussi si la réponse contient directement un message
if (!status && !errorMessage) {
// Peut-être que la réponse est directement le statut
status = triggerResult.message || triggerResult.data?.status;
}
}
// Si on n'a toujours rien trouvé, afficher la structure complète
if (!status && !errorMessage) {
console.warn(` ⚠️ Structure de réponse inattendue pour la borne ${stationId}:`, triggerResult);
errorMessage = `Structure de réponse inattendue: ${JSON.stringify(triggerResult).substring(0, 100)}`;
}
// Déterminer le succès
const isSuccess = status === "Accepted" || status === "accepted";
if (isSuccess) {
successCount++;
results.push({ id: stationId, name: stationName, success: true, status: status });
console.log(` ✅ Succès: ${status}`);
} else {
errorCount++;
const finalError = errorMessage || status || "Statut inconnu";
results.push({ id: stationId, name: stationName, success: false, error: String(finalError), status: status });
console.log(` ❌ Échec: ${finalError}${status ? ` (status: ${status})` : ""}`);
}
} catch (error) {
errorCount++;
const errorMessage = error instanceof Error ? error.message : String(error);
results.push({ id: stationId, name: stationName, success: false, error: errorMessage });
console.log(` ❌ Erreur: ${errorMessage}`);
// Le script continue avec la borne suivante même en cas d'erreur
console.log(` ⏭️ Passage à la borne suivante...`);
}
// Petit délai pour éviter de surcharger le serveur
if (i < stationsWithId.length - 1) {
await new Promise((resolve) => setTimeout(resolve, 100));
}
}
// Résumé
console.log("\n" + "=".repeat(60));
console.log("📊 RÉSUMÉ");
console.log("=".repeat(60));
if (stopBootNotificationScriptFlag) {
console.log("⚠️ Script arrêté par l'utilisateur");
}
console.log(`✅ Succès: ${successCount}`);
console.log(`❌ Échecs totaux: ${errorCount}`);
if (timeoutCount > 0) {
console.log(`⏱️ Timeouts: ${timeoutCount}`);
}
if (networkErrorCount > 0) {
console.log(`🌐 Erreurs réseau: ${networkErrorCount}`);
}
console.log(`📋 Total traité: ${results.length}/${stationsWithId.length}`);
if (stopBootNotificationScriptFlag && results.length < stationsWithId.length) {
console.log(`⏸️ Arrêté avant la fin (${stationsWithId.length - results.length} bornes non traitées)`);
} else if (results.length === stationsWithId.length) {
console.log(`✨ Toutes les bornes ont été traitées!`);
}
console.log("=".repeat(60));
// Afficher les échecs
const failures = results.filter((r) => !r.success);
if (failures.length > 0) {
console.log("\n❌ Bornes en échec:");
failures.forEach((f) => {
console.log(` - ${f.name} (ID: ${f.id}): ${f.error}`);
});
}
return results;
} catch (error) { console.error("❌ Erreur fatale:", error); throw error; } }
// Exporter pour utilisation dans la console if (typeof window !== "undefined") { window.triggerBootNotificationToAllStations = triggerBootNotificationToAllStations; window.stopBootNotificationScript = stopBootNotificationScript; console.log("✅ Script chargé!"); console.log("📝 Commandes disponibles:"); console.log(" - triggerBootNotificationToAllStations() : Démarrer"); console.log(" - stopBootNotificationScript() : Arrêter le script en cours"); }