Plugin pour gestion de Spa GECKO avec controleur à distance GECKO in touch 2

Bonjour,

Nouvel utilisateur à la fois du forum et de la box Luna, je m’interroge sur la possibilité de gérer notre spa équipé du système de gestion canadien GECKO. Il existe un module Gecko in touch 2 en option permettant de connecter le spa en wifi et de le piloter via l’application smartphone GECKO (températures, programmes…). Il est aussi possible de piloter le spa via ce module en utilisant google home ou alexa.
D’où ma question, y a t-il un moyen de gérer le pilotage dans jeedom en utilisant ce module complémentaire? Et par quel moyen? Plugin? …

En vous remerciant par avance

Bonne journée

As-tu regarder là, à priori, une librairie existe

Si tu t’y connais en dev, tu peux essayer de developper un plugin.
Sinon, ca semble dejà integré chez home assistant

Norbert

Bonjour,

Merci de ton retour. Cela sous-entend qu’il faut installer le logiciel home assistant sur Pc ou… autre. Gérer le module spa depuis home assistant et du côté jeedom, télécharger le plugin homme assistant ?

Merci

Hello
Avez vous réussit à vous en sortir ?
Un ami a ce boitier de contrôle et m’a demandé de l’intégrer à jeedom … :slight_smile: … par contre je rencontre une difficulté … la lib arrive a se connecter une fois sur 30 au spa … avez-vous le meme comportement ?

[2023-05-30 15:56:41][SCENARIO] Returned with status 1
[2023-05-30 15:56:41][SCENARIO] 	-> ["Number of arguments: 1 arguments.","Argument List: ['\/var\/www\/html\/plugins\/script\/data\/gecko_discover.py']","","","        ","        ----------------------------- USE AT YOUR OWN RISK -----------------------------","","        This code will allow you to make changes to your spa configuration that is","        outside of what the app, top panel and side panel settings allow. I've not","        tested every setting and it might be that you prevent your spa pack from","        operating as it used to do.","","        I strongly suggest dumping the configuration values with the \"config\" command","        and recording them somewhere safe.","","        <\/Disclaimer>","","","Starting discovery process...Found 1 spas","Connecting to spa `SPA du 113` at 192.168.1.20 ..."]
[2023-05-30 15:56:41][SCENARIO] Number of arguments: 1 arguments.
[2023-05-30 15:56:41][SCENARIO] Argument List: ['/var/www/html/plugins/script/data/gecko_discover.py']
[2023-05-30 15:56:41][SCENARIO]
[2023-05-30 15:56:41][SCENARIO]
[2023-05-30 15:56:41][SCENARIO]         
[2023-05-30 15:56:41][SCENARIO]         ----------------------------- USE AT YOUR OWN RISK -----------------------------
[2023-05-30 15:56:41][SCENARIO]
[2023-05-30 15:56:41][SCENARIO]         This code will allow you to make changes to your spa configuration that is
[2023-05-30 15:56:41][SCENARIO]         outside of what the app, top panel and side panel settings allow. I've not
[2023-05-30 15:56:41][SCENARIO]         tested every setting and it might be that you prevent your spa pack from
[2023-05-30 15:56:41][SCENARIO]         operating as it used to do.
[2023-05-30 15:56:41][SCENARIO]
[2023-05-30 15:56:41][SCENARIO]         I strongly suggest dumping the configuration values with the "config" command
[2023-05-30 15:56:41][SCENARIO]         and recording them somewhere safe.
[2023-05-30 15:56:41][SCENARIO]
[2023-05-30 15:56:41][SCENARIO]         
[2023-05-30 15:56:41][SCENARIO]
[2023-05-30 15:56:41][SCENARIO]
[2023-05-30 15:56:41][SCENARIO] Starting discovery process...Found 1 spas
[2023-05-30 15:56:41][SCENARIO] Connecting to spa `SPA du 113` at 192.168.1.20 ...

Quelques fois c’est instantané (très rarement) et 95% des fois je n’arrive pas à me connecter …

