Bonjour,
Je comprends pas la question ?
C’était pas une question :-).
Non, il envoyait bien 0, 1 ou -1.
Mais j’ai poussé des ajouts et modifs (sleep) pour mettre des valeurs soi-même.
Dis-moi ce que t’en penses.
Oui, c’est ça le problème. (0, 1) ça fait à droite toute ! Alors que (0, 0.2) ça fait un peu à droite.
Pour moi le sleep n’est pas précis. Je m’explique :
- La distance parcourue avec un pas de 0.2 est constante.
- La distance parcourue avec un sleep est la distance parcourue entre le moment où la caméra interprète la première instruction + le temps de sleep + le temps d’interprétation de la deuxième. Tu ne maitrises que ton sleep en fait.
Ouais, faudrait l’ajouter juste après la commande de move.
Je vais l’ajouter.
Je ne parlais pas de rajouter un sleep. Je parlais plus de ne pas envoyer d’entier mais envoyer un pas.
Bonjour Messieurs,
Je ravi de voir qu’enfin de nombreuses camera onvif vont pouvoir profiter de votre travail. Perso j’utilise des caméras Imou mais avec un fonctionnement plus que limité.
Cela va faire plaisir à beaucoup d’utilisateurs, merci.
Sans abuser, est ce vous irez au delà des commandes Ptz comme la gestion des préset et du Home ?
Bon courage pour la suite, et bonne journée
Cdt
Roland
C’est deja dans la modif que j’ai envoyé hier
Déjà, on essaye de corriger les erreurs et implementer des commandes ptz de base.
Ok verra pour la suite
J’ai vu un truc pour ça.
En l’état actuel, le widget est le seul configuré pour utiliser la commande de stop mouvement.
Si tu cliques sur gauche/droite/haut/bas dans l’équipement ou depuis un scénario (pas sur le widget), jamais la commande de stop ne partira, parce que le widget envoi une info en plus qui fait déclencher cette action.
J’ai poussé une modification pour que ça prenne tout le temps en compte.
Cette config fonctionne maintenant chez moi :
@llaumgui En l’état actuel, c’est fiable chez moi, le stop est maintenant dans la suite directe de la commande demandée.
Tu as pu voir ce que ça donne chez toi ?
Que je puisse propose les modifs en l’état.
dans ma todo… J’ai reçu un nouveau NAS ça ma pas mal OQP. Je te fais ça cette semaine.
Je viens de faire le tests et ca ne stop pas… PAr contre si je mets moi même un sleep + stop dans la commande ça semble fonctionner.
J’ai mis 0.2 pour le pas et 500ms pour le délai.
Tu as bien mis la commande de stop dans le champ « URL de commande de stop mouvement » ?
T’as essayé les 2 commandes de stop ?
Je n’avais pas vu ! Maintenant ça marche ! Merci !
Cool.
Ce champ est déjà présent dans le plugin, pour certaines cameras pilotables via page web.
Pour info, j’ai supprimé le plugin PTZ OnVif. Pour moi tout est bon niveau code. Peut-être détailler la doc ?
@llaumgui J’ai rajouté la doc sur le git.
Hello @Loic, je sais que ça fait beaucoup de modifications, j’ai mis le détails des modifications ici, quand t’as un peu de temps.
En gros, corrections des caméras découvertes en ONVIF et ajouts de paramètres et commandes ONVIF.
On avait une erreur avec plusieurs caméras lors de la découverte onvif, (liste contenant qu’une caméra, port 80 au lieu de 8000 par ex…)
Je t’ai partagé mon GitHub https://github.com/Flobul/Jeedom-camera branche beta
core/class/camera.class.php :
Ajout du port onvif qui peut être différent du port 80.
L300 (IP + port onvif):
if (count($result) > 0) {
foreach ($result as $cam) {
$url = parse_url($cam['XAddrs']);
$return[] = array(
'ip' => $url['host'] . ':' . $url['port'],
'type' => $cam['Types'],
'discover' => 'onvif',
'exist' => false,
);
}
}
$cameras = self::byType('camera');
foreach ($return as &$cam) {
foreach ($cameras as $camera) {
if ($cam['ip'] == $camera->getConfiguration('ip') . ':' . $camera->getConfiguration('onvif_port')) {
$cam['exist'] = true;
break;
}
}
}
L323 (sauvegarde de l’ip et du port onvif) :
public static function addDiscoverCam($_config) {
$host = explode(':', $_config['ip']);
$eqLogic = new self();
$eqLogic->setName($host[0]);
$eqLogic->setConfiguration('username', $_config['username']);
$eqLogic->setConfiguration('password', $_config['password']);
$eqLogic->setConfiguration('ip', $host[0]);
$eqLogic->setConfiguration('onvif_port', $host[1]);
$eqLogic->setEqType_name('camera');
$eqLogic->setIsVisible(1);
$eqLogic->setIsEnable(1);
$eqLogic->setConfiguration('device', 'onvif');
$eqLogic->save();
}
Pour permettre de créer des commandes issues du fichier de configuration de onvif (Ptz)
A la fin de la méthiode configOnvif L326, supprimer, cette ligne
$this->save();
Et supprimer cette ligne L672 :
return;
Gérer les commandes actions onvif :
Avant la ligne 1042 ajouter :
if ($eqLogic->getConfiguration('device') == 'onvif') {
$profileToken = $eqLogic->getConfiguration('cameraStreamProfileToken');
$speedX = $eqLogic->getConfiguration('speed_x', 1);
$speedY = $eqLogic->getConfiguration('speed_y', 1);
$speedZ = $eqLogic->getConfiguration('speed_z', 1);
$sleep = $eqLogic->getConfiguration('delay_stop', 0) * 1000;
$onvif = new Ponvif();
$onvif->setUsername($eqLogic->getConfiguration('username'));
$onvif->setPassword($eqLogic->getConfiguration('password'));
$onvif->setIPAddress($eqLogic->getConfiguration('ip') . ':' . $eqLogic->getConfiguration('onvif_port', 80));
$onvif->initialize();
$action = false;
try {
switch ($this->getLogicalId()) {
case 'ptzleft':
$onvif->ptz_ContinuousMove($profileToken, -$speedX, 0);
$action = true;
break;
case 'ptzright':
$onvif->ptz_ContinuousMove($profileToken, $speedX, 0);
$action = true;
break;
case 'ptzup':
$onvif->ptz_ContinuousMove($profileToken, 0, $speedY);
$action = true;
break;
case 'ptzdown':
$onvif->ptz_ContinuousMove($profileToken, 0, -$speedY);
$action = true;
break;
case 'ptzzoomin':
case 'ptzzoomout':
$onvif->ptz_ContinuousMoveZoom($profileToken, ($logicalId === 'ptzzoomout') ? -$speedZ : $speedZ);
$action = true;
break;
case 'ptzmovestop':
$onvif->ptz_ContinuousMove($profileToken, 0, 0);
break;
case 'ptzstop':
$onvif->ptz_Stop($profileToken, 'true', 'true');
break;
case 'ptzreboot':
$onvif->core_SystemReboot();
break;
case 'gotohome':
$onvif->ptz_GotoHomePosition($profileToken, 0, 0);
break;
}
if ($action && strpos($request = $this->getConfiguration('stopCmdUrl'), '#') === 0) {
usleep($sleep);
$cmd = cmd::byId(str_replace('#', '', $request));
if (is_object($cmd)) {
$cmd->execCmd();
}
return true;
}
} catch (Exception $e) {
log::add('camera', 'debug', 'onvif error reason for ' . $this->getLogicalId() . ' : ' . json_encode($onvif->getLastResponse()));
}
}
Modifier le fichier de config onvif pour ajouter les commandes :
/core/config/devices/onvif/onvif.json
{
"remark" : "Découverte des caméras Onvif (attention ne marche pas toujours, aucune garantie possible)",
"name" : "Onvif",
"manufacturer" : "Onvif",
"configuration": {
},
"commands": [
{
"eqType": "camera",
"logicalId": "ptzdown",
"name": "Bas",
"type": "action",
"subType": "other",
"isHistorized": "0",
"display": {
"icon": "<i class=\"fas fa-arrow-down\"><\/i>",
"generic_type": "CAMERA_DOWN"
},
"isVisible": "1"
},
{
"eqType": "camera",
"logicalId": "ptzup",
"name": "Haut",
"type": "action",
"subType": "other",
"isHistorized": "0",
"display": {
"icon": "<i class=\"fas fa-arrow-up\"><\/i>",
"generic_type": "CAMERA_UP"
},
"isVisible": "1"
},
{
"eqType": "camera",
"logicalId": "ptzleft",
"name": "Gauche",
"type": "action",
"subType": "other",
"isHistorized": "0",
"display": {
"icon": "<i class=\"fas fa-arrow-left\"><\/i>",
"generic_type": "CAMERA_LEFT"
},
"isVisible": "1"
},
{
"eqType": "camera",
"logicalId": "ptzright",
"name": "Droite",
"type": "action",
"subType": "other",
"isHistorized": "0",
"display": {
"icon": "<i class=\"fas fa-arrow-right\"><\/i>",
"generic_type": "CAMERA_RIGHT"
},
"isVisible": "1"
},
{
"eqType": "camera",
"logicalId": "ptzzoomin",
"name": "Zoom +",
"type": "action",
"subType": "other",
"isHistorized": "0",
"display": {
"icon": "<i class=\"fas fa-search-plus\"><\/i>",
"generic_type": "CAMERA_ZOOM"
},
"isVisible": "1"
},
{
"eqType": "camera",
"logicalId": "ptzzoomout",
"name": "Zoom -",
"type": "action",
"subType": "other",
"isHistorized": "0",
"display": {
"icon": "<i class=\"fas fa-search-minus\"><\/i>",
"generic_type": "CAMERA_DEZOOM"
},
"isVisible": "1"
},
{
"eqType": "camera",
"logicalId": "ptzstop",
"name": "Stop",
"type": "action",
"subType": "other",
"isHistorized": "0",
"isVisible": "1"
},
{
"eqType": "camera",
"logicalId": "ptzmovestop",
"name": "Stop mouvement",
"type": "action",
"subType": "other",
"isHistorized": "0",
"isVisible": "1"
},
{
"eqType": "camera",
"logicalId": "ptzreboot",
"name": "Redémarrer",
"type": "action",
"subType": "other",
"isHistorized": "0",
"isVisible": "1"
}
]
}
Ajouter L161 de /desktop/php/camera.php
<div class="form-group onvifgOnly">
<label class="col-sm-4 control-label">{{Jeton de profil}}</label>
<div class="col-sm-6">
<input type="text" class="eqLogicAttr form-control" data-l1key="configuration" data-l2key="cameraStreamProfileToken" />
</div>
</div>
<div class="form-group onvifgOnly">
<label class="col-sm-4 control-label">{{Vitesses X, Y et Z}}</label>
<div class="col-sm-2" title="{{X (de 0 à 1)">
<input type="number" min="0" max="1" class="eqLogicAttr form-control" data-l1key="configuration" data-l2key="speed_x" />
</div>
<div class="col-sm-2" title="{{Y (de 0 à 1)}}">
<input type="number" min="0" max="1" class="eqLogicAttr form-control" data-l1key="configuration" data-l2key="speed_y" />
</div>
<div class="col-sm-2" title="{{Z (de 0 à 1)}}">
<input type="number" min="0" max="1" class="eqLogicAttr form-control" data-l1key="configuration" data-l2key="speed_z" />
</div>
</div>
<div class="form-group onvifgOnly">
<label class="col-sm-4 control-label">{{Délai avant commande stop (en ms)}}</label>
<div class="col-sm-3" title="{{Délai (de 0 à 5000)}}">
<input type="number" min="0" max="5000" class="eqLogicAttr form-control" data-l1key="configuration" data-l2key="delay_stop" />
</div>
</div>
3rdparty/ponvif.php :
remplacer :
public function getIPAddress($ipAddress)
{
return $this->ipAddress;
}
par
public function getIPAddress()
{
return $this->ipaddress;
}
Pour que l’ensemble des caméras onvif soient listées, au lieu d’une seule :
remplacer :
if (
socket_select(
$sock_read,
$sock_write,
$sock_except,
$this->discoverytimeout
) > 0
) {
par
while (
socket_select(
$sock_read,
$sock_write,
$sock_except,
$this->discoverytimeout
) > 0
) {
Ajout de 2 méthodes :
public function core_SystemReboot()
{
$REQ = $this->_makeToken();
$post_string =
'<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><s:Header><wsse:Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>%%USERNAME%%</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">%%PASSWORD%%</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">%%NONCE%%</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">%%CREATED%%</Created></UsernameToken></wsse:Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SystemReboot xmlns="http://www.onvif.org/ver10/device/wsdl"/></s:Body></s:Envelope>';
$post_string = str_replace(
["%%USERNAME%%", "%%PASSWORD%%", "%%NONCE%%", "%%CREATED%%"],
[
$REQ["USERNAME"],
$REQ["PASSDIGEST"],
$REQ["NONCE"],
$REQ["TIMESTAMP"],
],
$post_string
);
if (
$this->isFault(
$response = $this->_send_request($this->mediauri, $post_string)
)
) {
if ($this->intransingent) {
throw new Exception("DeleteOSD: Communication error");
}
}
}
public function ptz_GotoHomePosition($profileToken, $pantilt, $zoom)
{
if ($this->ptzuri == "") {
return [];
}
$REQ = $this->_makeToken();
$post_string =
'<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>%%USERNAME%%</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">%%PASSWORD%%</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">%%NONCE%%</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">%%CREATED%%</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><GotoHomePosition><ProfileToken>%%PROFILETOKEN%%</ProfileToken><PanTilt>%%PANTILT%%</PanTilt><Zoom>%%ZOOM%%</Zoom></GotoHomePosition></s:Body></s:Envelope>';
$post_string = str_replace(
[
"%%USERNAME%%",
"%%PASSWORD%%",
"%%NONCE%%",
"%%CREATED%%",
"%%PROFILETOKEN%%",
"%%PANTILT%%",
"%%ZOOM%%",
],
[
$REQ["USERNAME"],
$REQ["PASSDIGEST"],
$REQ["NONCE"],
$REQ["TIMESTAMP"],
$profileToken,
$pantilt,
$zoom,
],
$post_string
);
if ($this->isFault($this->_send_request($this->ptzuri, $post_string))) {
if ($this->intransingent) {
throw new Exception("Stop: Communication error");
}
}
}
Remplacer :
$version["event"] = $capabilities["Events"]["XAddr"];
par
Remplacer :
if (isset($capabilities["Events"]["XAddr"])) {
$version["event"] = $capabilities["Events"]["XAddr"];
}
Corriger la méthode _getActiveSources pour avoir plusieurs sources au lieu d’une et mettre le résultat en tableau même si une seule source (erreur sinon) :
protected function _getActiveSources($videoSources, $profiles)
{
// create an array of $videoSources if only one videoSource
$videoSources =
$videoSources === [] ||
array_keys($videoSources) === range(0, count($videoSources) - 1)
? $videoSources
: [$videoSources];
$sources = [];
// camera may have many sources
for ($i = 0; $i < count($videoSources); $i++) {
// NVT is a camera
if (isset($videoSources[$i]["@attributes"])) {
$sources[$i]["sourcetoken"] =
$videoSources[$i]["@attributes"]["token"];
$this->_getProfileData($sources, $i, $profiles);
} else {
// NVT is an encoder
if (
strtolower(
$videoSources[$i]["@attributes"]["SignalActive"]
) == "true"
) {
$sources[$i]["sourcetoken"] =
$videoSources[$i]["@attributes"]["token"];
$this->_getProfileData($sources, $i, $profiles);
}
} // for
}
return $sources;
}
Remplacer :
if (($result = curl_exec($soap_do)) === false) {
$err = curl_error($soap_do);
$this->lastresponse = ["Fault" => $err];
} else {
$this->lastresponse = $this->_xml2array($result);
}
par :
try {
if (($result = curl_exec($soap_do)) === false) {
$err = curl_error($soap_do);
$this->lastresponse = [
"Fault" => $err,
];
} else {
$this->lastresponse = $this->_xml2array($result);
}
} catch (Exception $e) {
$this->lastresponse = [
"Fault" => $e->getMessage(),
];
}
Bonjour,
Merci pour le code, pour simplifier le truc et surtout eviter que je fasse de grosse boulette en copiant collant le code j’ai demandé a jeedom sas si je pouvais rendre le repo github du plugin public comme ca tu pourras faire un PR. Je te tiens au courant.
Au-delà de rendre public, tu peux le mettre en contributeur limité.


