Erreur SQL "Duplicate entry" (1062) lors de la création de commandes dans postSave()

Bonjour à tous,

Je suis en train de développer un plugin (pour les lampes Hao Deng) et je bute sur un problème persistant lors de la création automatique des commandes dans la fonction postSave().

Malgré plusieurs tentatives de vérification pour voir si la commande existe déjà (par LogicalID ou par Nom), je reçois systématiquement une erreur SQL Duplicate entry lors de la sauvegarde de l’équipement, comme si Jeedom tentait de recréer la commande alors qu’elle existe déjà en base.

Le problème : À chaque sauvegarde de l’équipement, j’obtiens cette erreur bloquante : [MySQL] Error code : 23000 (1062). Duplicate entry '11-Allumer' for key 'unique' : INSERT INTO cmd SET ...

Cela indique que la commande « Allumer » existe déjà pour l’équipement ID 11, mais mon code semble ne pas la « voir » avant d’essayer de la créer.

Mon code actuel (dans la classe de l’équipement) :

public function postSave() {
    if ($this->getId() == '') { return; }

    // Liste des commandes à créer
    $commands_config = array(
        'on' => array('name' => 'Allumer', 'icon' => '<i class="fas fa-lightbulb"></i>', 'type' => 'action', 'subtype' => 'other'),
        'off' => array('name' => 'Eteindre', 'icon' => '<i class="far fa-lightbulb"></i>', 'type' => 'action', 'subtype' => 'other'),
        'status' => array('name' => 'Etat', 'icon' => '', 'type' => 'info', 'subtype' => 'binary')
    );

    foreach ($commands_config as $logicalId => $data) {
        // 1. Tentative de récupération par ID Logique
        $cmd = $this->getCmd(null, $logicalId);
        
        // 2. Si pas trouvé, tentative par Nom (pour éviter le doublon de nom)
        if (!is_object($cmd)) {
            $cmd = $this->getCmd(null, null, $data['name']);
        }

        // 3. Si toujours pas trouvé, instanciation d'une nouvelle commande
        if (!is_object($cmd)) {
            $cmd = new haodengCmd();
            $cmd->setEqLogic_id($this->getId());
            $cmd->setLogicalId($logicalId);
            $cmd->setName(__($data['name'], __FILE__));
            $cmd->setType($data['type']);
            $cmd->setSubType($data['subtype']);
            $cmd->setIsVisible(1);
            if (isset($data['icon'])) {
                $cmd->setDisplay('icon', $data['icon']);
            }
            // C'est ici que ça plante avec le Duplicate Entry
            $cmd->save();
        }
        // Si la commande existait déjà, je ne fais rien (ou je mets à jour sans save pour l'instant pour éviter l'erreur).
    }
}

Ce que j’ai essayé :

  1. Utiliser cmd::byEqLogicIdCmdName pour chercher spécifiquement par nom.
  2. Utiliser try { $cmd->save(); } catch... pour ignorer l’erreur (mais l’erreur s’affiche quand même dans les logs et bloque parfois l’interface).
  3. Utiliser un itérateur sur cmd::byEqLogicId($this->getId()) pour scanner manuellement la liste réelle.

On dirait qu’il y a une désynchronisation entre le cache de Jeedom (qui me dit que la commande n’existe pas) et la base de données SQL (qui contient bien la commande).

Est-ce que l’un d’entre vous aurait la « bonne pratique » officielle pour gérer cette création de commandes dans postSave sans déclencher de conflit SQL ?

Merci d’avance pour votre aide !

Voila une capture de ma page santé

J’ai regardé très rapidement ton code mais déjà il faut que tu saches que lorsque tu fait un save() ça va lancer une nouvelle fois postsave c’est peut être là le problème.

bon, je devrai réviser avant de répondre :wink:

