L’intégration caméra permet aujourd’hui d’afficher sur le dashboard l’image d’une caméra publiant un flux RTSP, et d’activer le flux vidéo. On peut aussi depuis une scène envoyer l’image d’une caméra sur Telegram. Mais on pourrait faire plus ;-)\n\nQuand une caméra est compatible ONVIF (c’est par exemple le cas des caméras TP-Link Tapo), il est techniquement possible via ce protocole standardisé de piloter la caméra. Voir explications ici.\n\nJe pense que ce serait utile dans Gladys de pouvoir accéder aux fonctions suivantes depuis une scène ou le dashboard :\n\n* activer/désactiver une caméra\n* modifier l’orientation "horizontale" de la caméra (pan)\n* modifier l’orientation "verticale" de la caméra (tilt)\n* modifier le zoom de la caméra (zoom)\n* (et si possible) positionner la caméra dans l’un de ses préréglages (pan+tilt+zoom)\n* gérer la détection de mouvement de la caméra (pour l’utiliser comme déclencheur de scène)\n* Diffuser un texte en ‹ text-to-speech › sur une caméra (en rendant sélectionnable une caméra dans l’action "parler sur une enceinte").\n\nQuelques exemples de cas d’emplois :\n\n* quand je quitte la maison, active les caméras (et inversement quand je rentre)\n* chaque soir, quand je suis absent, envoie-moi sur Telegram, la photo de ma caméra en regardant successivement différents angles de la pièce\n* en cas d’intrusion, diffuse un message dissuasif sur ma caméra\n* …\n\nDe ce que je comprends de la norme ONVIF, c’est le "Profil S" qu’il faut prendre en compte. Les profiles ONVIF sont décrits ici.\n\nJe ne sais pas si cela peut aider au développement, mais il y a un plug-on Home Assistant dispo sur Github qui gère le protocole ONVIF pour les caméras Tapo : ici
Qui nous fait une petite intégration externe ONVIF ? ![]()
Je peux déjà regarder pour TAPO ayant fait une integration externe TAPO
J’ai lancé Claude dessus il a deja terminé. Je teste ce soir et je redit si cela fonctionne! En attendant j’ai trop chaud alors pause piscine pour moi.
Profite
Ceci dit, les deux ne sont pas incompatibles, avec Claude Code sur mobile ![]()
Le mobile dans l’eau un peu moins ![]()
@pierre-gilles Une proposition de claude pour modifier le sdk et le core pour la gestion des caméras ONVIF, PTZ…
Proposition : contrôle PTZ des caméras dans Gladys
Ce document propose l’ajout du pilotage PTZ (pan / tilt / zoom) des caméras à Gladys :
les constantes à ajouter au SDK, la manière dont une commande atteint l’intégration,
et un widget de contrôle sur le tableau de bord.
Il est écrit depuis une intégration externe existante — gladys-tapo —
qui parle déjà ONVIF à des caméras TP-Link Tapo et pour laquelle le PTZ est mesuré comme
disponible côté caméra, mais inexprimable côté Gladys.
1. Le besoin
Une caméra motorisée (Tapo C210, C500, TC70, et la plupart des caméras ONVIF du marché)
sait faire trois choses qu’aucune catégorie Gladys ne couvre aujourd’hui :
- se déplacer dans une direction, plus ou moins vite et plus ou moins loin ;
- rejoindre une position enregistrée (« entrée », « jardin ») ;
- s’arrêter.
Les usages domotiques correspondants sont classiques :
- « quand on sonne à la porte, la caméra du salon regarde l’entrée » ;
- « la nuit, la caméra se tourne vers le portail ; le matin, elle revient » ;
- piloter la caméra à la main depuis le tableau de bord, sans ouvrir l’app du constructeur.
Ce qui bloque aujourd’hui
DEVICE_FEATURE_TYPES.CAMERA ne contient qu’une seule entrée :
CAMERA: {
IMAGE: 'image',
},
Une caméra Gladys est donc, par construction, une source d’images en lecture seule.
Aucune catégorie existante ne convient pour contourner :
| Piste envisagée | Pourquoi elle est écartée |
|---|---|
CURTAIN.POSITION pour le pan |
Affiche un volet roulant sur une caméra ; sémantiquement faux, et bloque l’ajout d’un vrai PTZ plus tard |
SWITCH.BINARY par direction |
Quatre interrupteurs pour une croix directionnelle ; ne porte ni vitesse ni distance |
| Actions du manifeste | Fonctionne, mais les actions ne sont pas utilisables dans une scène — or c’est l’usage principal |
L’objet de cette proposition est donc d’ajouter la catégorie manquante plutôt que d’en détourner une.
2. Contrainte structurante : setValue ne transporte qu’un scalaire
C’est le point qui détermine toute la conception, et il vaut d’être posé avant les constantes.
Une commande part de l’interface, traverse le cœur et arrive dans l’intégration par
device.setValue :
// server/lib/device/device.setValue.js
async function setValue(device, deviceFeature, value, options = {}) {
const service = this.serviceManager.getService(device.service.name);
await service.device.setValue(device, deviceFeature, value, options);
// ...
}
value est un scalaire — un nombre, ou une chaîne. Or le pilotage PTZ demandé
comporte huit paramètres :
| Paramètre | Valeurs |
|---|---|
| Pan | LEFT, RIGHT |
| Tilt | UP, DOWN |
| Zoom | ZOOM_IN, ZOOM_OUT |
| Distance | coefficient de déplacement, de 0 à 1 |
| Speed | coefficient de vitesse, de 0 à 1 |
| Move Mode | ContinuousMove, RelativeMove, AbsoluteMove, GotoPreset, Stop |
| Continuous duration | pour ContinuousMove, la durée en secondes avant l’arrêt |
| Preset | le jeton du preset à rejoindre, avec GotoPreset |
Ces huit paramètres ne rentrent pas dans un scalaire. Trois façons de résoudre :
Option A — Une feature par commande, les réglages en paramètres d’appareil
Chaque direction devient une feature de type push, et distance / speed /
continuous duration deviennent des params de l’appareil (device.params),
réglés une fois à la configuration.
camera/ptz-left push → déplace à gauche, avec les réglages de l'appareil
camera/ptz-right push
camera/ptz-up push
camera/ptz-down push
camera/ptz-zoom-in push
camera/ptz-zoom-out push
camera/ptz-stop push
camera/ptz-preset string → le jeton du preset à rejoindre
Pour : ne demande aucun changement au cœur. PushDeviceFeature existe déjà et rend
un bouton ; les scènes savent déjà déclencher une feature push. Immédiatement
implémentable.
Contre : la vitesse et la distance ne sont plus réglables par commande — une scène ne
peut pas dire « tourne doucement ». Sept features pour un seul appareil alourdit la liste.
Option B — Une feature unique portant une commande sérialisée
Une seule feature camera/ptz, de type string, dont la valeur est un JSON :
{ "mode": "ContinuousMove", "pan": "LEFT", "speed": 0.5, "duration": 2 }
Pour : couvre les huit paramètres sans rien changer au cœur — setValue accepte déjà
les chaînes (et ne les persiste pas, ce qui convient : une commande n’est pas un état).
Contre : opaque. L’interface ne peut pas construire un formulaire à partir d’une
chaîne libre, et l’éditeur de scènes afficherait un champ texte où l’utilisateur devrait
taper du JSON. C’est une API pour développeurs, pas pour l’utilisateur final.
Option C — Étendre setValue avec des paramètres nommés (recommandée)
setValue reçoit déjà un objet options qu’il transmet tel quel au service. Il suffit de
s’en servir pour les paramètres secondaires, la value portant la commande principale.
// L'intégration reçoit :
setValue(device, feature, 'LEFT', { speed: 0.5, distance: 0.3, duration: 2 });
Pour : couvre les huit paramètres, garde une valeur principale lisible (donc affichable
et scriptable), et n’introduit aucune rupture — options existe et est déjà propagé.
L’interface peut construire un vrai formulaire, puisque chaque paramètre est nommé et typé.
Contre : demande de définir quels options sont valides par type de feature, et que
l’éditeur de scènes sache les présenter.
Recommandation : viser C, en livrant A comme première étape. A est
implémentable immédiatement et couvre déjà « va à la position X quand Y se produit »,
qui est l’usage dominant ; C ajoute ensuite le réglage fin sans invalider A.
3. Constantes à ajouter au SDK
À ajouter dans server/utils/constants.js de Gladys, puis répercuter dans
lib/device-constants.js du SDK — ce dernier étant, par convention documentée en tête de
fichier, un miroir strict du premier.
3.1 Types de features
CAMERA: {
IMAGE: 'image',
// --- PTZ : pilotage d'une caméra motorisée ---
// Directions. Le nom porte l'axe, pas le sens du mouvement attendu par le
// protocole : une caméra montée au plafond peut avoir un axe inversé, ce qui
// se règle dans l'intégration et non dans la sémantique de la feature.
PTZ_LEFT: 'ptz-left',
PTZ_RIGHT: 'ptz-right',
PTZ_UP: 'ptz-up',
PTZ_DOWN: 'ptz-down',
PTZ_ZOOM_IN: 'ptz-zoom-in',
PTZ_ZOOM_OUT: 'ptz-zoom-out',
// Arrêt d'un mouvement continu. Indispensable et pas seulement pratique :
// un ContinuousMove sans Stop laisse la caméra tourner jusqu'à sa butée.
PTZ_STOP: 'ptz-stop',
// Position enregistrée à rejoindre. La valeur est le jeton du preset tel que
// la caméra le nomme, jamais un index : les caméras ONVIF renvoient des
// jetons opaques et non une liste ordonnée.
PTZ_PRESET: 'ptz-preset',
// Position absolue, pour les caméras qui savent la rapporter. Séparée des
// directions parce qu'elle est lisible ET inscriptible, là où une direction
// n'est qu'un ordre.
PTZ_POSITION_PAN: 'ptz-position-pan',
PTZ_POSITION_TILT: 'ptz-position-tilt',
},
3.2 Précédent dans le code existant
L’ajout suit un motif déjà présent : TELEVISION porte LEFT, RIGHT, UP, DOWN,
STOP en tant que types de features distincts, exactement pour exprimer une croix
directionnelle.
TELEVISION: {
// ...
LEFT: 'left',
RIGHT: 'right',
UP: 'up',
DOWN: 'down',
// ...
},
La proposition ne crée donc pas de précédent : elle applique celui-ci aux caméras, en le
complétant de ce que le PTZ exige en plus (presets, arrêt, position absolue).
3.3 Valeurs des paramètres (option C)
Si l’option C est retenue, les coefficients ont besoin d’un domaine explicite :
const PTZ_MOVE_MODES = {
CONTINUOUS: 'ContinuousMove',
RELATIVE: 'RelativeMove',
ABSOLUTE: 'AbsoluteMove',
GOTO_PRESET: 'GotoPreset',
STOP: 'Stop',
};
// `speed` et `distance` sont des coefficients de 0 à 1, volontairement sans unité :
// une caméra exprime sa vitesse en degrés par seconde, une autre en pas moteur, et
// aucune ne le documente. Le coefficient est la seule grandeur portable, et
// l'intégration la traduit vers ce que son protocole attend.
const PTZ_COEFFICIENT_MIN = 0;
const PTZ_COEFFICIENT_MAX = 1;
Nommer les modes d’après la terminologie ONVIF est délibéré : c’est le vocabulaire du
standard que la quasi-totalité des caméras implémentent, et le traduire n’apporterait
qu’une couche de correspondance à maintenir.
4. Widget de contrôle de caméra
4.1 Étendre le widget existant plutôt qu’en créer un second
Gladys possède déjà un widget caméra (front/src/components/boxs/camera/Camera.jsx) qui
affiche l’image et, pour les caméras compatibles, le flux live.
Un widget PTZ séparé obligerait l’utilisateur à poser deux boîtes côte à côte pour une
seule caméra, et à les garder alignées. La proposition est donc d’ajouter les contrôles
au widget existant, affichés seulement si l’appareil porte des features PTZ.
4.2 Disposition proposée
┌─────────────────────────────────┐
│ │
│ image / live │
│ │
│ ┌───┐ │ ← superposition, coin bas droit
│ │ ▲ │ │
│ ┌───┼───┼───┐ │
│ │ ◄ │ ■ │ ► │ │ ■ = stop
│ └───┼───┼───┘ │
│ │ ▼ │ │
│ └───┘ │
│ [ Entrée ▾ ] [-] [+] │ ← presets zoom
└─────────────────────────────────┘
Points de conception, chacun motivé :
- En superposition, pas en dessous. Le widget caméra est souvent posé en petit format ;
une rangée de boutons supplémentaire sous l’image consommerait la hauteur qui sert
justement à voir l’image. - Contrôles masqués par défaut, révélés au survol (et toujours visibles au tactile, où
il n’y a pas de survol). Un tableau de bord consulté d’un coup d’œil n’a pas besoin de
huit boutons en permanence. - Appui maintenu = mouvement continu.
mousedowndéclenche la direction,mouseup
déclenchePTZ_STOP. C’est le geste que tout le monde connaît des interfaces de caméra,
et il correspond exactement au coupleContinuousMove/Stop.
Un clic simple retombe sur unRelativeMoved’un pas. - Presets dans une liste déroulante, pas en boutons : leur nombre varie d’une caméra à
l’autre et leurs noms sont libres. - Zoom séparé de la croix directionnelle, car toutes les caméras motorisées ne zooment
pas — les boutons n’apparaissent que si les features correspondantes existent.
4.3 Rendu des features en dehors du widget
Indépendamment du widget, les features PTZ apparaissent dans la vue « appareil dans une
pièce ». Le routage se fait dans front/src/components/boxs/device-in-room/DeviceRow.jsx :
const ROW_TYPE_BY_FEATURE_TYPE = {
// ...
[DEVICE_FEATURE_TYPES.CAMERA.PTZ_LEFT]: PushDeviceFeature,
[DEVICE_FEATURE_TYPES.CAMERA.PTZ_RIGHT]: PushDeviceFeature,
[DEVICE_FEATURE_TYPES.CAMERA.PTZ_UP]: PushDeviceFeature,
[DEVICE_FEATURE_TYPES.CAMERA.PTZ_DOWN]: PushDeviceFeature,
[DEVICE_FEATURE_TYPES.CAMERA.PTZ_ZOOM_IN]: PushDeviceFeature,
[DEVICE_FEATURE_TYPES.CAMERA.PTZ_ZOOM_OUT]: PushDeviceFeature,
[DEVICE_FEATURE_TYPES.CAMERA.PTZ_STOP]: PushDeviceFeature,
[DEVICE_FEATURE_TYPES.CAMERA.PTZ_POSITION_PAN]: MultiLevelDeviceFeature,
[DEVICE_FEATURE_TYPES.CAMERA.PTZ_POSITION_TILT]: MultiLevelDeviceFeature,
};
PushDeviceFeature et MultiLevelDeviceFeature existent déjà : sept des neuf types
n’appellent donc aucun composant nouveau. Seul PTZ_PRESET en demande un — une liste
déroulante alimentée par les presets de la caméra — et un composant PtzControl groupant
la croix directionnelle serait souhaitable pour éviter d’afficher sept lignes de boutons
empilées.
À noter : une feature dont le type n’est pas dans cette table n’échoue pas, elle est
rendue comme un capteur. Les features PTZ seraient donc visibles mais non pilotables tant
que le routage n’est pas ajouté — ce qui permet de livrer le SDK et l’interface
séparément.
5. Utilisation dans les scènes
C’est l’intérêt principal par rapport aux actions d’intégration, qui ne sont pas
scriptables.
Avec l’option A, une scène « quelqu’un sonne → la caméra regarde l’entrée » s’écrit avec
l’action existante « changer l’état d’un appareil », en réglant camera/ptz-preset sur le
jeton du preset. Aucune action de scène nouvelle n’est nécessaire.
Avec l’option C, l’éditeur de scènes gagne à proposer les paramètres nommés (vitesse,
distance, durée) dans le formulaire de cette action, plutôt que de les laisser aux réglages
de l’appareil.
6. Découpage suggéré
Chaque étape a une valeur propre et peut être livrée seule :
- Constantes dans le SDK et le cœur — les types
CAMERA.PTZ_*. Sans effet visible,
mais débloque immédiatement les intégrations : une intégration peut publier les features
et être pilotée par l’API, avant même que l’interface ne sache les afficher. - Routage dans
DeviceRow.jsx— réutilisePushDeviceFeatureet
MultiLevelDeviceFeature. Rend les features pilotables depuis la vue appareil pour un
coût très faible. - Composant
PtzControl— la croix directionnelle groupée et la liste de presets. - Intégration au widget caméra — la superposition décrite en 4.2.
- Paramètres nommés (option C) — vitesse, distance, durée par commande, et leur
présentation dans l’éditeur de scènes.
Les étapes 1 et 2 suffisent à rendre le PTZ utilisable de bout en bout, scènes comprises.
Merci du retour, c’est très pertinent, j’ai donné à Fable pour analyse et proposition de spec + implémentation.
Je te tiens au courant !
J’ai itéré avec Fable pour quelque chose de plus précis, et pour tenir compte des supported_options, la grande nouveauté récente de Gladys ![]()
La spec :
Dis moi ce que tu en penses
Ca marche je regarde ce soir.
Ca me parait pas mal et encore mieux de ce qui avait été proposé.
Bonsoir @Will_71
Je dispose de 4 caméras ONVIF (justement achetées pour cette raison). N’hésitez pas à me solliciter au besoin de tests de cette intégration.
Merci à vous pour votre implication et pour les partages dont vous faites profiter les utilisateurs de Gladys ![]()
Jean
@Will_71 La PR est prête à être testée !
Image Docker :
ghcr.io/gladysassistant/gladys-preview:claude-onvif-camera-spec-foh27o
Ok
.
Je fait un test dans le weekend. Je te tiens au courant
@Will_71 Tu as pu tester au final ? ![]()
Je devais le faire hier et j’ai eu un imprévu, je n’ai pas touché mon PC hier.
Et le premier jour de congé, je commence par faire de la mécanique sur ma moto, donc il me faudra un peu plus de temps. Je fais le test au plus vite
@pierre-gilles J’ai donc implémenté la fonction sur les caméras TAPO.
Voilà un premier retour:
On avance, j’ai des fonctionnalités en plus sur ma caméra dont la gestion des mouvements PTZ
Sauf que pour l’instant cela ne fonctionne pas. Il faut que je cherche pourquoi, si c’est mon intégration ou autre chose. Le problème, c’est que j’ai aucun log quand j’appuie sur les boutons.
Le contrôle sur l’image de la caméra est bien, sauf que les actions dessous ne sont plus possibles !
Oui, du coup, les boutons sous les flèches ne sont plus utilisables.
Il faudrait qu’ils soient affichés uniquement quand on est en live, car en snapshot, il n’y a pas d’intérêt d’avoir la croix pour controler la caméra.
@Will_71 C’est corrigé
Dis moi si c’est mieux !


