Ce guide a pour objectif de vous accompagner dans l'utilisation du système de régulation JmpSolar. Il s'agit d'un logiciel utilisé comme régulateur de puissance délivrée par une batterie Marstek en collaboration avec un wattmètre Shelly Pro EM. Il s'appuie sur les API ouvertes de ces deux constructeurs afin de s'assurer d'une certaine pérennité.
Il s'agit d'un programme console développé sur la base de l'infrastructure Qt 6.12 ou version ultérieure. Le système d'exploitation cible est Linux ou une appliance compatible tel qu'un NAS. Il est disponible en version binaire pour les architectures x86_64 et ARM64. Il a été testé avec succès sur une machine physique sous Debian 13 et une machine virtuelle Debian 13 exécutée par une Freebox.
Les versions proposées sont au format AppImage. Ce sont des application auto-suffisantes. Le format AppImage est un format de conteneur qui contient l'application ainsi que toutes ses dépendances. L'installation de Qt 6.12 ou version ultérieure est donc inutile.
Pour utiliser jmpsolar, on préférera une machine qui reste allumée en permanence. Il est recommandé de placer le programme dans un environnement qui permet sa relance en cas de panne secteur prolongée. A cet effet, nous donnerons un exemple d'utilisation en tant que service systemd.
La forme générale de la ligne de commande pour un lancement manuel est :
jmpsolar.AppImage [options] <IP:port UDP (Venus)> <http://IP (Shelly)> [nom du fichier de configuration]
L'ordre des arguments est important. Le tableau qui suit les présente :
| Indice | Point de connexion UDP | Exemple | Rôle | |
| 1 | Obligatoire | <Adresse ou alias IP>:<N° de port UDP> | 192.168.0.10:30000 | Fourni au régulateur les paramètres de connexion au port UDP de la Venus. Notez le double point entre les deux paramètres : il est obligatoire. 30000 est le numéro de port par défaut de la Venus. |
| 2 | Obligatoire | <http|https>//<adresse ou alias IP du wattmètre Shelly> | http://192.168.0.11 | Fourni au régulateur le préfixe d'URL qui lui permet de communiquer avec le serveur web embarqué par le wattmètre. |
| 3 | Facultatif | [nom du fichier JSON de configuration] |
/etc/jmpsolar.json
|
Le fichier de configuration permet de définir une ou plusieurs des options possibles.
Cela évite de les saisir sur la ligne de commande mais pas seulement.
|
Contrairement aux arguments, l'ordre des options sur la ligne de commande est sans importance.
En revanche, si un fichier de configuration est défini et qu'il est rechargé via le signal SIGHUP, l'ensemble des options qu'il contient est pris en compte quelle que soit les options définies sur la ligne de commande.
Le tableau qui suit précise l'ensemble des options disponibles et leur valeur par défaut.
| Abréviation | Défaut | Rôle | ||
| -h | --help | Aide | Affichage de l'aide au lancement du régulateur. | |
| -v | --version | Aide | Affichage de la version du régulateur. | |
| -s <0|1> | --sim <0|1> | Simulation | 1 | Mode simulation si "1", mode réel si "O". En mode simulation, la commande de délivrance de puissnace n'est pas transmise à la Venus mais simplement affichée. Notez que le mode par défaut est "simulation". |
| -i <entier> | --id <entier> | Identifiant | 0 | Identifiant numérique de la Venus lorsqu'il y en a plusieurs. S'il n'y a qu'une seule Venus sur le réseau cette valeur est toujours 0 (défaut). |
| -d <entier> | --dod <entier> | Deep Of Discharge | 20 | Il s'agit du pourcentage de charge de la batterie en dessous duquel, le régulateur interrompt ses demande de livraison d'énergie. Il devrait toujours être supérieur à celui définit par la Venus elle-même sinon l'énergie ne sera pas fournie mais vous n'en serez pas informé. |
| -w <entier> | --pwm <entier> | Power Max. | 800 |
Limite de la puissance délivrée. Jusqu'à la version 1.1.0, lorsque la puissance demandée est supérieure à cette valeur, elle y est ramenée.
A partir de la version 1.1.0, il est possible soit de fixer la puissance maximum sous
forme d'une constante strictement positive avec un comportement identique aux versions
précédentes. En période estivale, la batterie se recharge très vite et peut donc absorber les pointes de consommation. Ce n'est plus le cas en période hivernale. Si on souhaite conserver un minimum de capacité pour faire face à une panne prolongée du secteur, il est indispensable d'éviter que les pointes de consommation ne vide trop rapidement la batterie. |
| -r <entier> | --wtr <entier> | Waiting To Recharge | 30 | Délai en minute d'interruption de la régulation lorsque le seuil de décharge de la batterie (DOD) est atteint. Ce devrait être le délai minimum pour que la batterie se recharge d'au moins un pourcent. De toute façon, si la charge n'a pas permit d'atteindre ce pourcent (absence de charge la nuit par exemple), l'attente de "wtr" minutes sera renouvelée et ainsi de suite jusqu'à ce que la batterie se soit un peu rechargée. |
| -g <entier> | --rp <entier> | Regulation Period | 1000 | Délai minimum en millisecondes entre deux commandes de régulation. Notez qu'en raison de l'emploi d'UDP qui supporte mal les collisions sur le réseau, le délai réel est souvent de l'ordre de 2 à 5 secondes (dépend de la qualité du réseau). |
| -p <entier> | --pmt <entier> | Passive Mode Timeout | 10 | Délai en secondes du maintien de chaque puissance commandée en mode passif. |
| -t <chaîne de caractères> | --tme <chaîne de caractères> | Times | aucun |
Définition des créneaux au sein desquels la délivrance de puissance est autorisée.
Si un ou plusieurs créneaux sont définis, aucune puissance ne sera délivrée en dehors de ces créneaux.
Cela est pratique pour favoriser des moments privilégiés de consommation comme les heures pleines. Un créneau est défini par une heure de début et une heure de fin. Ces deux horaires sont séparés par un tiret. L'horaire eest défini par deux caractères par l'heure et deux caractères pour les minutes avec le double-point comme séparateur. Les créneaux horaires sont séparés par des virgules. Le chevauchement des créneaux est interdit. Ainsi 00:00-03:00,06:00-22:00 indique que la puissance est délivrée de 6h à 22h puis de
minuit à 3h du matin.L'ordonnancement des créneaux horaires est sans importance. |
Le fichier de configuration est un fichier JSON dont les propriéts ont des noms identiques au format long des options :
{
"sim": false,
"tme": "00:00-03:00,06:00-23:59",
"id": 0,
"rp": 1000,
"dod": 20,
"wtr": 30,
"pmt": 15,
"pwm": 800
}
"sim".
Sur la ligne de commande la valeur est soit 0, soit 1.
Dans le fichier de configuration la valeur est un booléen qui est soit true,
soit false.
Les propriétés dans ce fichier sont toutes optionnelles. Au lancement, si elles n'apparaissent pas, c'est la valeur par défaut qui est prise en compte. Au rechargement, seules les options qui apparaissent dans le fichier sont prises en compte.
Il est important que l'on puisse suivre à la demande le fonctionnement du régulateur. Pour cela, il émet les messages d'avertissement et d'erreur sur le flux standard des erreurs et les messages d'information sur le flux de sortie standard.
Comme le régulateur a pour vocation de travailler en tâche de fond, il est recommandé de rediriger ces flux dans un ou deux fichiers (un pour les avertissements/erreurs et un autre pour les messages d'information). Voici un exemple de redirection :
./jmpsolar 192.168.0.10:30000 http://192.168.0.11 /etc/jmpsolar.json 1>/var/log/jmps_out.log 2>/var/log/jmps_err.log
Si ces informations n'ont pas d'intérêt pour vous, il est possible de les rediriger ver /dev/null.
Voici un exemple des traces émises :
jmpsolar v1.0.4 (202610280838) : régulation de la production solaire
(c) Jean-Marie Piatte
Paramètres de la connexion UDP côté Venus .........: [192.168.0.10:30000]
URL de la connexion HTTP au wattmètre Shelly ......: http://192.168.0.11/rpc
Créneaux horairex de la régulation ................: 06:00-23:59
Simulation de la commande de régulation vers Venus : Non
Identifiant de la Venus sur le réseau .............: 0
Profondeur maximale de décharge de la batterie ....: 22 %
Puissance maximale délivrable par la Venus ........: 800 W
Délai d'attente pour une recharge minimale ........: 30 minutes
Période entre deux procédures de régulation .......: 100 millisecondes
Délai d'activation du mode passif .................: 15 secondes
Nom du fichier de configuration ...................: /etc/jmpsolar.json
[28/10/2026 08:42:38] Puissance intantanée retournée par le wattmètre : 229.806 W
[28/10/2026 08:42:38] Le wattmètre Shelly et la batterie Venus sont tous deux en ligne
[28/10/2026 08:42:38] puissance wattmètre = 229.806 W, puissance demandée = 229 W, durée = 15 s, décharge = 55 %
[28/10/2026 08:42:41] puissance wattmètre = 252.418 W, puissance demandée = 252 W, durée = 15 s, décharge = 55 %
[28/10/2026 08:42:44] puissance wattmètre = 241.387 W, puissance demandée = 241 W, durée = 15 s, décharge = 55 %
[28/10/2026 08:42:47] puissance wattmètre = 228.983 W, puissance demandée = 228 W, durée = 15 s, décharge = 55 %
[28/10/2026 08:42:50] puissance wattmètre = 243.156 W, puissance demandée = 243 W, durée = 15 s, décharge = 55 %
[28/10/2026 08:42:54] puissance wattmètre = 245.309 W, puissance demandée = 245 W, durée = 15 s, décharge = 55 %
[28/10/2026 08:42:57] puissance wattmètre = 231.443 W, puissance demandée = 231 W, durée = 15 s, décharge = 55 %
...
Lorsque vous rechargez le fichier de configuration à chaud via le signal SIGHUP ce rechargement
apparait dans les traces sous la forme du réaffichage des arguments et des options prises en compte.
En mode simulation, seule la puissance lue auprès du wattmètre est affichée.
Voici un exemple des traces du fichier des erreurs :
[28/10/2026 09:09:46] [28/10/2026 09:09:54] Echec de la méthode UdpEngine::setPassiveMode : QString UdpEngine::sendAndListen(const QHostAddress&, quint16, const QString&) -> Pas de réponse UDP au bout de 1500 ms
[28/10/2026 09:11:12] [28/10/2026 09:11:21] Echec de la méthode UdpEngine::setPassiveMode : QString UdpEngine::sendAndListen(const QHostAddress&, quint16, const QString&) -> Pas de réponse UDP au bout de 1500 ms
[28/10/2026 10:21:16] [28/10/2026 10:21:24] Echec de la méthode UdpEngine::setPassiveMode : QString UdpEngine::sendAndListen(const QHostAddress&, quint16, const QString&) -> Pas de réponse UDP au bout de 1500 ms
[28/10/2026 10:31:00] [28/10/2026 10:31:10] Echec de la méthode UdpEngine::setPassiveMode : QString UdpEngine::sendAndListen(const QHostAddress&, quint16, const QString&) -> Pas de réponse UDP au bout de 1500 ms
[28/10/2026 10:31:10] [28/10/2026 10:31:54] Echec de la méthode UdpEngine::setPassiveMode : QString UdpEngine::sendAndListen(const QHostAddress&, quint16, const QString&) -> Pas de réponse UDP au bout de 1500 ms
[28/10/2026 10:39:40] [28/10/2026 10:39:49] Echec de la méthode UdpEngine::setPassiveMode : QString UdpEngine::sendAndListen(const QHostAddress&, quint16, const QString&) -> Pas de réponse UDP au bout de 1500 ms
[28/10/2026 10:44:13] [28/10/2026 10:44:21] Echec de la méthode UdpEngine::setPassiveMode : QString UdpEngine::sendAndListen(const QHostAddress&, quint16, const QString&) -> Pas de réponse UDP au bout de 1500 ms
[28/10/2026 11:11:07] [28/10/2026 11:11:15] Echec de la méthode UdpEngine::setPassiveMode : QString UdpEngine::sendAndListen(const QHostAddress&, quint16, const QString&) -> Pas de réponse UDP au bout de 1500 ms
Comme le régulateur est un programme console traditionnel, il existe de multiples façons de le lancer.
Pour abstraire l'architecture, on crée un lien symbolique nommé jmpsolar sur le véritable
exécutable qui est soit jmpsolar-x86_64.AppImage, soit jmpsolar-aarch64.AppImage.
Ainsi, dans les exemples qui suivent, jmpsolar référence le véritable exécutable.
A partir d'un terminal, on peut directement le lancer en lui fixant ses options et surtout ses arguments. L'affichage des messages de suivi s'effectue sur le terminal.
L'inconvénient est que dès que l'on ferme le terminal, on ferme aussi ses processus enfants dont le régulateur. Cette solution n'est donc pas viable pour une régulation opérationnelle sur un temps long.
En revanche, elle peut être très utile pour le mode "simulation" qui permet de vérifier la présence de la Venus et du wattmètre sur le réseau tout en affichant périodiquement la puissance instantanée mesurée par le wattmètre.
Il est malgré tout possible de lancer le régulateur sans qu'il soit le processus enfant du terminal.
Il suffit de le lancer via l'utilitaire setsid :
setsid ./jmpsolar 192.168.0.10:30000 http://192.168.0.11 /etc/jmpsolar.json 1>/var/log/out.log 2>/var/log/err.log &
Même lancé en arrière-plan via la commande setsid, le régulateur n'est pas à l'abri
d'une panne prolongée.
Quel que soit le mode de lancement via un terminal, l'arrêt plus ou moins programmé de la machine ne
relancera pas le régulateur dès que la machine sera de nouveau opérationnelle.
Un moyen facile de palier à cet inconvénienbt est de lancer le programme via le planificateur
cron via l'instruction @reboot :
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * user-name command to be executed
@reboot root sleep 60 && setsid /usr/bin/jmpsolar 192.168.0.10:30000 http://192.168.0.11 /etc/jmpsolar.json 1>/var/log/out.log 2>/var/log/err.log
17 * * * * root cd / && run-parts --report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || { cd / && run-parts --report /etc/cron.daily; }
47 6 * * 7 root test -x /usr/sbin/anacron || { cd / && run-parts --report /etc/cron.weekly; }
52 6 1 * * root test -x /usr/sbin/anacron || { cd / && run-parts --report /etc/cron.monthly; }
#
De cette manière, chaque fois que la machine se relance, le régulateur est lancé également.
Notez que le régulateur se lance 1 minute après le boot.
Ce démarrage différé garantit que le réseau est opérationnel lorsque le régulateur se lance.
Cela suppose toutefois des droits d'administration associé au compte qui accéde au fichier
/etc/crontab.
C'est clairement la solution que nous recommandons pour un usage opérationnel du régulateur. Il suffit d’écrire un fichier d’unité (.service) décrivant comment lancer votre exécutable, puis de l’activer. Voici la démarche complète (utilisation des droits root).
Placer l’exécutable dans un emplacement système.
Nous recommandons /usr/local/bin ou /opt/jmpsolar.
Pour cela placez-vous sur le répertoire qui contient votre exécutable puis tapez la commande
install -m 755 ./jmpsolar-aarch64.AppImage /usr/local/bin/jmpsolar-aarch64.AppImage
Cette commande recopie l'exécutable sur /usr/local/bin et luis affecte les droits
"rwxr-xr-x" (755).
Nous vous conseillons de créer un lien symbolique pour simplifier le nom de l'exécutable :
ln -s /usr/local/bin/jmpsolar-aarch64.AppImage /usr/local/bin/jmpsolar
ATTENTION : lorsqu'un service démarre, il vérifie les droits sur chaque répertoire traversé pour accéder
au fichier à exécuter.
La commande namei -l /usr/local/bin/jmpsolar permet de suivre les droits jusqu'à l'exécutable.
Certains Linux dont Debian interdisent l'accès du répertoire d'un utilisateur (sous /home)
aux autres comptes.
C'est donc une très mauvaise idée au plan de la sécurité de placer l'exécutable lancé par le service avec
le compte jmpsolar sur un répertoire utilisateur car cela vous oblige à ouvrir les droits
au compte jmpsolar sur le compte de cet utilisateur.
Créez /etc/systemd/system/jmpsolar.service.
Ce fichier comporte les informations suivantes :
[Unit]
Description=jmpsolar
# Attendre que le réseau soit réellement configuré (utile car l'application ouvre des sockets)
Wants=network-online.target
After=network-online.target
[Service]
Type=exec
ExecStart=/usr/local/bin/jmpsolar 192.168.0.10:30000 http://192.168.0.11 /etc/jmpsolar.json
StandardOutput=append:/var/log/jmpsolar/jmpsolar.log
StandardError=append:/var/log/jmpsolar/jmpsolar.err
WorkingDirectory=/var/lib/jmpsolar
# Compte non privilégié (à créer, voir plus bas)
User=jmpsolar
Group=jmpsolar
# Redémarrage automatique en cas de plantage
Restart=on-failure
RestartSec=5
# Variables d'environnement éventuelles
Environment=
[Install]
WantedBy=multi-user.target
Créer l’utilisateur dédié (recommandé).
Cela se fait via les commandes :
sudo useradd --system --no-create-home --shell /usr/sbin/nologin jmpsolar
sudo mkdir -p /var/lib/jmpsolar
sudo chown jmpsolar:jmpsolar /var/lib/jmpsolar
Assurez vous que le nouveau compte jmpsolar à accès en lecture/écriture au répertoire
/var/log/jmpsolar.
Une alternative plus simple consiste à ajouter DynamicUser=yes et
StateDirectory=jmpsolar dans [Service].
systemd crée alors l’utilisateur à la volée, ainsi que /var/lib/jmpsolar.
Activer et démarrer le service.
sudo systemctl daemon-reload # relire les unités
sudo systemctl enable --now jmpsolar # activer au boot + démarrer tout de suite
Surveiller l'activité du régulateur.
Si vous ne définsissez pas les propriétés StandardOutput et StandardError
les messages de sortie sont dirigés dans le journal du système.
Les commandes qui suivent permettent de les consulter.
systemctl status jmpsolar
journalctl -u jmpsolar -f # logs en direct
journalctl -u jmpsolar -b # logs depuis le dernier boot
Si vous avez définis la redirection de la sortie standard et du flux d'erreur vers des fichiers via
les propriétés StandardOutput et StandardError,
les messages sont consultables via n'importe quel éditeur de texte ou par des commandes du shell comme par
exemple :
tail -30 /var/log/jmpsolar/jmpsolar.log
tail -30 /var/log/jmpsolar/jmpsolar.err
Ces commandes affichent sur la console les 30 dernières lignes de chacun des fichiers.
Notez que dans le fichier de service proposé nous avons opté pour la définition des propriétés
StandardOutput et StandardError.
En effet, le régulateur est assez verbeux et "encombrer" le journal système de ces messages n'est pas très
judicieux.
Pensez après toute modification du fichier (.service) à taper la commande daemon-reload puis
systemctl restart jmpsolar.
Il est possible de fixer les arguments de l'exécutable dans un fichier externe de façon à ne pas modifier le fichier du service mais cela sort du cadre de cette documentation.