Remarque
Il s’agit de la documentation du plugin en version bêta. Les fonctionnalitées à venir sont listées dans la todo-liste au début du changelog et ne sont donc pas évoquées ici.
Le plugin pyenv4Jeedom (pyenv) permet d’externaliser les fonctions de pyenv. Ce plugin est une dépendance pour un autre plugin que vous avez installé. Si vous ne développez pas un plugin nécessitant pyenv, seul le premier chapitre vous sera utile, le reste de cette documentation ne vous sera d’aucune utilité.
Il n’y a pas d’équipement et de commande à gérer, la page du plugin permet de lister les virtualenv installés par les plugins qui en ont besoin. Il est possible de les supprimer en cas de problème, cette action reste cependant à réserver dans des cas bien particilers.
Il se peut qu’un message indique qu’une commande bloquante est en cours d’exécution. Ce n’est pas un message d’erreur
mais un message normal qui vous informe qu’une commande gourmande en ressource est en cours d’exécution. Cela peut être
l’installation et donc la compilation d’une nouvelle version de python nécessaire pour un plugin ou l’installation d’un
virtualenv. Une commande bloquante empêche l’exécution d’une autre commande bloquante. Il n’est possible d’exécuter
qu’une seule commande bloquante à la fois.
Si cette commande est lancée depuis au moins 5 minutes, que le verrou est toujours en place et que la commande n’est
pas exécutée, il est possible de réinitialiser le verrou manuellement afin de débloquer la situation.
La page santé du plugin reprend plus ou moins les mêmes informations.
Il est seulement possible de configurer pyenv4Jeedom pour inclure pyenv au backup ou pas. La configuration par défaut est sans inclusion afin de limiter la taille du backup et les ressources nécessaires pour créer le backup. Le choix pour cette option est laissée à l’utilisateur en fonction de la place et des ressources disponibles sur son système.
pyenv est un outil permettant d’utiliser la version de python que vous souhaitez et pyenv4Jeedom vous permet d’isoler l’exécution de votre script python dans un virtualenv dédié. Il est possible de créer plusieurs virtualenv avec des versions de python différentes ou avec des versions du même module différentes.
Il est possible d’installer une version plus récente que celle du système mais aussi plus ancienne afin de pouvoir faire fonctionner une ancienne version d’un module.
Finalement pyenv permet de garantir la compatibilité de votre plugin quelle que soit la version debian sur laquelle est installée Jeedom en fixant les versions à utiliser.
Depuis la version 4.2 de Jeedom, il est conseillé d’utiliser la méthode d’installation de votre plugin avec le fichier
packages.json. Dans ce fichier, il faut inclure pyenv dans les plugins à installer :
{
"plugin": {
"pyenv": {}
},
}
Pour installer la version bêta, comme il n’y a pas de moyen via le fichier packages.json, il est possible de le faire en php dans le fichier plugin_info/install.php. Dans ce fichier il est possible de créer une fonction install_pyenv qui, dans le plugin MyModbus est le suivant :
function install_pyenv() {
$myModbusId = 'mymodbus';
$myModbusUpdate = update::byLogicalId($myModbusId);
$myModbusVersion = $myModbusUpdate->getConfiguration('version');
$pluginId = 'pyenv';
$update = update::byLogicalId($pluginId);
if (!is_object($update)) {
$update = new update();
$update->setLogicalId($pluginId);
}
$update->setSource('market');
$update->setConfiguration('version', $myModbusVersion);
$update->save();
$update->doUpdate();
$plugin = plugin::byId($pluginId);
if (!is_object($plugin)) {
log::add($myModbusId, 'error', sprintf(__("** Installation ** : plugin non trouvé : %s", __FILE__), $pluginId));
die();
}
$plugin->setIsEnable(1);
$plugin->dependancy_install();
log::add($myModbusId, 'info', sprintf(__("** Installation ** : installation terminée : %s", __FILE__), $pluginId));
mymodbus::init_pyenv();
}
Cette fonction installe la même version (bêta ou stable) que celle du plugin MyModbus. Elle est appelée dans les fonctions mymodbus_install et mymodbus_update. A la fin, la fonction init_pyenv du plugin MyModbus est lancée.
La fonction init_pyenv évoquée dans le chapitre Installation du plugin est la suivante :
public static function init_pyenv() {
pyenv::init();
$requirements = array('requests', 'pyserial', 'pyudev', 'pymodbus==3.2.2');
try {
pyenv::createVirtualenv(__CLASS__, mymodbusConst::PYENV_PYTHON, implode("\n", $requirements), mymodbusConst::PYENV_SUFFIX);
} catch (Exception $e) {
// Déjà installé
}
try {
$virtualenvs = pyenv::getVirtualenvNames(__CLASS__, mymodbusConst::PYENV_PYTHON, mymodbusConst::PYENV_SUFFIX);
} catch (Exception $e) {
log::add('mymodbus', 'error', __('Impossible de lister les virtualenv du plugin pyenv4Jeedom', __FILE__));
return;
}
$ret = null;
foreach ($virtualenvs as $virtualenv) {
if ($virtualenv['suffix'] !== mymodbusConst::PYENV_SUFFIX || $virtualenv['python'] !== mymodbusConst::PYENV_PYTHON) {
try {
pyenv::deleteVirtualenv(__CLASS__, $virtualenv['suffix']);
} catch (Exception $e) {
log::add('mymodbus', 'error', sprintf(__("Impossible de supprimer le virtualenv avec le suffixe '%s' du plugin pyenv4Jeedom", __FILE__), $virtualenv['suffix']));
}
} else {
$ret = $virtualenv['fullname'];
}
}
return $ret;
}
Elle crée le virtualenv nécessaire à MyModbus et supprime les autres virtualenv installés par MyModbus.
Le lancement du démon se fait de la manière suivante :
$virtualenv = self::init_pyenv();
if (is_null($virtualenv)) {
log::add('mymodbus', 'error', __('L\'environnement pyenv n\'a pas pu être installé, vérifiez la page de pyenv4Jeedom', __FILE__));
return;
}
$eqPyenv = pyenv::byLogicalId('pyenv', 'pyenv');
if (!is_object($eqPyenv)) {
log::add('mymodbus', 'error', __('pyenv4Jeedom n\'a pas été initialisé correctement', __FILE__));
return;
}
if ($eqPyenv->getConfiguration(pyenv::LOCK, 'false') !== 'false') {
log::add('mymodbus', 'error', __('Une commande pyenv bloquante est en cours d\'exécution', __FILE__));
return;
}
// Création des valeurs d'argument avec escapeshellarg()
// ...
// ...
$script = realpath(__DIR__ . '/../../resources/mymodbusd/mymodbusd.py');
$args = '--socketport ' . $socketPort . ' --loglevel ' . $daemonLoglevel . ' --apikey ' . $daemonApikey . ' --callback ' . $daemonCallback . ' --json ' . $jsonEqConfig;
log::add('mymodbus', 'info', 'Lancement du démon mymodbus : ' . $script);
$result = pyenv::runPyenv($script, $args, $virtualenv, true);
Toutes les interactions avec pyenv4Jeedom se font via des méthodes statiques de la classe pyenv, une sous-classe de
eqLogic. Il faut systématiquement faire les appels dans un bloc try {...} catch (Exception $e) {...} afin que vous
puissiez savoir si tout s’est bien passé.
pyenv::createVirtualenv crée un virtualenv pour un plugin et installe les modules
pyenv::createVirtualenv($_pluginId, $_pythonVersion, $_requirements, $_suffix='none', $_upgrade=false);
pyenv::createVirtualenv() :
$pluginId: (string) l’id du plugin pour lequel le virtualenv est installé. Si le plugin n’est pas installé, une exception est levée. Si le nom du virtualenv existe déjà, une exception est levée.
$_pythonVersion: (string) la version de python pour laquelle le virtualenv doit être installé. Si cette version n’est pas installée, cette instruction l’installera. La version doit être disponible à l’installation sans quoi une exception est levée.
$_requirements: (string) si c’est le chemin d’un fichier, ce fichier doit être au format requirements.txt. Les
modules décrits seront installés. $_requirements peut également être le contenu d’un fichier requirements.txt.
$_suffix: (string) afin de différencier les virtualenv installés pour un même plugin, ceux-ci doivent avoir un
suffixe différent. Le nom réel pour le virtualenv est $_pluginId . '++' . $_suffix.
$_upgrade: (boolean) à mettre à true pour mettre la version de python d’un virtualenv existant à niveau.
Pas de valeur de retour. En cas d’erreur, une exception est levée.
try {
pyenv::createVirtualenv('mymodbus', '3.11.4', 'pymodbus==3.2.2', 'pymodbus3.2.2');
} catch (Exception $e) {
}
pyenv::deleteVirtualenv($_pluginId, $_suffix='none');
pyenv::deleteVirtualenv :
$_pluginId: (string) l’id du plugin pour lequel le virtualenv doit être supprimé. Si le plugin n’est pas installé, une exception est levée.
$_suffix: (string) le suffixe du virtualenv à supprimer. Si aucun virtualenv ne correspond, rien n’est fait et aucune exception n’est levée.
Pas de valeur de retour. En cas d’erreur, une exception est levée.
try {
pyenv::deleteVirtualenv('mymodbus');
} catch (Exception $e) {
}
pyenv::getVirtualenvNames($_pluginId='', $_pythonVersion='', $_suffix='');
pyenv::getVirtualenvNames recherche les virtualenv selon des critères de recherche.
Recommandé pour récupérer le nom du virtualenv à utiliser pour les commandes pyenv::runPyenv ou
pyenv::sourceScript.
$_pluginId: l’id du plugin pour lequel le virtualenv doit être recherché. Si le plugin n’est pas installé, une exception est levée. Si ce paramètre n’est pas précisé, les virtualenv de tous les plugins seront listés.
$_pythonVersion: (string) la version de python pour laquelle le virtualenv doit être recherché. Si ce paramètre n’est pas précisé, les virtualenv de toutes les versions de python seront listées.
$_suffix: (string) le suffixe du virtualenv à rechercher. Si ce paramètre n’est pas précisé, tous les virtualenv seront listées.
Retourne une liste (array) avec le nom du virtualenv et la version python des virtualenv correspondants aux critères de recherche.
try {
pyenv::getVirtualenvNames('mymodbus', '3.11.4');
} catch (Exception $e) {
}
Retourne :
array (
0 => array (
'fullname' => 'mymodbus++pymodbus3.2.2',
'pluginId' => 'mymodbus',
'suffix' => 'pymodbus3.2.2',
'python' => '3.11.4'
),
1 => array (
'fullname' => 'mymodbus++pymodbus3.5.2',
'pluginId' => 'mymodbus',
'suffix' => 'pymodbus3.5.2',
'python' => '3.11.4'
),
2 => array (
'fullname' => 'mymodbus++pymodbus3.6.4',
'pluginId' => 'mymodbus',
'suffix' => 'pymodbus3.6.4',
'python' => '3.11.4'
)
)
pyenv::runPyenv($_command, $_args='', $_virtualenv=null, $_daemon=false, $_lock=false);
pyenv::runPyenv lance une commande dans l’environnement pyenv. Il est possible de préciser un virtualenv, de préciser que la commande doit être lancée comme un démon, auquel cas un virtualenv doit être spécifié.
$_command: (string) commande à exécuter. S’il s’agit d’un fichier, une commande cd sera exécutée vers le
répertoire du fichier avant d’exécuter la commande.
$_args: (string) les arguments de la commande. A préciser surtout si la commande est un fichier script.
$_virtualenv: (string) le nom du virtualenv dans lequel exécuter la commande. Si non précisé, le résultat est
l’équivalent de exec($_commande . ' ' . $_args).
$_daemon: (boolean) mode démon. Avec un virtualenv, la sortie du script est redirigée vers le log du plugin.
$lock: (boolean) commande bloquante. Si $_lock vaut true, aucune autre commande avec $_lock à true ne
pourra être exécutée via pyenv::runPyenv
Retourne le résultat de la commande dans un array, à raison d’un élément par ligne de retour.
Ne retourne rien en mode démon.
$args = '-a -b "valeur"';
$virtualenvs = pyenv::getVirtualenvNames('mymodbus', '3.11.4', 'pymodbus3.2.2');
try {
pyenv::runPyenv(realpath(__DIR__ . '/../../resources/super_script.py'), $args, $virtualenvs[0]['fullname']);
} catch (Exception $e) {
}
pyenv::sourceScript($_command, $_args='', $_virtualenv=null, $_daemon=false);
pyenv::sourceScript génère le contenu d’un script shell pour exécuter la commande dans l’environnement pyenv.
Identique à pyenv::runPyenv.
Retourne le contenu d’un script shell (sans la ligne shebang) pour exécuter la commande passée en paramètre.
$args = '-a -b "valeur"';
$virtualenvs = pyenv::getVirtualenvNames('mymodbus', '3.11.4', 'pymodbus3.2.2');
try {
$script = pyenv::sourceScript(realpath(__DIR__ . '/../../resources/super_script.py'), $args, $virtualenvs[0]['fullname']);
} catch (Exception $e) {
}
La page du plugin et la page santé du plugin donnent des informations utiles sur l’état du plugin.