Messages Client → Serveur
Format des messages
Chaque message est un objet JSON avec une seule clé de premier niveau identifiant la commande. La valeur est un objet dont la structure dépend de la commande.
Toutes les clés de commandes client sont en MAJUSCULES (ex. DU, SV, SA, GC).
Index des commandes
| Clé | Description |
|---|---|
DU | Demander le dump complet |
XX | Heartbeat / keepalive |
LO | Connexion |
IL | Changer la langue |
SV | Envoyer la valeur d’un seul élément |
SA | Envoyer toutes les valeurs d’éléments |
DP | Demander le dump d’une propriété |
I1 | Clic sur preicon1 de propriété |
I2 | Clic sur preicon2 de propriété |
I3 | Clic sur posticon1 de propriété |
I4 | Clic sur posticon2 de propriété |
J1 | Clic sur preicon d’élément |
J2 | Clic sur posticon d’élément |
PL | Charger un profil |
PS | Sauvegarder un profil |
GC | Créer une ligne de grille |
GU | Mettre à jour une ligne de grille |
GD | Supprimer une ligne de grille |
GF | Sélectionner une ligne de grille |
GH | Monter une ligne de grille |
GB | Descendre une ligne de grille |
GR | Réinitialiser (vider) la grille |
CL | Charger une configuration de modules |
CS | Sauvegarder la configuration courante |
FS | Choisir le dossier de fichiers surveillé |
ML | Charger un module |
MK | Décharger un module |
QY | Interroger un module (mécanisme inter-module, pas destiné au client) |
YA | Démarrer le serveur INDI embarqué |
YZ | Arrêter le serveur INDI embarqué |
YL | Charger un driver INDI |
YR | Recharger un driver INDI |
YS | Arrêter un driver INDI |
Tous les messages ciblant un module ou une propriété utilisent l’enveloppe suivante :
{
"COMMANDE": {
"m": {
"NomDuModule": {
"p": {
"nomDeLaPropriété": { ... }
}
}
}
}
}où m désigne modules et p désigne properties.
Gestion de session
DU — Demander le dump complet
Doit être envoyé immédiatement après la connexion. Le serveur répond avec un message d contenant tous les modules.
{ "DU": { "language": "fr" } }| Champ | Description |
|---|---|
language | Langue préférée pour les libellés traduits ("en", "fr", etc.) |
XX — Heartbeat
À envoyer toutes les 30 secondes pour maintenir la connexion active. Le serveur répond avec xx.
{ "XX": {} }LO — Connexion
Requis lorsque le serveur a le contrôle d’accès activé (grant-client vaut "-1" dans la réponse dump).
{
"LO": {
"user": "username",
"pw": "password",
"language": "fr"
}
}IL — Changer la langue
Modifie la langue des libellés traduits sans se reconnecter.
{ "IL": { "language": "fr" } }Mise à jour des valeurs de propriété
SV — Envoyer la valeur d’un seul élément
À utiliser lorsque directedit est true sur l’élément — envoie immédiatement la valeur d’un seul élément. Sur un élément à directedit: false, le backend le rejette (erreur dans le log du module, rien n’est appliqué) : utiliser SA à la place.
{
"SV": {
"m": {
"Focus": {
"p": {
"parameters": {
"e": {
"iterations": 7
}
}
}
}
}
}
}e contient une seule paire nomÉlément: valeur.
SA — Envoyer toutes les valeurs d’éléments
Utilisé pour soumettre tous les éléments éditables d’une propriété en une fois (ex. depuis un formulaire). Également utilisé lorsque directedit est false.
{
"SA": {
"m": {
"Focus": {
"p": {
"parameters": {
"e": {
"iterations": 7,
"exposure": 3.5,
"active": true
}
}
}
}
}
}
}Un SA doit contenir tous les éléments éditables de la propriété. Si un seul manque, le backend n’applique rien du tout — pas même les éléments présents — et journalise un avertissement côté serveur (visible côté client via un message l). Ce n’est pas un cas à gérer avec une dégradation gracieuse : c’est le signe d’un bug dans le client, qui doit toujours envoyer l’ensemble complet des éléments.
DP — Demander le dump d’une propriété
Demande au serveur de renvoyer l’état complet d’une propriété, comme si elle venait d’être (re)chargée — utile pour forcer le rafraîchissement d’une propriété qu’un client soupçonne d’être désynchronisée, sans recharger tout le module. Le serveur répond par un message ap pour cette seule propriété.
{
"DP": {
"m": {
"Focus": {
"p": {
"parameters": {}
}
}
}
}
}Pas de champ e : DP cible la propriété entière, pas un élément particulier.
Actions sur les icônes
preicon1/preicon2/posticon1/posticon2 (propriétés) et preicon/posticon (éléments, voir Modèle de données) ne sont pas des décorations : chacune est une action cliquable. Une valeur non vide est le nom de l’icône Google Fonts (Material Icons) à afficher ; cliquer sur l’icône affichée envoie la commande correspondante ci-dessous sans aucune valeur ni paramètre — un payload vide qui cible simplement la propriété (ou l’élément). Ce que fait réellement l’action côté serveur (sauvegarder un profil, charger un profil, lancer un processus…) dépend entièrement du module ; le seul rôle du client est d’afficher l’icône et de transmettre le clic tel quel.
Une chaîne vide signifie qu’il n’y a aucune icône dans cet emplacement — rien à afficher, et donc rien à cliquer ni à envoyer. Ne pas afficher de substitut ni brancher de gestionnaire de clic pour un champ icône vide.
I1 — Clic sur preicon1 de propriété
{
"I1": {
"m": {
"Focus": {
"p": {
"saveprofile": {}
}
}
}
}
}I2 — Clic sur preicon2 de propriété
{
"I2": {
"m": {
"Focus": {
"p": {
"loadprofile": {}
}
}
}
}
}I3 — Clic sur posticon1 de propriété
Même structure que I1 / I2.
I4 — Clic sur posticon2 de propriété
Même structure que I1 / I2.
J1 — Clic sur preicon d’élément
{
"J1": {
"m": {
"Focus": {
"p": {
"devices": {
"e": {
"myDevice": {}
}
}
}
}
}
}
}J2 — Clic sur posticon d’élément
Même structure que J1.
Gestion des profils
PL — Charger un profil
{
"PL": {
"m": {
"Focus": {
"profile": "myProfile"
}
}
}
}Le serveur répond avec un message fl (profil chargé) puis envoie les valeurs de propriétés mises à jour.
fc transitoire précède flLe serveur envoie un message fc (modifications non sauvegardées) juste avant fl, comme étape intermédiaire du chargement du profil, avant que les valeurs des propriétés ne changent réellement. Un client qui réagit à fc en affichant un indicateur « modifications non sauvegardées » le verra clignoter le temps d’un message et se corriger de lui-même dès que fl arrive juste après — c’est attendu, pas un bug.
PS — Sauvegarder un profil
{
"PS": {
"m": {
"Focus": {
"profile": "myProfile"
}
}
}
}Le serveur répond avec un message fs (profil sauvegardé).
Opérations sur les grilles
Les commandes de grille ciblent une propriété spécifique au sein d’un module. Toutes utilisent l’enveloppe m / p.
GC — Créer une ligne de grille
Ajoute une nouvelle ligne à la grille avec les valeurs d’éléments fournies.
{
"GC": {
"m": {
"Sequencer": {
"p": {
"sequence": {
"e": {
"target": "M31",
"exposure": 120,
"count": 10
}
}
}
}
}
}
}Le serveur répond avec un message gc confirmant l’index de la nouvelle ligne.
ea tel qu’il arrive — rien à se demanderCréer une ligne de grille applique d’abord les valeurs soumises aux éléments de base de la propriété, puis ajoute la ligne et émet gc. Ça peut prendre plusieurs messages ea (un par élément de base modifié, envoyés l’un après l’autre — soumettre target/exposure/count comme ci-dessus produit trois messages ea distincts avant le gc final), mais un client n’a jamais besoin de connaître ou de prévoir ce nombre : le serveur n’envoie jamais que le strict nécessaire pour garder le datastore du client exact, ni plus ni moins (voir Principes d’architecture client). Applique chaque message tel qu’il se présente, comme d’habitude, et le résultat est correct quel que soit le nombre de messages reçus.
Le seul effet visible : une propriété qui affiche à la fois ses éléments de base et sa grille (showElts et showGrid tous les deux actifs — combinaison rare) clignotera à travers ces états intermédiaires avant que la nouvelle ligne apparaisse. C’est le vrai état du datastore, pas un bug, et ça se résout tout seul — pas besoin de le filtrer ni de faire du polling pour le contourner.
GU — Mettre à jour une ligne de grille
Met à jour une ligne spécifique (index base zéro i) avec de nouvelles valeurs d’éléments.
{
"GU": {
"m": {
"Sequencer": {
"p": {
"sequence": {
"i": 1,
"e": {
"target": "M42",
"exposure": 60
}
}
}
}
}
}
}GD — Supprimer une ligne de grille
Supprime la ligne à l’index i (base zéro).
{
"GD": {
"m": {
"Sequencer": {
"p": {
"sequence": {
"i": 0
}
}
}
}
}
}GF — Sélectionner une ligne de grille
Demande au serveur de charger une ligne de la grille dans les éléments éditables de la propriété (utilisé lors du clic sur une ligne pour l’éditer).
{
"GF": {
"m": {
"Sequencer": {
"p": {
"sequence": {
"i": 2
}
}
}
}
}
}GH — Monter une ligne de grille
Déplace la ligne à l’index i d’une position vers le haut.
{
"GH": {
"m": {
"Sequencer": {
"p": {
"sequence": {
"i": 3
}
}
}
}
}
}GB — Descendre une ligne de grille
Déplace la ligne à l’index i d’une position vers le bas.
{
"GB": {
"m": {
"Sequencer": {
"p": {
"sequence": {
"i": 2
}
}
}
}
}
}GR — Réinitialiser la grille
Vide entièrement la grille d’une propriété (toutes les lignes supprimées) — contrairement à GD qui ne supprime qu’une ligne. Le serveur diffuse gr en réponse.
{
"GR": {
"m": {
"Sequencer": {
"p": {
"sequence": {}
}
}
}
}
}Pas de champ i : GR cible la grille entière, pas une ligne particulière.
Cycle de vie des modules
CL — Charger une configuration
Charge tous les modules d’une configuration sauvegardée (liste dans controllerdata.availableconfs, voir Contrôleur).
{ "CL": { "name": "modules" } }CS — Sauvegarder la configuration courante
Enregistre l’ensemble des modules actuellement chargés sous le nom donné (GlobalDatastore n’est jamais inclus).
{ "CS": { "name": "modules" } }ML — Charger un module
Demande au serveur de charger dynamiquement un module. Ces commandes visent le contrôleur : le payload n’utilise pas l’enveloppe m/p.
{ "ML": { "lib": "focus", "label": "MyFocuser", "profile": "default" } }| Champ | Description |
|---|---|
lib | Bibliothèque du module, sans le préfixe libost (le serveur l’ajoute) : focus pour libostfocus. Les bibliothèques disponibles sont les clés de controllerdata.libraries |
label | Libellé de cette instance ; le nom du module est ce libellé sans les espaces. Si un module de ce nom est déjà chargé, la demande est refusée |
profile | Nom de profil à charger (optionnel) |
Les clés de controllerdata.libraries sont déjà préfixées (libostfocus, libostdummy…). Le serveur ajoute libost une nouvelle fois, sans condition : transmettre la clé telle quelle double le préfixe et échoue :
{ "ML": { "lib": "libostdummy", "label": "MyDummy" } }Error loading library MyDummy - Cannot load library libostlibostdummy: cannot open shared object file: No such file or directoryRetirer d’abord le préfixe libost de la clé (même transformation que celle sur laquelle s’appuie la convention de clé de profiles, voir Données du contrôleur) :
{ "ML": { "lib": "dummy", "label": "MyDummy" } }MK — Décharger un module
Demande au serveur de décharger un module en cours d’exécution, désigné par son nom.
{ "MK": { "name": "MyFocuser" } }GlobalDatastore ne peut pas être déchargé : la demande est refusée (message d’erreur dans les logs).
Interroger un module
QY — Interroger un module
QY/qa existe avant tout pour qu’un module puisse poser une question à un autre module en interne (un module qui appelle Basemodule::otherModuleQuery(), qui passe par ce même mécanisme). Un client peut techniquement envoyer QY aussi — l’enveloppe est identique — mais il n’y a aujourd’hui aucun cas d’usage établi côté client. Documenté ici pour l’exhaustivité, et parce que ça comptera quand on documentera comment construire un module — pas comme quelque chose sur lequel un client devrait construire une fonctionnalité.
Mécanisme générique de requête/réponse propre à chaque module : poser à un module une question nommée avec des paramètres, recevoir une réponse nommée en retour via qa. Les noms de requête qu’un module comprend réellement (s’il en comprend) dépendent entièrement de ce module — le protocole ne définit que l’enveloppe. À noter : pas d’enveloppe p ici, QY cible le module lui-même, pas une de ses propriétés.
{
"QY": {
"m": {
"Sequencer": {
"query": "profileduration",
"params": { "profile": "default" }
}
}
}
}Aujourd’hui seul le module Sequencer implémente une vraie réponse (profileduration), utilisée en interne par le module Planner. Aucun autre module livré ne répond de façon utile à une quelconque requête pour l’instant.
Système de fichiers
FS — Choisir le dossier surveillé
Désigne le dossier dont le serveur publie la liste de fichiers (voir Système de fichiers). Le chemin est relatif au webroot ; la chaîne vide désigne la racine.
{ "FS": { "folder": "/Sequencer/gam Cyg/LIGHT/Luminance" } }Le changement est global au serveur (tous les clients voient la nouvelle liste). Le chemin n’est pas validé : un dossier inexistant donne une liste vide.
Contrôle du serveur INDI embarqué
Ces commandes ne sont disponibles que lorsque le serveur dispose d’une instance INDI embarquée.
| Commande | Description |
|---|---|
YA | Démarrer le serveur INDI embarqué |
YZ | Arrêter le serveur INDI embarqué |
YL | Charger un driver INDI ({"YL": {"driver": "<binary>"}}) |
YR | Recharger un driver INDI ({"YR": {"driver": "<binary>"}}) |
YS | Arrêter un driver INDI ({"YS": {"driver": "<binary>"}}) |
YA et YZ n’ont pas de payload ({"YA": {}}). Pour YL, YR et YS, driver est le champ binary d’une entrée de controllerdata.indidrivers.
Récapitulatif du contrôle d’accès
| Valeur grant serveur | Commandes autorisées |
|---|---|
grant-client: "1" | Toutes les commandes |
grant-client: "0" | DU, LO, IL uniquement |
grant-client: "-1" | LO uniquement (authentification requise) |
Les commandes envoyées sans droits suffisants sont silencieusement ignorées par le serveur.