Scenario de suppression massive des equipements et de leur historique

Bonjour,

J’aurai besoin d’aide pour un scénario qui supprime l’historique et les commandes MQTT.

Cela fait quelques années que j’utilise avec plaisir Jeedom.
Dernièrement j’ai palié au probleme du bluetooth qui n’est plus disponible depuis Debian 11 avec MQTT
(Antenne Bluetooth avec un ESP32, Open MQTT Gateway et jMQTT)

Depuis j’essuie des lenteurs de la part du systeme et même une impossibilité d’utiliser certains modules.
En parcourant les forums, j’ai trouvé d’abord un code qui reduisait sensiblement certaines historiques.
(Tuto - analyser les archives pour détecter des pbs (lenteurs / espaces disques))
Même si j’ai apprécié cette méthode, cela n’a pas résolu le problème mais amélioré certaines commandes et reduit la taille de mon backup.

J’ai compris que le probleme venait du MQTT quand le dernier équipement intégré avait un ID très élevé.
J’ai créé un script qui liste mes équipement MQTT et verifie son utilisation.
J’ai été supris de voir qu’il avait créé 325 000 équipements ?!
Pour ceux qui sont interessé, voici le code :

// Lister tous les equipements dans jeedom venant du plugins (j)MQTT et verification de leur utilisation => CSV

log::add('scenario', 'info', "🔍 Début du script optimisé de sauvegarde MQTT en CSV");

$debugPath = '/var/www/html/backup/debug_mqtt_';
// file_put_contents($debugPath . 'step1_start.txt', "Début du script\n");

// Fichier CSV de sortie
$filePath = '/var/www/html/backup/' . date('Ymd_His') . '_commandes_Mqtt.csv';
$content = "Source;Equipement;ID Equipement;Commande;ID Commande;Utilisation\n"; // En-tête CSV

// 📌 **Précharger tous les scénarios et équipements**
$allScenarios = scenario::all();
$allEqLogics = eqLogic::all();

// 📌 **Préparer une liste de toutes les commandes utilisées**
$usedCmds = [];

// 🔎 Parcourir les scénarios une seule fois
foreach ($allScenarios as $scenario) {
    $scenarioConfig = json_encode($scenario->getConfiguration());
    preg_match_all('/"cmd::(\d+)"/', $scenarioConfig, $matches);
    if (!empty($matches[1])) {
        foreach ($matches[1] as $cmdId) {
            $usedCmds[$cmdId]['scenario'] = true;
        }
    }
}

// 🔎 Parcourir tous les équipements une seule fois
foreach ($allEqLogics as $eq) {
    foreach ($eq->getCmd() as $cmd) {
        $cmdConfig = json_encode($cmd->getConfiguration());
        preg_match_all('/#(\d+)#/', $cmdConfig, $matches);
        if (!empty($matches[1])) {
            foreach ($matches[1] as $cmdId) {
                $usedCmds[$cmdId]['equipment'] = true;
            }
        }
    }
}

// 📌 **Sélectionner les équipements MQTT uniquement**
$eqLogics = [];
foreach ($allEqLogics as $eqLogic) {
    $eqType = strtolower($eqLogic->getEqType_name());
    if ($eqType === 'mqtt' || $eqType === 'jmqtt') {
        $eqLogics[] = $eqLogic;
    }
}

// file_put_contents($debugPath . 'step3_mqtt_eq.txt', "Nombre d'équipements MQTT détectés : " . count($eqLogics) . "\n");

if (empty($eqLogics)) {
    $content .= "Aucun équipement MQTT trouve;;;;;\n";
} else {
    foreach ($eqLogics as $eqLogic) {
        $eqName = $eqLogic->getName();
        $eqId = $eqLogic->getId();
        $source = (strpos(strtolower($eqLogic->getEqType_name()), 'jmqtt') !== false) ? 'jMQTT' : 'MQTT';
        
        $cmds = $eqLogic->getCmd();
        if (!empty($cmds)) {
            foreach ($cmds as $cmd) {
                $cmdId = $cmd->getId();
                $cmdName = $cmd->getName();
                
                $isUsedScenario = isset($usedCmds[$cmdId]['scenario']);
                $isUsedVirtual = isset($usedCmds[$cmdId]['equipment']);
                
                $status = "Non utilisee";
                if ($isUsedScenario && $isUsedVirtual) {
                    $status = "Utilise dans Scenarios & Virtuels";
                } elseif ($isUsedScenario) {
                    $status = "Utilise dans un Scenario";
                } elseif ($isUsedVirtual) {
                    $status = "Utilise dans un Virtuel";
                }
                
                $content .= "$source;$eqName;$eqId;$cmdName;$cmdId;$status\n";
            }
        } else {
            $content .= "$source;$eqName;$eqId;Aucune commande;;;;\n";
        }
    }
}

// Sauvegarde du fichier CSV
if (file_put_contents($filePath, $content) !== false) {
    // file_put_contents($debugPath . 'step7_success.txt', "✅ Fichier CSV généré avec succès.\n");
    log::add('scenario', 'info', "✅ Fichier CSV généré : " . $filePath);
} else {
    // file_put_contents($debugPath . 'step7_error.txt', "❌ Échec de l'écriture du fichier CSV.\n");
    log::add('scenario', 'error', "❌ Échec de l'écriture du fichier CSV.");
}

log::add('scenario', 'info', "🏁 Fin du script optimisé en CSV.");
// file_put_contents($debugPath . 'step8_end.txt', "🏁 Fin du script.\n");

J’ai donc fait une recherche sur la supression et suis tombé sur ce post : Supprimer des équipements Mqtt - #2 par Jeandhom
Cependant, en le passant dans chatgpt, il me suggere que l’historique ne serait pas supprimée et me propose ce code.