@chris94440

Bon c’est bon j’ai trouvé ou était le soucis … entre le clavier et la chaise …
Je vais dans un premier temps gérer les actions et statuts via scenario et bloc code … mais cela ne sera pas tres performant car il y aura une connexion au spa a chaque demande … si je suis motivé je ferais un plugin … mais avant il faut que j’apprenne le python … :slight_smile: … souhaitez vous que je partage mon / mes scénario ?

Salut. Ouai je veux bien que tu partage tes scénarios je suis en train de regarder pour gérer mon spa depuis jeedom également via le in touch 2 :wink:

Slt
Je sais pas si c’est un problème de matériel ou une solution pas stable mais la connexion au gecko de mon ami se fait une fois sur 20 … :frowning:
Pouvez-vous essayer d’exécuter le script python
gecko_api_getState.txt (5,0 Ko)
(en renommant le .txt en .py) et en ayant au préalablement installé la lib : pip install geckolib et modifier l’uuid (Online UUID Generator Tool) dans le script

Et me donner le résultat svp
@chris94440

Bonjour,

Je viens de voir à l’instant les échanges. Après le poste du sujet, je n’ai pour ma part pas avancé sur le sujet car je ne suis pas capable de developper quoi que cela soit… Du coup je n’ai pas encore acheté l’interface gecko.
Je suis donc intéressé par vos retourd d’experience et marche à suivre pas à pas :slight_smile:

En vous remerciant

Fabien

Moi aussi je débute avec python3 … mais y a moyen de faire qq chose … pas hyper optimisé sans plugins mais fonctionnel …

Pour le moment le script retourne un json qu’un scenario jeedom peut interprété

Peux tu installer la lib et lancer la commande comme spécifié dans mon précédent post et me donner la réponse stp
@chris94440

Bon a priori pb de pisitionnement du boitier … mon ami l’a déplacé et tout sembke ok…je vais donc continuer avec la partie execution d’action (lumieres,pompes,etc…)

Désolé je n’avais pas suivi le sujet ces derniers jours.
J’ai eu des soucis avec le premier module que j’ai reçu. Même à 1m la connexion échouait 9 fois sur 10. J’ai fait marcher le SAV et maintenant c’est beaucoup plus stable.
Sachant que effectivement avec mon installation les deux boitiers sont vraiment très proche l’un de l’autre (1m) avec un mur ossature bois et 15cm de laine de bois).
Mais j’ai vu sur d’autres forum que la connexion peut parfois être très capricieuse suivant l’emplacement des boitiers.

En ce qui concerne le développement d’un plugin j’avais déjà fait les bases du plugin eufy que j’ai refilé à un autre membre faute de temps.
Si jamais les scripts fonctionnent je pourrais faire de même pour le gecko in touch 2 mais je manque cruellement de temps :smiley: donc si vous compter sur moi il ne faudra pas être trop pressé lol

J’ai essayé ton script mais il ne passe pas chez moi.
Il n’aime pas les printf avec le f pour avoir les variables ni ceux de ce type print(« Locating spas on your network « , end= » », flush=True) avec les paramètres end et flush. Syntaxe non reconnue.

c’est étrange quelle version de python utilises tu ?

j’ai la version 3.7.3 à priori

moi aussi je suis en 3.7.3

Peut etre un problème de conversion du fichier lors de l’import … as tu essayé de faire un dos2unix

dos2unix gecko_api_getState.py

@chris94440
Update:
La partie recuperation des donnees et creation a la volee dun virtuel et des commandes associees est ok …des que jai un peu de temps je fais la partie envoi dordre et je vous poste tout cela

Hello
Dsl pour le temps mais j’avais pas trop de temps à accorder à ce sujet et surtout j’avais pas accès au spa de mon amis pour tester …
J’ai tout fait en mode scenario …
J’ai crée 2 scripts python que j’ai positionné dan /var/www/html/plugins/script/data
Le zip contenant les 2 scripts à renommer en .zip et à extraire
apiGeckoJeedom.txt (4,6 Ko)
En fonction de comment vous les intégrer sur votre jeedom il faudra leur donner le droit d’exécution et peut etre faire une conversion doc2unix

