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);