Mon scenario complet pour les plus courageux :
//Scenario d'interrogation d'une IA (provoqué par un autre scenario et necessitant les tags msg*, profile*, command, mode, piece)
//$openaiApiKey = "xxx"; //Token API
$openaiApiKey = $scenario->getData('OPENAI_API_KEY'); //TODO : Stokez votre Token openAI dans une variable OPENAI_API_KEY
$openaiModel = 'gpt-3.5-turbo'; //'gpt-4-turbo'
$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"); //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 IA
$eqCmdExclus = array("Rafraichir", "binaire","Thumbnail"); //Terme excluant certaines Actions
$debug_echo = true; //false; //Afficher les messages de debug dans les logs
$debug_eq_echo = false; //false; //Afficher la liste des equipements dans les logs
//if($debug_echo) {
// ini_set('display_errors', 1);
// ini_set('display_startup_errors', 1);
// error_reporting(E_ALL);
// $scenario->setLog("Affichage des erreurs");
//}else{
// $scenario->setLog("Pas d'affichage des erreurs");
//}
try {
/************************************************************************************************************************
* Récupération des parametres du scenario et construction des infos de la maison a envoyer (etat des capteurs et actions)
************************************************************************************************************************/
$tags = $scenario->getTags();
//Tags : profile, msg, command
$tagProfile = isset($tags['#profile#']) ? $tags['#profile#'] : 'Inconnu'; //Tag obligatoire permettant d'envoyer la notification de retour (via un scenario de notification)
//Le profile sert aussi a isoler les interractions dans un thread personnel.
$tagCommand = isset($tags['#command#']) ? $tags['#command#'] : '#[Aucun][Notification Telegram][Franck]#'; //TODO : Tag facultatif indiquant la commande de notification
$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((string) $piece)), array_map('strtolower',$piecesInclus)) && !in_array(strtolower(trim((string) $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
if (isset($tags['#msg#'])){ //Tag obligatoire contenant la question
$infoQuestion = trim((string) $tags['#msg#']);
if($debug_echo) echo "msg :".$infoQuestion."\n";
}else{
if($debug_echo) echo "Aucune 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?";
if($debug_echo) echo "msg par defaut :".$infoQuestion."\n";
}
$infoReponse = '';
// Nettoyage de la valeur récupérée
$prompt = trim((string) $infoQuestion);
//$scenario->setLog("Question récupérée : ".$prompt);
//if($debug_echo) echo "Question récupérée : ".$prompt";
//*** Recupération des infos de Jeedom ***
if($debug_echo) 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_eq_echo) echo "| -".$eqName."\n";
$isAuthorizedCatAction = false;
foreach ($eqActionInclusCategories as $eqActionInclusCategory){
if($eqLogic->getCategory($eqActionInclusCategory)==1) {
if($debug_eq_echo) echo "| Cat ".$eqActionInclusCategory." : OK (".$eqLogic->getCategory($eqActionInclusCategory).")\n";
$isAuthorizedCatAction = true;
break;
}else{
if($debug_eq_echo) echo "| Cat ".$eqActionInclusCategory." : KO! (".$eqLogic->getCategory($eqActionInclusCategory).")\n";
}
}
$cmds = $eqLogic->getCmd();
$eqCmds = [];
foreach ($cmds as $cmd) {
//if($debug_eq_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_eq_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(((strpos($eqName,"Lumière")!==false)||(strpos($eqName,"lampe")!==false)||(strpos($eqName,"led")!==false)) ) {
$cmdName = str_replace(["On","Off"], ["Allumer","Eteindre"], $cmdName);
}
if($debug_eq_echo) echo "| -".$cmd->getType()." ".$cmd->getHumanName()." ".$eqName." ".$cmdName."\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_eq_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","Eteinte","Allumée"), $cmdValue);//0=éteinte, 1=allumée
}
if($cmd->getUnite()!=="") { $cmdValue = $cmdValue.$cmd->getUnite(); }
if($debug_eq_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_eq_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_eq_echo) echo "equipements : \n$jeedom_json_result\n\n";
//echo "******* length : ".strlen($jeedom_json_result).".\n";
/*****************************************************************************************************
* Interrogation de l'assistant IA
******************************************************************************************************/
if($debug_echo) echo "Initialisation de l'assistant IA\n";
require_once '/var/www/html/plugins/script/data/openAIAssistant.class.php';
if($debug_echo) echo "Initialisation de l'assistant IA terminée\n";
//$ai = new OpenAIAssistant($openaiApiKey,'/tmp/jeedom_openai_config.json');
$ai = new OpenAIAssistant($openaiApiKey);
$assistantConfig = [
'name' => 'Assistant Domotique Jeedom',
'instructions' =>
'Tu es un assistant domotique Jeedom. Je m\'appelle ' . $tagProfile . ".\n" .
"Réponds toujours uniquement si tu es certain de la réponse et TOUJOURS sous la forme d'un JSON brut (et juste le JSON) contenant les champs suivants :\n" .
" - question (reprise de la question d'origine simple SANS le json des valeurs des capteurs),\n" .
" - response (réponse en langage naturel),\n" .
" - piece (nom de la ou les pièces identifiées dans la question ET dans les infos domotiques, séparées par des virgules. Laisser vide si non identifiée),\n" .
" - id (id de la commande, laisser vide si non trouvé),\n" .
" - mode (action ou info).\n" .
"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.\n" .
"Quand je te demande d'allumer ou d'ouvrir un équipement, ne me réponds pas qu'il est déjà éteint ou fermé (et inversement).\n" .
"Fais simplement ce que je te demande en renvoyant l'id de la commande dans le champ id, et la variable mode = 'action'.\n"
//."Voici les valeurs actuelles des capteurs de la domotique de la maison sur lesquelles baser ta réponse : ",// . $jeedom_json_result,
,'model' => $openaiModel
];
$profile = $tagProfile; //$scenario->getData('room');
if(strtolower($prompt)=="reset" || strtolower($prompt)=="init"){
if($debug_echo) echo "REINITIALISATION DU CONTEXTE\n";
$ai->resetConfig();
}
$message = $prompt."\n"."Voici les valeurs actuelles des capteurs de la domotique de la maison sur lesquelles baser ta réponse : ". $jeedom_json_result;
if($debug_echo) echo "Interrogation de l'assistant IA\n";
$response = $ai->ask($profile, $message, $assistantConfig);
//if($debug_echo) echo "Question scenario: (".$profile.") ".$message."\n";
if($debug_echo) echo "Réponse BRUT 1: ".$response."\n";
/****************************************************************************************************************
* Traitement de la réponse
*****************************************************************************************************************/
$responseData = json_decode($response, true);
if($debug_echo) echo "******** Traitement de la réponse ************\n";
if($debug_echo) echo "Réponse BRUT 2 : " . print_r($responseData, true)."\n";
if (isset($responseData['choices'][0]['message']['content'])) {
$aiResponse = trim((string) $responseData['choices'][0]['message']['content']);
} else {
$aiResponse = $responseData; //trim((string) $responseData);
}
if (isset($aiResponse)) {
if(!is_array($aiResponse)){
$aiResponse = preg_replace('/^```json\s*|\s*```$/m', '', $aiResponse);
//if($debug_echo) echo "DEBUG2 ".$aiResponse."\n";
$aiResponseJson = json_decode($aiResponse, true);
//if($debug_echo) echo "DEBUG3 ".$aiResponseJson."\n";
}else{
$aiResponseJson = $aiResponse;
}
if (isset($aiResponseJson['response'])) {
if($debug_echo) echo "JSON : ".$aiResponse."\n";
if($debug_echo) echo "Réponse JSON : ".$aiResponseJson['response']."\n";
$infoReponse = trim((string) $aiResponseJson['response']);
if (isset($aiResponseJson['mode']) && $aiResponseJson['mode']=='action') {
if($debug_echo) echo "Réponse JSON mode '".$aiResponseJson['mode']."'\n";
if (isset($aiResponseJson['id']) ) {
if($debug_echo) echo "Réponse JSON id '".$aiResponseJson['id']."'\n";
//TODO : Si action ou bloucle, rappel du scenario avec des tag action, etc...
$cmdId = $aiResponseJson['id'];
$cmdAction=cmd::byId($cmdId);
if (is_object($cmdAction)) {
//$eqAction = $cmdAction->getEqLogic();
if($cmdAction->getType()=='action'){
$cmdActionName = $cmdAction->getHumanName();
if($debug_echo) echo "ACTION ==> ".$cmdAction->getType()." ".$cmdActionName."\n";
$cmdAction->execCmd(); // Execute l'action
$infoReponse = $infoReponse." (".$cmdActionName.")";
}
//cmd::byId($idMsg)->event($myMsg);
}
}else{
$infoReponse = $infoReponse." (Commande non trouvée)";
}
}
} else {
if($debug_echo) echo "Réponse TEXT : ".$aiResponse."\n";
$infoReponse = $aiResponse;
}
} else {
$infoReponse = "Erreur : Aucune réponse valide reçue à la question : ".$infoQuestion.".";
//exit;
}
/****************************************************************************************************************
* Envoi de la réponse
*****************************************************************************************************************/
if($debug_echo) echo "****************************\n";
echo "Question de ".$tagProfile." : ".$infoQuestion."\n";
echo "Réponse de l'ia : ".print_r($infoReponse, true)."\n";
// Envoi de la réponse à l'équipement Jeedom (ID )
//TODO : si erreur de l'appel API, alors vidage du contexte
if($debug_echo) echo "Envoi à ".$tagProfile."\n";
$scenario = scenario::byId(387); //TODO : personnaliser le numero du scenario d'envoi ou de tout autre moyen de retour ici
$tags = $scenario->getTags();
#Ajouter des tags
$tags['#profile#'] = ($tagProfile!='Inconnu' && $tagProfile!='') ? $tagProfile : 'Franck'; //TODO : destinataire par defaut du message retour
$tags['#msg#'] = $infoReponse;
$tags['#command#'] = $tagCommand;
$scenario->setTags($tags);
$scenario->launch();
if($debug_echo) echo "*************************************************************\n";
/****************************************************************************************************************
*
*****************************************************************************************************************/
} catch (Exception $e) {
$errorMsg = "Erreur OpenAI: " . $e->getMessage();
if (isset($scenario)) {
$scenario->setLog($errorMsg);
}
echo $errorMsg . "\n";
// Vous pouvez aussi envoyer une notification, etc.
}
// ============================================
// COMMANDES UTILES (décommentez si besoin)
// ============================================
// Réinitialiser la configuration (supprime assistant et threads)
// $ai->resetConfig();
// Obtenir un nouveau thread pour une pièce spécifique
// $threadId = $ai->getOrCreateThread('cuisine');