chmod +x sur chacun des scripts
doc2unix sur chacun des scripts

Apres tout se passe côté scénario

Premier scénario nommé : sGetSpasStateForAllSpa
Il permet de récupérer toutes les infos de tous va spa gecko et va tout stocker dans une variable nommé ListSpaIdentifier

$scenario->setLog('Start script discover and get state for all spa');
$aListSpaIdentifier=array();

exec('python3 /var/www/html/plugins/script/data/gecko_api_getState.py', $output, $retval);
$scenario->setLog("Returned status $retval");

foreach($output as $line){
  	if ((strtolower('ResponseState') == strtolower($line)) or 
              (strpos(strtolower('ResponseState'),strtolower($line)) !== false ) or 
              (strpos(strtolower($line),strtolower('ResponseState')) !== false )) {
		$jsonResponse=explode('|',$line)[1];

      	$aResponse=json_decode($jsonResponse, true);
      	if (sizeof($aResponse) > 0) {
            $scenario->setLog(json_encode($aResponse));
            $scenario->setData('ListSpaIdentifier',json_encode($aResponse));
        }
    }
}

$scenario->setLog('End script discover and get state for all spa');

Il vous faut alors le scheduler toutes les 5/10 minutes à votre convenance

Second scénario nommé : sCreateUpdateCmdSpa
Celui va se charger de crée automatiquement le virtuel et les commandes associées … mais également de mettre à jour les données.

Il vous faut modifier en début de script l’id de l’objet parent pour spécifier l’emplacement du virtuel … ici objet jardin avec l’id 1

$scenario->setLog('Start script create / update virtual and cmd for a spa gecko');

//parent id -> jardin
$parentID=1;

$resp= $scenario->getData(str_replace(array('#','variable(',')'),array(''),$scenario->getRealTrigger()));
$resp= $scenario->getData('ListSpaIdentifier');


$scenario->setLog('		-> ' . json_encode($resp));
if (array_key_exists('spas', $resp)) {
  foreach($resp['spas'] as $spa) {
    $scenario->setLog($spa['name'] . ' -> ' . $spa['id']);

    $aResp=createVirtual($scenario,$parentId,$spa['name'],$spa['id']);
    //create raw data
    createCmd($scenario,$aResp[0],$aResp[1],'raw_data_'.$spa['id'],'info','other','','','',json_encode($spa),false,'','','','');
    
    if (array_key_exists('cmds', $spa)) {
      foreach($spa['cmds'] as $cmd) {
        $scenario->setLog('  - ' . $cmd['name'] . ' -> ' . json_encode($cmd));
        createCmds($scenario,$cmd,$aResp);

      }
    }
  }
}


$scenario->setLog('End script get state for spa');