// Liste des IDs des équipements à supprimer
$equipementsASupprimer = array(1, 2, 3); // Remplacez par les IDs réels

foreach ($equipementsASupprimer as $equipId) {
    // Chargement de l'équipement
    $eqLogic = eqLogic::byId($equipId);
    if (is_object($eqLogic)) {
        // Suppression de l'historique des commandes associées
        foreach ($eqLogic->getCmd() as $cmd) {
            $cmd->removeHistory();
        }
        // Suppression de l'équipement
        $eqLogic->remove();
        $scenario->setLog("Équipement ID $equipId supprimé avec succès.");
    } else {
        $scenario->setLog("Équipement ID $equipId introuvable.");
    }
}

N’aimant pas supprimer quelque chose qui risque de perturber la stabilité du systeme et n’étant pas programmeur, je recherche un bonne ame pour me dire ce qu’il en pense.

Merci,
Benjamin.

Salut

As-tu aussi fait une whitelist pour ton open mqtt? Perso, j’habite en centre ville et je suis donc entouré d’appareil bluetooth. Sans whitelist mon mqtt crash en moins de 24h.

Antoine

Salut,

Hum avant de passer du code un peu au hasard perso je ferais avec les fonctions standard de jeedom …

Il y a une fonctionnalité native de jeedom qui permet de faire un clean de la DB et qui permet normalement justement de supprimer tous les historiques de commandes qui n’existent plus.

Elle se lance depuis :

Par contre tu es certain que tu en as des historiques ? Car si ce sont des équipements fantome pas sur qu’ils aient fait beaucoup d’historiques …

Pour le savoir tu peux passer la requête SQL suivante depuis le menu " Administration Base de données "

SELECT DISTINCT cmd_id FROM `history` WHERE cmd_id NOT IN (select id from cmd) UNION SELECT DISTINCT cmd_id FROM `historyArch` WHERE cmd_id NOT IN (select id from cmd)

Si elle affiche quelque chose c’est que tu as bien des id de commandes présents dans les tables d’historique mais pas dans la table cmd qui contient le paramétrage de tes commandes

1 « J'aime »

Merci à vous pour vos réponses.

As-tu aussi fait une whitelist pour ton open mqtt?

Je l’ai fait sur un des deux ESP. Etant en campagne, je n’ai pas ce probleme.
Par contre, j’utilise le bluetooth pour mes sondes de temperature Wiaomi.
J’ai vu que maintenant, on peux passer en Zigbee.
Cela va me prendre du temps, mais je penser migrer et arreter ce bluetooth.
Dommage pour une fonctionalité native lors du dernier Debian.

Il y a une fonctionnalité native de jeedom qui permet de faire un clean de la DB

Merci pour le hint.
Je vais prendre mon mal en patience et supprimer les éléments par batch de commande ID et puis je lancerai cette commande.

Malheureusement, j’ai lancé cette commande qui a supprimer non seulement les commandes d’MQTT mais aussi les commandes de jMQTT que je souhaitais garder. Merci les backups :

// Liste des IDs des équipements à exclure du traitement
$exclure = array(816, 815, 821); // Remplacer par les ID des équipements à exclure

// Récupérer tous les équipements de type 'MQTT'
$allEqLogic = eqLogic::byType('MQTT'); 

foreach ($allEqLogic as $eqLogic) {
    // Exclure les équipements dont l'ID est dans la liste $exclure
    if (in_array($eqLogic->getId(), $exclure)) {
        continue; // Passer à l'équipement suivant
    }
    
    // Log de l'équipement à supprimer
    $scenario->setLog('Équipement à effacer : ' . $eqLogic->getId() . ' ==> ' . $eqLogic->getHumanName());

    // Suppression de l'historique des commandes associées à cet équipement via SQL
    $cmdIds = array();
    foreach ($eqLogic->getCmd() as $cmd) {
        $cmdIds[] = $cmd->getId();
    }

    // Vérification qu'il y a des commandes associées
    if (!empty($cmdIds)) {
        // Supprimer l'historique des commandes
        $sql = "DELETE FROM history WHERE cmd_id IN (:cmdIds)";
        $params = array(':cmdIds' => implode(',', $cmdIds));
        DB::Prepare($sql, $params);  // Utilisation de DB::Prepare avec les paramètres

        // Supprimer l'archivage de l'historique des commandes
        $sqlArch = "DELETE FROM historyArch WHERE cmd_id IN (:cmdIds)";
        DB::Prepare($sqlArch, $params);  // Utilisation de DB::Prepare avec les paramètres
    }

    // Suppression de l'équipement
    $eqLogic->remove();
    $scenario->setLog("Équipement ID " . $eqLogic->getId() . " supprimé avec succès.");
}

Si quelqu’un sait comment isoler MQTT de jMQTT, je suis preneur.

Bonjour,

La méthode remove() sur un eqLogic supprime les commandes de cet équipement avec tous les historiques. Inutile de les supprimer avant. ChatGPT se trompe.

A+
Michel

1 « J'aime »

Merci Michel pour tes conseils.

Je pense que mon probleme vient de la ligne:

$allEqLogic = eqLogic::byType(‹ MQTT ›);

Cela retire toutes les commande de jMQTT que je souhaite garder.
Une idée ?

Il faudrait tester ceci :

if (in_array($eqLogic->getId(), $exclure) || $eqLogic->getEqType_name() != 'MQTT') {

Pour être sûr de filtrer correctement.

Mais c’est curieux que les équipements de jMQTT soient retournés par eqLogic::byType('MQTT')
:thinking: