ChatGPT dans Jeedom?

Alors pour ta demande, rien de plus simple : j’ai demandé à chatgpt AHAHA.

Alors visiblement il n’existe pas d’entrée API qui permettent de sélectionner directement des équipements en partant d’une vue.
Pour l’instant j’ai réussi à adapter du code que m’a craché chatGPT pour l’appliquer uniquement sur un objet (donc une pièce), et ça fonctionne.

Code bloc d’un scenario, le json est crashé dans le log :

$objectName = 'Bureau';
$object = jeeObject::byName($objectName);

if (is_object($object)) {
    // Récupération des équipements liés à cet objet
    $eqLogics = $object->getEqLogic(true);

    $data = [];
    foreach ($eqLogics as $eqLogic) {
        // Ajout des informations de l'équipement dans le tableau
      	$commands = [];
      	foreach ($eqLogic->getCmd() as $cmd) {
            // Filtrer uniquement les commandes de type 'info'
            if ($cmd->getType() === 'info') {
                $commands[] = [
                    'id' => $cmd->getId(),
                    'name' => $cmd->getName(),
                    //'type' => $cmd->getType(),
                    'subType' => $cmd->getSubType(),
                    'value' => $cmd->execCmd(), // Récupérer la valeur actuelle
                ];
            }
        }

        // Ajouter les équipements avec uniquement les commandes 'info'
        $data[] = [
            'id' => $eqLogic->getId(),
            'name' => $eqLogic->getName(),
            'type' => $eqLogic->getEqType_name(),
            'isEnable' => $eqLogic->getIsEnable(),
            'commands' => $commands,
        ];
    }

    // Conversion en JSON
    $json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);

    // Affichage dans le log du scénario
    $scenario->setLog($json);

    // Vous pouvez également sauvegarder ce JSON dans un fichier si besoin
    //$filePath = '/var/www/html/json_view_' . $objectName . '.json';
    //file_put_contents($filePath, $json);
} else {
    $scenario->setLog("L'objet '$objectName' n'existe pas.");
}

A creuser pour l’adapter à une vue, si un dev jeedom ou un dev plugin passe par là…

Fun fact : au début je ne filtrais pas par type d’objet (donc je bouclais à travers les commandes « info » ET « action »). Sauf que la boucle effectuait les « execCmd() » pour les actions => très dangereux. Du coup, quand il est arrivé sur l’équipement qui contrôle ma prise connectée derrière laquelle y’a tout mon équipement réseau (mes routeurs, switch, freebox, serveur NUC etc…), ben tout s’est éteint… OUPS !

2 « J'aime »

Je réponds à moi même, voici un code bloc de scenario permettant de fournir un json de la liste des équipements d’une vue (id de la vue à modifier dans le code) :

// ID de la vue
$viewId = 3;

$viewZones = viewZone::byView($viewId);

foreach ($viewZones as $viewZone) {
  $viewsData = viewData::byviewZoneId($viewZone->getId());
  
  $commands = [];
  foreach ($viewsData as $viewData) {
    $cmds = cmd::byEqLogicId($viewData->getLink_id());
    $eqLogicObj = eqLogic::byId($viewData->getLink_id());
    
    //Filtrer uniquement les equipements actifs
    if ($eqLogicObj->getIsEnable() == 1) {
      foreach ($cmds as $cmd) {
        // Filtrer uniquement les commandes de type 'info'
        if ($cmd->getType() === 'info') {
          $commands[] = [
            'id' => $cmd->getId(),
            'equipement' => $cmd->getHumanName(),
            'name' => $cmd->getName(),
            'type' => $cmd->getType(),
            'subType' => $cmd->getSubType(),
            'value' => $cmd->execCmd(),
          ];
        }
      }
    }
  }
}

// Afficher le tableau JSON
$json = json_encode($commands, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
$scenario->setLog($json);

1 « J'aime »

J’ai finalement fait la même chose. Je voulais eviter les boucles, mais au final, c’est ce qu’il y a de plus souple.
Du coup, ca marche nickel!
On peut interroger chatGPT sur toutes les infos de jeedom (a condition de les mettre dans la vue).

Bien vue le filtre sur les infos. J’imagine pas si c’était arrivé chez moi :slight_smile:

Je mets la v1 de mon scénario ici.
Il fonctionne en passant la question par le tag msg, mais ne renvoi la réponse que dans les logs.
Il suffit de renseigner une infos d’un virtuel en sortie pour ensuite faire ce que l’on veut avec.
Je vous conseille de créer une vue spécifique avec uniquement les équipements que vous souhaitez pouvoir interroger.
Et dans une V2, on verra pour lancer des actions :wink:

$openaiApiKey = "xxx";
$openaiUrl = "https://api.openai.com/v1/chat/completions";

//ini_set('display_errors', 1);
//ini_set('display_startup_errors', 1);
//error_reporting(E_ALL);

$tags = $scenario->getTags();

if (isset($tags['#msg#'])){
  $infoQuestion = trim($tags['#msg#']);
}else{
  echo "Erreur lors de la récupération de la question.\n";
  $infoQuestion = "Bonjour";
  //$infoQuestion = "Y'a-t-il du courrier?";
 // $infoQuestion = "Toutes les fenêtres sont-elles fermées?";
 // $infoQuestion = "Quelle est la consommation d'eau aujourd'hui?";
}

// Nettoyage de la valeur récupérée
$prompt = trim($infoQuestion);
$scenario->setLog("Question récupérée : $prompt");
echo "Question récupérée : $prompt\n";

//*** Recupération des infos de Jeedom ***
$viewId = 4;  // Home
$view = view::byId($viewId);
if ($view) {
  //$vue_json = $vue->toAjax('dashboard',true);
  $viewZones = viewZone::byView($viewId);
  $commands = [];
  
  foreach ($viewZones as $viewZone) {
    $viewsData = viewData::byviewZoneId($viewZone->getId());


    foreach ($viewsData as $viewData) {
      $cmds = cmd::byEqLogicId($viewData->getLink_id());
      $eqLogicObj = eqLogic::byId($viewData->getLink_id());

      //Filtrer uniquement les equipements actifs de type 'info'
      if ($eqLogicObj->getIsEnable() == 1) {
        foreach ($cmds as $cmd) {
          if ($cmd->getType() === 'info') {
            $commands[] = [
              'id' => $cmd->getId(),
              'equipement' => $cmd->getHumanName(),
              'name' => $cmd->getName(),
              //'type' => $cmd->getType(),
              //'subType' => $cmd->getSubType(),
              'value' => $cmd->execCmd(),
              'unit' => $cmd->getUnite(),
              'value date' => $cmd->getValueDate()
            ];
          }
        }
      }
    }
  }
  $jeedom_json_result = json_encode($commands, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);	
  //echo "equipements : $jeedom_json_result\n\n";
    
} else {
    echo "Vue introuvable";
}

//*** Requete ChatGPT ***
$data = [
    "model" => "gpt-3.5-turbo", //"gpt-4",
    "messages" => [
        ["role" => "system", "content" => "Tu es un assistant pour la domotique Jeedom."],
        ["role" => "system", "content" => "Répondre avec un maximum de 500 caractères."],
        ["role" => "system", "content" => "Remplace les points dans les valeurs par des virgules."],    
        ["role" => "system", "content" => "Et interdiction d'utiliser des emojis ou caractéres spéciaux."],
        ["role" => "system", "content" => "Voici un fichier contenant les informations de la domotique de la maison au format json : " . $jeedom_json_result],
        ["role" => "system", "content" => "Peux-tu répondre simplement à la question suivante : ".$prompt]
    ],
    "max_tokens" => 100,
    "temperature" => 0.7
];

// Configuration de la requête HTTP pour OpenAI
$options = [
    "http" => [
        "header" => "Content-Type: application/json\r\n" .
                    "Authorization: Bearer " . $openaiApiKey . "\r\n",
        "method"  => "POST",
        "content" => json_encode($data)
    ]
];
$context = stream_context_create($options);
$response = file_get_contents($openaiUrl, false, $context);

if ($response === FALSE) {
    echo "Erreur lors de la requête à l'API OpenAI.";
    //exit;
}

// Traitement de la réponse
$responseData = json_decode($response, true);
if (isset($responseData['choices'][0]['message']['content'])) {
    $chatgptResponse = trim($responseData['choices'][0]['message']['content']);
    echo "Réponse de ChatGPT : $chatgptResponse\n";
} else {
    echo "Erreur : Aucune réponse valide reçue de ChatGPT.";
    //exit;
}

// Envoi de la réponse à l'équipement Jeedom (ID )
//$cmdId = 13602;
//$encodedResponse = urlencode($chatgptResponse);

Bonjour à tous

Merci pour votre travail :+1:
C’est super intéressant.

En revanche, autant je maîtrise quand même plutôt correctement mon jeedom, autant là je suis largué complet sur comment vous procédez.:crazy_face:

Si vous aviez un p’tit tuto un peu plus détaillé ce serait cool (genre par exemple, ou est ce qu’on mets ce script, le détail des scénarios lancés etc …:pray::pray::pray:

Merci encore à vous :hugs:

Ça marche nickel, je vais bientôt pouvoir remplacer mes interactions avec ce scénario :smiley:
Par contre il a du mal avec quelques équipements avec plusieurs champs binaires, il s’emmêle un peu les pinceaux, donc il faudrait des équipements contenant qu’un seul état.

Je confirme.
J’ai fait evoluer le scenario (qui se declenche sur reception d’un message telegrame, et qui renvoit la réponse par un autre scenario de notification). Bref, j’ai remplacé les interractions, au moins dans le sens des interrogations. Pour les actions, c’est moins evident…

$openaiApiKey = "xxxx";
$openaiUrl = "https://api.openai.com/v1/chat/completions";
$debug_echo = false;
$sendToIA = true;
$pieces = array("Consos","Entrée","Salon","Salle à manger","Cuisine","Garage","12 niveau","Bibliothèque","Salle de bain","Chambre Parents","Bureau","Etage","Chambre Evan","Chambre Eliott");
//$pieces = array("Salon");
$equipementsExclus = array("Prise", "Volets", "Résumé", "Dodo", "Eteindre");

ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

$tags = $scenario->getTags();
//Tags : profile, msg, command
$tagProfile = isset($tags['#profile#']) ? $tags['#profile#'] : 'Franck';
$tagCommand = isset($tags['#command#']) ? $tags['#command#'] : '#[Aucun][Notification Telegram][Franck]#';

if (isset($tags['#msg#'])){
  $infoQuestion = trim($tags['#msg#']);
}else{
  echo "Erreur lors de la récupération de la question.\n";
  $infoQuestion = "Bonjour";
  //$infoQuestion = "Y'a-t-il du courrier?";
  //$infoQuestion = "Toutes les fenêtres sont-elles fermées?";
 // $infoQuestion = "Quelle est la consommation d'eau aujourd'hui?";
}
$infoReponse = '';
// Nettoyage de la valeur récupérée
$prompt = trim($infoQuestion);
$scenario->setLog("Question récupérée : $prompt");
echo "Question récupérée : $prompt\n";

//*** Recupération des infos de Jeedom ***
echo "Recupération des infos de Jeedom\n";
$commands = [];
$viewId = 4;  // Home
$view = view::byId($viewId);
$objectId = 1; //Dashboard
$object = jeeObject::byId($objectId);
if (is_object($object)) {
  $piece = $object->getName();
  $eqLogics = $object->getEqLogic(true);
  $childs = $object->getChilds();
  foreach ($childs as $child) {
    if ($child->getIsVisible() == 1) {
      $piece = $child->getName();
      //$eqLogics = $eqLogics + $child->getEqLogic(true);
      if(in_array($piece, $pieces, true)){
        $eqLogics2 = array_merge($eqLogics, $child->getEqLogic(true));
        $eqLogics = $eqLogics2;
      }
    }
  }
  //echo "\n \n";
  //echo "\nEquipements :\n\n";
  $piecePrev = "";
  foreach ($eqLogics as $eqLogic) {
    // Ajout des informations de l'équipement dans le tableau
    if ($eqLogic->getIsEnable() == 1 && $eqLogic->getIsVisible()==1) {
      $piece = $eqLogic->getObject()->getName();
      if ($piece != $piecePrev){
       	$piecePrev = $piece; 
      }
      $eqName = $eqLogic->getName();
            
      $found = false;
      foreach ($equipementsExclus as $element) {
          if (strpos($eqName, $element) !== false) {
              $found = true;
              break;
          }
      }
      
      if(!$found){
        //$commands[$piece][] = [$eqName => []];
        if($debug_echo) echo " -".$eqName."\n";
        $cmds = $eqLogic->getCmd();
        //Filtrer uniquement les equipements actifs de type 'info'
        $eqCmds = [];
        foreach ($cmds as $cmd) {
          //if($debug_echo) echo "  - ".$cmd->getType()." ".$cmd->getHumanName()."\n";
          if ( $cmd->getType() === 'info' && ($cmd->getIsVisible()==1 || $cmd->getName()=="Etat")) { //$cmd->getIsVisible()==1 &&
            if($debug_echo) echo "  - ".$cmd->getType()." ".$cmd->getHumanName()."\n";
            $name = $cmd->getName();
            //$name = $cmd->getHumanName();
            //$name = str_replace("][", " ", $name);
            //$name = str_replace(']', '', $name);
            //$name = str_replace('[', '', $name);
            //$name = trim($name);
            if($debug_echo) echo "  --".$name." : ".$cmdValue."\n";
            $cmdValue = $cmd->execCmd();
            if ($cmdValue !== "") {
              if(!(strpos($eqName,"Porte")===false)) $cmdValue = str_replace(array(0,1), array("Ouverte","Fermée"), $cmdValue);
              if(!(strpos($eqName,"Volet")===false)) $cmdValue = str_replace(array(0,1), array("Ouvert","Fermé"), $cmdValue);
              if(!(strpos($eqName,"Fenêtre")===false)) $cmdValue = str_replace(array(0,1), array("Fermée","Ouverte"), $cmdValue);
                           
              if(!(strpos($eqName,"Lumière")===false)||!(strpos($eqName,"lampe")===false)||!(strpos($eqName,"led")===false)) $cmdValue = str_replace(array(255,0,1), array("allumée","éteinte","allumée"), $cmdValue);//0=éteinte, 1=allumée
              
              if($cmd->getUnite()!=="") { $cmdValue = $cmdValue.$cmd->getUnite(); }
              //$commands[$piece][$eqName][] = [$name => $cmdValue];
              if($debug_echo) echo "  ---".$cmdValue." OK \n";
              $eqCmds[$name] = $cmdValue;
            /*$commands[$piece][$eqName][] = [
              //'id' => $cmd->getId(),
              'info' => $name,
              //'name' => $cmd->getName(),
              //'type' => $cmd->getType(),
              //'subType' => $cmd->getSubType(),
              'valeur' => $cmd->execCmd(),
              //'unit' => $cmd->getUnite(),
              //'value date' => $cmd->getValueDate()
            ];*/
            }else{
             if($debug_echo) echo "  ---".$cmdValue." vide \n"; 
            }
          }
        }
        if(!empty($eqCmds)){
          //echo "eqCmds : \n".json_encode($eqCmds, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)."\n\n";
          $commands[$piece][$eqName][] = $eqCmds;
        }
      }
    }
  }
  $jeedom_json_result = json_encode($commands, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);	
  if($debug_echo) echo "equipements : \n$jeedom_json_result\n\n";
  echo "******* length : ".strlen($jeedom_json_result).".\n";
}

//*** Requete ChatGPT ***
echo "Requete ChatGPT\n";
if($sendToIA){
  $data = [
      "model" => "gpt-3.5-turbo", //"gpt-4", //
      "messages" => [
          ["role" => "system", "content" => "Tu es un assistant pour la domotique."],
          //["role" => "system", "content" => "Réponds avec un maximum de 500 caractères."],
          //["role" => "system", "content" => "Remplace les points dans les valeurs par des virgules."],    
          //["role" => "system", "content" => "Réponse courte sans emojis ou caractéres spéciaux."],
        //["role" => "system", "content" => "La correspondance des états pour les fenêtres est : 1=ouverte, 0=fermée."],
        //["role" => "system", "content" => "Etat des portes : 0=ouverte, 1=fermée."],
        //["role" => "system", "content" => "Etat des volets : 0=ouvert, 1=fermé."],
        //["role" => "system", "content" => "Etat des lumières : 0=éteinte, 1=allumée."],
          //["role" => "user", "content" => "Voici un fichier json contenant les informations de la domotique de la maison : " . $jeedom_json_result],
        ["role" => "user", "content" => "Voici les informations de la domotique de la maison : " . $jeedom_json_result],
          ["role" => "user", "content" => "Peux-tu répondre à la question suivante : ".$prompt]
      ],
      "max_tokens" => 1000,
      "temperature" => 0.7
  ];

  // Configuration de la requête HTTP pour OpenAI
  echo "Configuration de la requête HTTP pour OpenAI\n";
  $options = [
      "http" => [
          "header" => "Content-Type: application/json\r\n" .
                      "Authorization: Bearer " . $openaiApiKey . "\r\n",
          "method"  => "POST",
          "content" => json_encode($data)
      ]
  ];
  $context = stream_context_create($options);

  $response = file_get_contents($openaiUrl, false, $context);

  if ($response === FALSE) {
      $infoReponse = "Erreur lors de la requête à l'API OpenAI pour la question : ".$infoQuestion.".";
      //exit;
  }

  // Traitement de la réponse
  echo "Traitement de la réponse\n";
  $responseData = json_decode($response, true);
  if (isset($responseData['choices'][0]['message']['content'])) {
      $chatgptResponse = trim($responseData['choices'][0]['message']['content']);
      //echo "Réponse de ChatGPT : $chatgptResponse\n";
      $infoReponse = $chatgptResponse;
  } else {
      $infoReponse = "Erreur : Aucune réponse valide reçue de ChatGPT à la question : ".$infoQuestion.".";
      //exit;
  }
}
echo "Réponse : ".$infoReponse."\n";
// Envoi de la réponse à l'équipement Jeedom (ID )
echo "Envoi à ".$tagProfile."\n";
$scenario = scenario::byId(387);
$tags = $scenario->getTags();
#Ajouter des tags
$tags['#profile#'] = $tagProfile;
$tags['#msg#'] = $infoReponse;
$tags['#command#'] = $tagCommand;
$scenario->setTags($tags);
$scenario->launch();

//Si pas besoin de lancer un scenario, il suffit d'executer une commande. Par ex telegram, pour la reponse (TODO)
//$cmdId = 13602;
//$encodedResponse = urlencode($infoReponse);


Edit : corrections de bugs

Par contre, j’ai parfois de sinfos contradictoire, sur l’état d’une fenetre par ex. J’ai indiqué dans le prompt les états en fonction des valeurs 0 ou 1, pour être sur.
Mais bon :
image

Et plutot qu’une vue, j’ai pris une liste de pieces dans la piece principale (qui est la maison dans mon cas). A adapter selon votre configuration.
J’ai aussi exclus les equipements et commandes non visibles, mais pas les infos ‹ Etat › non visibles, pour prendre en compte les widgets on/off ou l’infos n’est pas visible.
J’ai aussi revu la construction du json pour limiter la taille au minimum, car passé une certaine taille, chatGPT renvoit une erreur.

Le moteur GPT4 permet une plus grande limite, mais il coute plus chere a l’utilisation. Les tests d’hier m’ont couté 4€ :smiley:

1 « J'aime »

Bon, par contre, y’a un côté aléatoire dans les réponses qui m’embête un peu. Tu lui demande les lumières allumées, et elle te répond pas 2x la même chose. Même en précisant de manière exhaustive.
Difficile de lui faire confiance

1 « J'aime »

Ha mais ça, c’est normal pour une IA, elle réfléchit, tu lui donne beaucoup d’information - peut être trop pour la question posée - elle re-évalue donc tout à chaque fois, il y a forcément un autre changement qui justifie qu’elle n’a pas répondu pareil la 2e fois, mais quoi… Tu devrais peut être simplement lui demander ? :smiley:

Moi j’aurais gardé l’histoire des vues pour réduire au max les données, tout en gardant l’histoire des filtres sur les objets et les équipements. Je rajouterais aussi un filtre sur les commandes au cas où.
Après est-ce utile de définir le 1 ou 0 ? Je pense qu’il est assez performant pour le deviner.

Oui, je sais comment fonctionne une IA, mais la structure du fichier json n’a pas changé entre 2 appels. Je vais voir pour modifier le prompt pour qu’elle soit plus exhaustive.
D’ailleurs, j’ai deja remarqué que chatGPT a la facheuse tendance a affirmer des trucs faux quand elle ne sait pas. Et a s’entêter quand elle ne sait pas. C’est le modèle qui est comme ca…

Pour ce qui est de lui demander pourquoi elle dit tel ou tel truc, le problème, c’est que les appel API ne gardent pas le contexte. C’est d’ailleurs un peu dommage pour un usage domotique… Mais bon, ca viendra peut-etre…

Chacun fait comme il préfère :slight_smile:
Moi je ne voulais pas devoir rajouter des trucs dans la vue a chaque ajout dans ma domotique.
Du coup, je prend piece par piece, et je filtre.

Bien entendu, après c’est toujours sympa qu’on converge sur les mêmes idées pour la portabilité :slight_smile:

Je suis resté sur la vue, c’est rapide d’ajouter des équipements après tout, puis chez moi choisir le dashboard c’est un json trop gros pour l’api. Avec la vue, c’est déjà 8000 caractères, j’imagine même pas avec le dashboard, et je pense réduire en parsant la pièce dans la question.

Comme il philosophe trop, j’ai ajouté un contexte « Inutile de justifier. » mdr

Pour ceux qui garde le json avec 0 et 1, j’ai adapté comme ça :

if ( $cmd->getType() === 'info' && ($cmd->getName()=="Etat" || $cmd->getName()=="Température" || $cmd->getName()=="Humidité") ) {
....
              if(!(strpos($eqName,"Ampoule")===false)||!(strpos($eqName,"Lumière")===false)||!(strpos($eqName,"Lampe")===false)||!(strpos($eqName,"Lampadaire")===false)||!(strpos($eqName,"Applique")===false)||!(strpos($eqName,"Suspension")===false)) {
                $eqName = "Eclairage";
              }              
              if($cmd->getSubType() != 'binary') {
                if($eqName === "Eclairage") {
                  if ($cmdValue > 0) {
                    $cmdValue = 1;
                  }
                }
                else if($cmd->getUnite()!=="") { 
                  $cmdValue = $cmdValue.$cmd->getUnite();
                }
              }

Après j’ai le même comportement que toi, suivant la tournure de phrase, c’est tout et son contraire. Je vais réduire déjà le json, on verra si ça tourne plus rond.

J’ai quand même réduit le json, par défaut si pas de pièce, ce sera uniquement l’objet maison :

$pieces = array("Maison","Salon","Cuisine");
$pieceDansQuestion = false;

...

// Vérifions si la pièce est dans la question
foreach ($pieces as $piece) {
  if (!(strpos(strtolower($infoQuestion),strtolower($piece))===false)) {
    $pieceDansQuestion = true;
    if($debug_echo) {
      $scenario->setLog("La pièce \"$piece\" est dans la question.\n");
    }
    break;
  }
}
...
        // On ignore les pièces inutiles
        if ($pieceDansQuestion === true) {
          if(strpos(strtolower($infoQuestion),strtolower($piece))===false) {
            continue; 
          }
        }
        else {
           if($piece!=="Maison") {
            continue; 
          }         
        }

        $eqCmds = [];
        foreach ($cmds as $cmd) {

hello,
j’ai aussi un dev IA pour jeedom en cours, si vous êtes curieux de voir ce que cela donne.

1 « J'aime »

En voila une bonne idée. Je vais jeter un oeil de ce pas :slight_smile:

De mon coté, j’ai ajouté les actions à mon scenario.
ATTENTION, avec ce scénario, chatPGT est capable d’allumer/fermer/ouvrir des équipements physiques (pour l’instant filtré sur les catégories lumière, ouvrant et chauffage) . A manipuler en connaissance de cause!
Surtout que les réponses ne sont pas toujours très fiables. Exemple :
Q : Allumes la lumière de la cuisine
A : La lumière de la cuisine est déjà éteinte (mais elle l’allume parfois quand même!)

//Scenario d'interrogation de chatGPT (provoqué par un autre scenario et necessitant les tags msg*, profile*, command, mode, piece) 
$openaiApiKey = "xxx"; //Token de l'API chatGPT
$openaiUrl = "https://api.openai.com/v1/chat/completions"; //URL de l'API chatGPT
$debug_echo = false;	//Afficher les messages de debug dans les logs
$sendToIA = true;	//Envoyer la question a chatGPT

//"Maison","Jardin","Piscine","Sécurité","Consos","Entrée","Salon","Salle à manger","Cuisine","Garage","12 niveau","Bibliothèque","Salle de bain","Chambre Parents","Bureau","Etage","Chambre Evan","Chambre Eliott";
$piecesInclus = array("Maison","Jardin","Piscine","Consos","Entrée","Salon","Salle à manger","Cuisine","Garage","12 niveau","Bibliothèque","Salle de bain","Chambre Parents","Bureau","Etage","Chambre Evan","Chambre Eliott"); //Liste des pieces dans lesquelles récupérer les infos et actions des equipements
//$piecesInclus = array("Cuisine","Garage"); //TEST

$equipementsExclus = array("Prise", "Volets", "Résumé", "Dodo", "Eteindre"); //Mots excluant certains équipements de la liste
$eqActionInclusCategories = array("light","opening","heating");	//"heating","security","energy","automatism","multimedia","default" //Catégories d'équipement pilotables par chatGPT
$eqCmdExclus = array("Rafraichir", "binaire","Thumbnail"); //Terme excluant certaines Actions 

ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

$tags = $scenario->getTags();
//Tags : profile, msg, command
$tagProfile = isset($tags['#profile#']) ? $tags['#profile#'] : 'Franck';	//Tag obligatoire permettant d'envoyer la notification de retour (via un scenario de notification)
$tagCommand = isset($tags['#command#']) ? $tags['#command#'] : '#[Aucun][Notification Telegram][Franck]#'; //Tag facultatif indiquant la commande de notification (TODO)

$pieces = [];
$tagPieces = isset($tags['#piece#']) ? explode(',', $tags['#piece#']) : ''; //Tag optionnel permettant de filtrer sur une ou plusieurs pieces
if(isset($tags['#piece#'])){
  foreach ($tagPieces as $piece) {
  	if (in_array(strtolower(trim($piece)), array_map('strtolower',$piecesInclus)) && !in_array(strtolower(trim($piece)), array_map('strtolower',$pieces))) {
        $pieces[] = $piece;
    }
  }
}else{
	$pieces = $piecesInclus;
}
$tagMode = isset($tags['#mode#']) ? $tags['#mode#'] : 'action'; //'info'; //Tag optionnel permettant de forcer l'envois des commandes info ou action a chatGPT

if (isset($tags['#msg#'])){	//Tag obligatoire contenant la question a chatGPT
  $infoQuestion = trim($tags['#msg#']);
}else{
  echo "Erreur lors de la récupération de la question.\n";
  $infoQuestion = "Bonjour";
  //$infoQuestion = "Y'a-t-il du courrier?";
  //$infoQuestion = "Toutes les fenêtres sont-elles fermées?";
 // $infoQuestion = "Quelle est la consommation d'eau aujourd'hui?";
}

$infoReponse = '';
// Nettoyage de la valeur récupérée
$prompt = trim($infoQuestion);
$scenario->setLog("Question récupérée : $prompt");
echo "Question récupérée : $prompt\n";

//*** Recupération des infos de Jeedom ***
echo "Recupération des infos de Jeedom\n";
$commands = [];
$eqLogics = [];
foreach ($pieces as $piece) {
  $object = jeeObject::byName($piece);
  //$piece = $object->getName();
  if (is_object($object) && $object->getIsVisible() == 1) {
    //$eqLogics = $eqLogics + $object->getEqLogic(true);
      $eqLogics2 = array_merge($eqLogics, $object->getEqLogic(true));
      $eqLogics = $eqLogics2;
  }
}

$piecePrev = "";
foreach ($eqLogics as $eqLogic) {
  // Ajout des informations de l'équipement dans le tableau
  if ($eqLogic->getIsEnable() == 1 && $eqLogic->getIsVisible()==1) {
    $piece = $eqLogic->getObject()->getName();
    if ($piece != $piecePrev){
      $piecePrev = $piece; 
    }
    $eqName = $eqLogic->getName();
    
    $found = false;
    foreach ($equipementsExclus as $element) {
      if (strpos($eqName, $element) !== false) {
        $found = true;
        break;
      }
    }

    if(!$found){
      if($debug_echo) echo "| -".$eqName."\n";
      
      $isAuthorizedCatAction = false;      
      foreach ($eqActionInclusCategories as $eqActionInclusCategory){
        
        if($eqLogic->getCategory($eqActionInclusCategory)==1) {
          if($debug_echo) echo "|  Cat ".$eqActionInclusCategory." : OK (".$eqLogic->getCategory($eqActionInclusCategory).")\n";
          $isAuthorizedCatAction = true;
          break;
        }else{
        	if($debug_echo) echo "|  Cat ".$eqActionInclusCategory." : KO! (".$eqLogic->getCategory($eqActionInclusCategory).")\n";
        }
      }
      
      $cmds = $eqLogic->getCmd();
      $eqCmds = [];
      foreach ($cmds as $cmd) {
        //if($debug_echo) echo "  - ".$cmd->getType()." ".$cmd->getHumanName()."\n";
        $cmdName = $cmd->getName();
        
        $foundCmd = false;
        foreach ($eqCmdExclus as $element) {
          if (strpos($cmdName, $element) !== false) {
            $foundCmd = true;
            break;
          }
        }

        if(!$foundCmd){
          if (  ($cmd->getIsVisible()==1 || $cmdName=="Etat")) { //$cmd->getType() === 'info' &&
            //if($debug_echo) echo "|  -".$cmd->getType()." ".$cmd->getHumanName()."\n";
            //$cmdName = $cmd->getHumanName();
            //$cmdName = str_replace("][", " ", $cmdName);
            //$cmdName = str_replace(']', '', $cmdName);
            //$cmdName = str_replace('[', '', $cmdName);
            //$cmdName = trim($cmdName);
            $cmdType = $cmd->getType();
            if($tagMode==='action' && $cmdType==='action'){

              if($isAuthorizedCatAction){
                if($debug_echo) echo "|  -".$cmd->getType()." ".$cmd->getHumanName()."\n";

                $eqCmds[$cmdName] = [
                  'id' => $cmd->getId(),
                  //'nom' => $cmdName,
                  //'type' => $cmdType,
                  //'subType' => $cmd->getSubType(),
                  //'value' => $cmdValue,
                  //'unit' => $cmd->getUnite(),
                  //'value date' => $cmd->getValueDate()
                ];
              }

            }

            if($cmdType==='info'){
              $cmdValue = "";
              $cmdValue = $cmd->execCmd();
              if($debug_echo) echo "|  -".$cmd->getType()." ".$cmd->getHumanName()." : ".$cmdValue."";

              if ($cmdValue !== "") {
                if(!(strpos($eqName,"Porte")===false) && $cmdName=="Etat") $cmdValue = str_replace(array(0,1), array("Ouverte","Fermée"), $cmdValue);
                if(!(strpos($eqName,"Volet")===false) && $cmdName=="Etat") $cmdValue = str_replace(array(0,1), array("Ouvert","Fermé"), $cmdValue);
                if(!(strpos($eqName,"Fenêtre")===false) && $cmdName=="Etat") $cmdValue = str_replace(array(0,1), array("Fermée","Ouverte"), $cmdValue);
                if((!(strpos($eqName,"Lumière")===false)||!(strpos($eqName,"lampe")===false)||!(strpos($eqName,"led")===false)) && $cmdName=="Etat") $cmdValue = str_replace(array(255,0,1), array("allumée","éteinte","allumée"), $cmdValue);//0=éteinte, 1=allumée
                if($cmd->getUnite()!=="") { $cmdValue = $cmdValue.$cmd->getUnite(); }
                if($debug_echo) echo "  (".$cmdValue." :OK) \n";
                
                $eqCmds[$cmdName] = $cmdValue;
                //HS : trop volumineux :(
                /*$eqCmds[$cmdName] = [
                  'id' => $cmd->getId(),
                  //'nom' => $cmdName,
                  //'type' => $cmdType,
                  //'subType' => $cmd->getSubType(),
                  'value' => $cmdValue,
                  //'unit' => $cmd->getUnite(),
                  //'value date' => $cmd->getValueDate()
                ];*/

              }else{
                if($debug_echo) echo "  (".$cmdName." :vide) \n"; 
              }
            }

          }
      
      	}
      }
      if(!empty($eqCmds)){
        //echo "eqCmds : \n".json_encode($eqCmds, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)."\n\n";
        $commands[$piece][$eqName] = $eqCmds;
        //$commands[$piece][$eqName][] = ['catégorie' => $eqCategory];
      }
    }
  }
}

$jeedom_json_result = json_encode($commands, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);	
if($debug_echo) echo "equipements : \n$jeedom_json_result\n\n";
echo "******* length : ".strlen($jeedom_json_result).".\n";

//*** Requete ChatGPT ***
echo "Requete ChatGPT\n";
if($sendToIA){
  $data = [
      "model" => "gpt-3.5-turbo", //"gpt-4", //
      "messages" => [
          ["role" => "system", "content" => "Tu es la domotique de la maison."],
        ["role" => "system", "content" => "Réponds toujours uniquement si tu es certain de la réponse."],
        //["role" => "system", "content" => "Réponds précisement et exhaustivement, en précisant les informations qui t'ont permis de répondre"], //, mais de manière concise"], //, en précisant la source de l'information"],
        //["role" => "system", "content" => "Tu répondra par 'Je' en t'identifiant à la domotique de la maison"], 
        //["role" => "system", "content" => "Pas besoin de préciser 'Selon les informations fournies'"],
          //["role" => "system", "content" => "Réponds avec un maximum de 500 caractères."],
          //["role" => "system", "content" => "Remplace les points dans les valeurs par des virgules."],    
          //["role" => "system", "content" => "Réponse courte sans emojis ou caractéres spéciaux."],
          //["role" => "system", "content" => "La correspondance des états pour les fenêtres est : 1=ouverte, 0=fermée."],
          //["role" => "system", "content" => "Etat des portes : 0=ouverte, 1=fermée."],
          //["role" => "system", "content" => "Etat des volets : 0=ouvert, 1=fermé."],
          //["role" => "system", "content" => "Etat des lumières : 0=éteinte, 1=allumée."],
        
          //["role" => "system", "content" => "Les types infos sont des état d'équipement et les types action sont des commandes."],
          //["role" => "system", "content" => "Si la réponse concerne une commande de type 'info', répond normalement."],
          //["role" => "system", "content" => "Si la réponse concerne une commande de type 'action', répond toujours avec un JSON contenant les champs : id."],

          //["role" => "user", "content" => "Peux-tu répondre précisement et exhaustivement, mais de manière concise, à la question suivante en précisant les informations qui t'ont permis de répondre : ".$prompt],
        //["role" => "user", "content" => "En te basant sur ces informations, peux-tu répondre à la question suivante : ".$prompt]
        // ["role" => "user", "content" => "Question : ".$prompt]
      ],
      "max_tokens" => 1000,
      "temperature" => 0.7
  ];
	$data["messages"][] = ["role" => "system", "content" => "Réponds TOUJOURS sous la forme d'un json contenant les champs suivants : question (reprise de la question d'origine), response (réponse en language naturel), piece (nom de la ou les pièces identifiée dans la question ET dans les infos domotique, séparé par des virgules. Laisser vide si non identifiée), id (id de la commande, laisser vide si non trouvé), mode (action ou info)."];
  
  	$data["messages"][] = ["role" => "system", "content" => "Si la question est une demande d'action (de type allumer, éteindre, monter, descendre, ouvrir, fermer) : il faut obligatoirement renseigner une variable 'mode' avec la valeur 'action', sinon, la variable contiendra 'info'. Si tu trouves la commande dans le json."];
    
  $data["messages"][] = ["role" => "user", "content" => "Voici les valeurs des capteurs de la domotique de la maison sur lesquelles baser ta réponse: " . $jeedom_json_result];
  $data["messages"][] = ["role" => "user", "content" => "Question : ".$prompt];
    
  //echo "Data : ".json_encode($data)."\n";
  // Configuration de la requête HTTP pour OpenAI
  echo "Configuration de la requête HTTP pour OpenAI\n";
  $options = [
      "http" => [
          "header" => "Content-Type: application/json\r\n" .
                      "Authorization: Bearer " . $openaiApiKey . "\r\n",
          "method"  => "POST",
          "content" => json_encode($data)
      ]
  ];
  $context = stream_context_create($options);
  $response = file_get_contents($openaiUrl, false, $context);

  if ($response === FALSE) {
      $infoReponse = "Erreur lors de la requête à l'API OpenAI pour la question : ".$infoQuestion.".";
  }

  // Traitement de la réponse
  echo "Traitement de la réponse\n";
  $responseData = json_decode($response, true);
  if (isset($responseData['choices'][0]['message']['content'])) {
    $chatgptResponse = trim($responseData['choices'][0]['message']['content']);
	
    $chatgptResponseJson = json_decode($chatgptResponse, true);
    if (isset($chatgptResponseJson['response'])) {
      echo "JSON de ChatGPT : ".$chatgptResponse."\n";
      echo "Réponse JSON de ChatGPT : ".$chatgptResponseJson['response']."\n";
      $infoReponse = trim($chatgptResponseJson['response']);
      
      if (isset($chatgptResponseJson['mode']) && $chatgptResponseJson['mode']=='action') {
      echo "Réponse JSON mode '".$chatgptResponseJson['mode']."'\n";
      if (isset($chatgptResponseJson['id']) ) {
        echo "Réponse JSON id '".$chatgptResponseJson['id']."'\n";
        //TODO : Si action ou bloucle, rappel du scenario avec des tag action, etc...
		$cmdId = $chatgptResponseJson['id'];
        $cmdAction=cmd::byId($cmdId);
        if (is_object($cmdAction)) {
          //$eqAction = $cmdAction->getEqLogic();
          if($cmdAction->getType()=='action'){
            echo "ACTION ==> ".$cmdAction->getType()." ".$cmdAction->getHumanName()."\n";
            $cmdAction->execCmd(); // Execute l'action
          }
          //cmd::byId($idMsg)->event($myMsg);
        }
      }
    }
      
    } else {
      echo "Réponse TEXT de ChatGPT : ".$chatgptResponse."\n";
      $infoReponse = $chatgptResponse;
    }

  } else {
      $infoReponse = "Erreur : Aucune réponse valide reçue de ChatGPT à la question : ".$infoQuestion.".";
      //exit;
  }
}

echo "****************************\n";
echo "Question : ".$infoQuestion."\n";
echo "Réponse : ".$infoReponse."\n";
// Envoi de la réponse à l'équipement Jeedom (ID )


echo "Envoi à ".$tagProfile."\n";
$scenario = scenario::byId(387);
$tags = $scenario->getTags();
#Ajouter des tags
$tags['#profile#'] = $tagProfile;
$tags['#msg#'] = $infoReponse;
$tags['#command#'] = $tagCommand;
$scenario->setTags($tags);
$scenario->launch();

//Si pas besoin de lancer un scenario, il suffit d'executer une commande. Par ex telegram, pour la reponse (TODO)
//$cmdId = 13602;
//$encodedResponse = urlencode($infoReponse);
1 « J'aime »

Tu t’es bien amusé hahaha

J’ai vu qu’on pouvait intégrer un RAG, c’est à dire un fichier contenant la connaissance comme notre JSON. On pourrait du coup créer ce fichier dynamiquement et éviter de régénérer à chaque fois qu’on pose une question.

J’ai cru comprendre que ce qu’on faisait, a savoir intégrer le json sous forme de texte plutôt que de fichier, était équivalent a ajouter un fichier. Mais je vais creuser.
Et de toute façon, si on veut les états a jour, je ne vous pas d’autre façon que de fournir les infos a chaque fois …