function createCmds($scenario,$cmd,$virtual) {
	switch (true) {
        case stristr($cmd['name'],'water_care'):
            createCmd($scenario,$virtual[0],$virtual[1],$cmd['name'],'info','numeric','','','',$cmd['state'],true,'','',$cmd['stateList'],'');
        	if (array_key_exists('stateList', $cmd)) {
              	$i=0;
            	foreach($cmd['stateList'] as $state) {
                	createCmd($scenario,$virtual[0],$virtual[1],$cmd['name'].'_'.$state,'action','other','','','','',false,$cmd['name'],$i,'','');
                  	$i++;
                }
            }
            break;
        case stristr($cmd['name'],'lights'):
            createCmd($scenario,$virtual[0],$virtual[1],$cmd['name'],'info','binary','','','',($cmd['state']==1?'1':'0'),true,'','',$cmd['stateList'],'');
        	if (array_key_exists('stateList', $cmd)) {
            	foreach($cmd['stateList'] as $state) {
                	createCmd($scenario,$virtual[0],$virtual[1],$cmd['name'].'_'.$state,'action','other','','','','',false,$cmd['name'],$state,'','');
                }
            }
            break;
        case stristr($cmd['name'],'water_heater'):
            createCmd($scenario,$virtual[0],$virtual[1],$cmd['name'],'info','numeric',$cmd['unit'],$cmd['max_temp'],$cmd['min_temp'],$cmd['current_temp'],true,'','','','');
			createCmd($scenario,$virtual[0],$virtual[1],'current_operation','info','string','','','',$cmd['current_operation'],true,'','','','');
			$cmdTarget=createCmd($scenario,$virtual[0],$virtual[1],'target_temperature','info','numeric',$cmd['unit'],'','',$cmd['target_temperature'],true,'','','','');    
        	createCmd($scenario,$virtual[0],$virtual[1],$cmd['name'].'_set_temperature','action','slider',$cmd['unit'],$cmd['max_temp'],$cmd['min_temp'],'',false,'target_temperature','','',$cmdTarget->getId());
            break;
      	case stristr($cmd['name'],'pumps'):
            createCmd($scenario,$virtual[0],$virtual[1],$cmd['name'],'info','string','','','',$cmd['mode'],true,'','',$cmd['stateList'],'');
        	if (array_key_exists('stateList', $cmd)) {
            	foreach($cmd['stateList'] as $state) {
                	createCmd($scenario,$virtual[0],$virtual[1],$cmd['name'].'_'.$state,'action','other','','','','',false,$cmd['name'],$state,'','');
                }
            }
        	break;
      	case stristr($cmd['name'],'sensor'):
            createCmd($scenario,$virtual[0],$virtual[1],$cmd['name'].'_'.$cmd['label'],'info','string','','','',$cmd['state'] ? 'true' : 'false',true,'','',$cmd['stateList'],'');        
        	if (array_key_exists('stateList', $cmd)) {
            	foreach($cmd['stateList'] as $state) {
                	createCmd($scenario,$virtual[0],$virtual[1],$cmd['name'].'_'.$state,'action','other','','','','',false,$cmd['name'].'_'.$cmd['label'],$state,'','');
                }
            }
            break;        
        default:
            $scenario->setLog('Cmd not manage : ' . $cmd['name'],true,'');
            break;
    }
}

function createVirtual($scenario,$parentID,$virtualName,$virtualLogicalId) {
	$scenario->setLog("	  	    * Function createVirtual Start");
	$scenario->setLog("	  	      - params : $parentID");
  	
  	$virtual = eqLogic::byLogicalId($virtualLogicalId, 'virtual');
    if (!is_object($virtual)) {
      $scenario->setLog("	  	        - Logic Equipment not exist $virtualName ==> need to create with parent id $parentID ");
      $virtual = new virtual();
      $virtual->setName($virtualName);
      $virtual->setLogicalId($virtualLogicalId);
      $virtual->setEqType_name('virtual');
      $virtual->setObject_id($parentID);
      $virtual->setIsEnable(1);
      $virtual->setIsVisible(1);      
      $virtual->save();
    }
    $vId=$virtual->getId();
  	return array($virtual,$vId);
}