Voici comment je créés des commandes info dans le postsave:

    $this->createCmd('brand', 'Marque', 1, TYPE_INFO, SUBTYPE_STRING);
    $this->createCmd('model', 'Modèle', 2, TYPE_INFO, SUBTYPE_STRING);
    $this->createCmd('year', 'Année', 3, TYPE_INFO, SUBTYPE_STRING);
    $this->createCmd('type', 'Type', 4, TYPE_INFO, SUBTYPE_STRING); // Type de véhicule (électrique, hybride, etc.)
    $this->createCmd('carburant', 'Carburant', 5, TYPE_INFO, SUBTYPE_STRING, 1); // Type de carburant utilisé
    $this->createCmd('mileage', 'Kilométrage', 6, TYPE_INFO, SUBTYPE_NUMERIC, 1); // Kilométrage total du véhicule

Et pour des commandes action avec une boucle :


    // Actions disponibles pour interagir avec le véhicule (comme climatiser, charger, etc.)
    $actions = [
        ['refresh', 'Rafraichir', 39],
        ['climateNow', 'Climatiser', 40],
        ['stopClimateNow', 'Stop Climatiser', 41],
        ['chargeNow', 'Charger', 42],
        ['stopChargeNow', 'Stop Charger', 43],
        ['doorLock', 'Verrouiller', 44],
        ['doorUnlock', 'Déverrouiller', 45],
        ['lightFlash', 'Appel de phares', 46],
        ['hornBlow', 'Klaxonner', 47],
        ['vehicleFinder', 'Recherche véhicule', 48],
        ['sendPOI', 'Envoi POI', 49],
        ['hazardOn', 'Feux de détresse', 50],
        ['hazardOff', 'Stop feux de détresse', 51],
    ];
    foreach ($actions as $cmd) {
        $this->createCmd($cmd[0], $cmd[1], $cmd[2], TYPE_ACTION, SUBTYPE_OTHER);
    }

Et la fonction createCmd:

	private function createCmd($commandName, $commandDescription, $order, $type, $subType, $historized = 0, $template = [])
	{	
		$cmd = $this->getCmd(null, $commandName);
        if (!is_object($cmd)) {
            $cmd = new myToyotaCmd();
            $cmd->setOrder($order);
			$cmd->setName(__($commandDescription, __FILE__));
			$cmd->setEqLogic_id($this->getId());
			$cmd->setLogicalId($commandName);
			$cmd->setType($type);
			$cmd->setSubType($subType);
      $cmd->setIsHistorized($historized);
			if (!empty($template)) { $cmd->setTemplate($template[0], $template[1]); }
			$cmd->save();
      if ($historized==0) {$hist = 'non';}else{$hist='oui';}
			log::add('myToyota', 'debug', __('Ajout de la commande', __FILE__) . ' ' . $cmd->getName() . ' (LogicalId : '.$cmd->getLogicalId().'), ' . __('historisé :', __FILE__) . ' ' . $hist);
        }
  }

tu ne pourras pas trouver une commande par ce biais. Quand tu regardes la fonction getCmd elle ne fonctionne pas comme ça:

	/**
	 * get one or multiple cmd of the eqLogic
	 *
	 * @param string $_type ['action'|'info']
	 * @param string $_logicalId
	 * @param boolean $_visible
	 * @param boolean $_multiple
	 * @return cmd|cmd[]
	 */
	public function getCmd($_type = null, $_logicalId = null, $_visible = null, $_multiple = false) {
		if ($_logicalId !== null) {
			if (isset($this->_cmds[$_logicalId . '.' . $_multiple . '.' . $_type])) {
				return $this->_cmds[$_logicalId . '.' . $_multiple . '.' . $_type];
			}
			$cmds = cmd::byEqLogicIdAndLogicalId($this->id, $_logicalId, $_multiple, $_type, $this);
		} else {
			$cmds = cmd::byEqLogicId($this->id, $_type, $_visible, $this);
		}
		if (is_array($cmds)) {
			foreach ($cmds as $cmd) {
				$cmd->setEqLogic($this);
			}
		} elseif (is_object($cmds)) {
			$cmds->setEqLogic($this);
		}
		if ($_logicalId !== null && is_object($cmds)) {
			$this->_cmds[$_logicalId . '.' . $_multiple . '.' . $_type] = $cmds;
		}
		return $cmds;
	}

Merci bien je vais essayer se n est pas du tout mon domaine je vais voir ca .