function createCmd($scenario,$virtual,$virtualId,$cmdName,$type,$subtype,$unit,$maxValue,$minValue,$value,$historized,$infoName,$infoValue,$listValue,$idLinkedValue) {
	$scenario->setLog("	  	    * Function createCmd Start -> " . $cmdName . ' value : ' .$value);
  
  	$virtuelCmd=$virtual->getCmd(null, $cmdName);
    if (!is_object($virtuelCmd)) {
        $scenario->setLog("	  	          - cmd do not exist ==> need to create it");
        $virtuelCmd = new virtualCmd();
        $virtuelCmd->setName($cmdName);
        $virtuelCmd->setEqLogic_id($virtualId);
        $virtuelCmd->setLogicalId($cmdName);
        $virtuelCmd->setType($type);
        //$virtuelCmd->setSubType('numeric');
      	$virtuelCmd->setSubType($subtype);
	    $virtuelCmd->setIsHistorized($historized);
      
      	
    }
  	
  	if ($unit != '') {
      $virtuelCmd->setUnite($unit);
    }

    if ($maxValue != '') {
      $virtuelCmd->setConfiguration("maxValue",$maxValue);
    }

    if ($minValue != '') {                  
      $virtuelCmd->setConfiguration("minValue", $minValue);
    }

    if ($infoName !='') {
      $virtuelCmd->setConfiguration("infoName", $infoName);          
    }

    if ($infoValue != '') {
      $virtuelCmd->setConfiguration("value",$infoValue);                    
    }
  
  	if ($listValue !='') {
    	$virtuelCmd->setConfiguration("listValue",$listValue); 
    }
  
  	if ($idLinkedValue != '') {
      	$scenario->setLog('Id linked value : ' . $idLinkedValue);
      	$virtuelCmd->setConfiguration("infoId",$idLinkedValue);
      	$virtuelCmd->setValue($idLinkedValue);
    }
  
  
  	if ($value != '') {
      	$scenario->setLog("	  	          - update value => " . $value);
    	$virtual->checkAndUpdateCmd($cmdName, $value);
    }
  
    $virtuelCmd->save();
  
  	return $virtuelCmd;

}

Ce scénario devra être provoqué par le changement de valeur de la variable ListSpaIdentifier

#variable(ListSpaIdentifier)#

Troisième scénario permet de gérer les actions : sSpaActions

$scenario->setLog('Start script for lauching action on Gecko spa');

$trigger=$scenario->getRealTrigger();


$scenario->setLog(' - trigger : ' . json_encode($trigger));

$cmd=cmd::byId(str_replace(array('#'),array(''),$trigger));

if (is_object($cmd)) {
  	
  	$scenario->setLog(' - cmd : ' . $cmd->getName());
  	//$isSameValue=isSameValue($scenario,$cmd);

  	$spaInfo=getSpaId($scenario, $cmd);
  	$scenario->setLog('		=> spa id : ' . $spaInfo['id'] . '| same value : ' . $isSameValue);
  	if ($spaInfo['id'] != '' && !$isSameValue) {
		$respAction=getAction($scenario,$cmd);
      	if ($respAction!='') {
          	$scenario->setLog('		-> call gecko api :' . $respAction['cmd'] . '|'. $respAction['index'] . '|' . $respAction['action']);
          	$pythonCmd='python3 /var/www/html/plugins/script/data/gecko_api_actions.py \'' . $spaInfo['name'] . '\' ' . $spaInfo['id'] . ' ' . $respAction['cmd'] . ' ' . $respAction['index'] . ' ' .$respAction['action'];
          	$scenario->setLog('Exec python command : ' . $pythonCmd);
          	exec($pythonCmd, $output, $retval);
			$scenario->setLog('			-> respone status : '. $retval);
          
          	foreach($output as $line){
              	$scenario->setLog($line);
                if ((strtolower('ResponseState') == strtolower($line)) or 
                          (strpos(strtolower('ResponseState'),strtolower($line)) !== false ) or 
                          (strpos(strtolower($line),strtolower('ResponseState')) !== false )) {
                    $jsonResponse=explode('|',$line)[1];

                    $aResponse=json_decode($jsonResponse, true);
                    if (sizeof($aResponse) > 0) {
                        $scenario->setLog(json_encode($aResponse));
                        $scenario->setData('ListSpaIdentifier',json_encode($aResponse));
                    }
                }
            }
        }
    }
}

$scenario->setLog('End script for lauching action on Gecko spa');

function isSameValue($scenario,$cmd) {
  	return ($cmd->execCmd() == $cmd->getLastValue());
}

function getSpaId($scenario, $cmd){
	$parentId=$cmd->getEqLogic_id();
  	$scenario->setLog(' - parent id : ' . $parentId);
  	$eqParent=eqLogic::byId($parentId);
  
  	foreach($eqParent->getCmd() as $cmdEq) {
      	if (stristr($cmdEq->getName(),'raw_data')) {
          	return array('name'=>$eqParent->getName(), 'id'=>str_replace(array('raw_data_'),array(''),$cmdEq->getName()));
          	break;
        }
    }
}

function getAction($scenario,$cmd) {
  	$cmdName=$cmd->getName();
  	$value=($cmd->execCmd() == '' ? '0' : $cmd->execCmd());
  	$scenario->setLog(__FUNCTION__ . ':' . $cmdName . '|'.$value);
  	$splitCmd=explode('_',$cmdName);
  	switch (true) {
        case stristr($cmdName,'pump'):
        	return array('cmd' => $splitCmd[0], 'index' => $splitCmd[1], 'action'=>$value);
        	break;
       	case stristr($cmdName,'light'):
        	return array('cmd' => $splitCmd[0], 'index' => $splitCmd[1], 'action'=>$value);
        	break;
      	case stristr($cmdName,'water_care'):
        	//get index of asked value
        	$listValue=$cmd->getConfiguration('listValue');
        	$i=0;
        	foreach($listValue as $paramValue) {
              	$scenario->setLog($paramValue . ' vs ' . $value);
              	//if ($paramValue == $value) {
              	if ($i == $value) {
					return array('cmd' => 'water_care', 'index' => '0', 'action'=>'\'' .$paramValue . '\'');
        			break;
                }
              	$i++;
            }
        	break;
      	case stristr($cmdName,'target_temperature'):
      	case stristr($cmdName,'water_heater_set_temperature'):
        	return array('cmd' => 'target_temperature', 'index' => 0, 'action'=>$value);
            break;
      	default:
        	return '';
        	break;
    }
      
}

Il doit avoir comme déclencheur les actions vous voulez réaliser
ici : lampe, pompe et température de l’eau

Vous allez avoir un virtuel avec cette tête là

Hésitez pas si vous avez des soucis ou questions
@chris94440
Ps : il faut compter environs 10/15 secondes entre la demande d’une action et la réponse de l’api … alors soyez patient

Bonjour,

Merci pour le travail qui a été fait. J’ai suivi les instructions données juste avant pour installer les scripts et mettre en route les scénarios dans jeedom, mais à l’exécution, le script d’initialisation n’arrive pas jusqu’à la création du virtuel.

Voici ce que j’ai dans les logs :

Traceback (most recent call last):
1648|File "/var/www/html/plugins/script/data/gecko_api_getState.py", line 150, in <module>
1649|spa['cmds']=state({locator.spas[i].identifier_as_string})
1650|File "/var/www/html/plugins/script/data/gecko_api_getState.py", line 90, in state
1651|print(f"aa {facade.reminders[i].type}")
1652|AttributeError: 'tuple' object has no attribute 'type'

Une idée de comment corriger cette erreur ?
Merci !

Hello
Laisse moi le temps de replonger dans ce sujet … je n’ai pas le spa … j’ai fait ça pour un ami …je reviens vers toi des que je peux
@chris94440
Update:
Essai de commenter dans un premier temps ce qui à l’air de poser bp à savoir les lignes

	for i in range(len(facade.reminders)):
		print(f"aa {facade.reminders[i].type}")

pour commenter utilise le #

	#for i in range(len(facade.reminders)):
		#print(f"aa {facade.reminders[i].type}")

tiens moi au courant
@chris94440

Merci de prendre ce temps pour moi.
Une fois commenté, voici ce que je trouve dans les logs :

Exception in thread Thread-7:
1673|Traceback (most recent call last):
1674|File "/usr/lib/python3.7/threading.py", line 917, in _bootstrap_inner
1675|self.run()
1676|File "/usr/lib/python3.7/threading.py", line 865, in run
1677|self._target(*self._args, **self._kwargs)
1678|File "/usr/local/lib/python3.7/dist-packages/geckolib/automation/facade.py", line 316, in _update_thread_func
1679|while self.spa.isopen:
1680|AttributeError: 'NoneType' object has no attribute 'isopen'

@chris94440