Amélioration Onvif

Un petit ajout :

  • correctif onvif pour récupérer toutes les sources,
  • enregistrement du jeton du profil pour l’utiliser lors d’envoi de command PTZ,
  • ajout de la gestion de commandes Ptz,

3rdparty/ponvif.php L931 (prise en compte des plusieurs videoSources pour les caméras au lieu d’une :

														protected function _getActiveSources($videoSources, $profiles) {
															$sources = array();
															// 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);
																	}
																}
															}
															return $sources;
														}

class/camera.class.php L345 : (ajout de la sauvegarde du token : cameraStreamProfileToken)

		$mediaUri = preg_replace('/(([0-9]{1,3}\.){3}[0-9]{1,3})/m', '#username#:#password#@#ip#', $onvif->media_GetStreamUri($sources[0][0]['profiletoken']));
		$this->setConfiguration('cameraStreamProfileToken', $sources[0][0]['profiletoken']);
		$this->setConfiguration('cameraStreamAccessUrl', $mediaUri);

et L1043 ajout de gestion des commandes Ptz (move) :

        if ($eqLogic->getConfiguration('device') == 'onvif') {
            $profileToken = $eqLogic->getConfiguration('cameraStreamProfileToken');
            $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();
            if ($this->getLogicalId() == 'ptzleft') {
                $onvif->ptz_ContinuousMove($profileToken,-1,0);
                 return true;
            }
            if ($this->getLogicalId() == 'ptzright') {
                 $onvif->ptz_ContinuousMove($profileToken,1,0);
                 return true;
            }
            if ($this->getLogicalId() == 'ptzup') {
                 $onvif->ptz_ContinuousMove($profileToken,0,1);
                 return true;
            }
            if ($this->getLogicalId() == 'ptzdown') {
                 $onvif->ptz_ContinuousMove($profileToken,0,-1);
                 return true;
            }
            if ($this->getLogicalId() == 'ptzstop') {
                 $onvif->ptz_ContinuousMove($profileToken,0,0); // seems to work anywhere
                //$onvif->ptz_Stop($profileToken, 0, 0); // ActionNotSupported: error on some cameras                return true;
            }
        }

3 « J'aime »

merci je teste ça

ça doit pas être ce numéro de ligne

Ça gère le SOAP ? Genre comme évoqué ici ?

Je pense que ca devrait fonctionner.
Faut tester.

Sur mes cameras Arenti en onvif, ca fonctionne impec.

En fait, tout est déjà dans le plugin camera via la lib ponvif. Faut juste intégrer la gestion des actions comme je l’ai fait

Par contre, tu as saisi quoi comme commande ? Tu peux partager ta conf ?

1 « J'aime »

Sur l’équipement camera qui est onvif, Ajoute une commande action avec le logicalid « ptzright » par exemple

Fait le faire via scenario.

Hum, j’ai testé, mais ça ne semble pas marcher… De même ou renseigner le champs cameraStreamProfileToken ? Pour le moment seul le plugin ptzonvif fonctionne sur ma cam’.

C’est le but de la mise à jour, recupérer le jeton de profile pour pouvoir envoyer les commandes ptz sur ce profil.
Sans le jeton, pas de commande.
Avec ce code, le jeton est récupéré au premier ajout de la camera onvif dans jeedom

Bonsoir,

Merci pour ce partage @Flobul .

Petit retour :

  • testé sur une Reolink trackmix en 4.3.17 Debian Bullseye

  • la modification du 3rdparty/ponvif.php lève une Exception (GetStreamUri: Communication error L323) a chaque fois que je save l’Eqlogic, tandis que le code d’origine passe bien.

  • dans la gestion des commandes Ptz je metterai le return true; dans chaque condition :

if ($this->getLogicalId() == 'ptzdown') {
                $onvif->ptz_ContinuousMove($profileToken,0,-1);
                sleep(1);
                $onvif->ptz_Stop($profileToken,'true','true');
                return true;
            }

et je l’enleverai de la condition globale

if ($eqLogic->getConfiguration('device') == 'onvif') { 
   ... 
   return true;
}

Ainsi ca ne bloc pas la suite si l’user utilise une config onvif mais utilise une requête Curl…

Voila, a moi maintenant de découvrir tout ce que je peut créer comme commandes…

Bonne soirée.

2 « J'aime »

J’ai la même. Du coup, je ne crée pas, je suis partie d’une Caméra existante.

On est OK que c’est en dessous de :

		if ($this->getLogicalId() == 'sendSnapshot') {
			if (!isset($_options['title'])) {
				$_options['title'] = '';
			}
			if (!isset($_options['message'])) {
				$_options['message'] = '';
			}
			$eqLogic->recordCam($_options['title'], $_options['message']);
			return true;
		}

J’ai un message XML moi :
String could not be parsed as XML

Après comme dit, j’ai du SOAP.

Tu as l’erreur à la méthode « media_GetStreamUri ».
Il faudrait mettre une ligne de log pour voir la valeur de $sources[0][0][‹ profiletoken ›], quite à voir tout le $sources.
$mediaUri = preg_replace('/(([0-9]{1,3}\.){3}[0-9]{1,3})/m', '#username#:#password#@#ip#', $onvif->media_GetStreamUri($sources[0][0]['profiletoken']));

Si j’ajoute les lignes à la méthode configOnvif:

		$sources = $onvif->getSources();
        log::add('camera','debug', 'TEST ' . json_encode($sources));
		$mediaUri = preg_replace('/(([0-9]{1,3}\.){3}[0-9]{1,3})/m', '#username#:#password#@#ip#', $onvif->media_GetStreamUri($sources[0][0]['profiletoken']));
        log::add('camera','debug', 'TEST2 ' . json_encode($onvif->media_GetStreamUri($sources[0][0]['profiletoken'])));
		$this->setConfiguration('cameraStreamProfileToken', $sources[0][0]['profiletoken']); //save profiletoken for sending onvif ptz cmd

voilà les logs :

[2023-08-08 13:06:07]DEBUG : TEST [{"sourcetoken":"V_SRC_000","0":{"profilename":"PROFILE_000","profiletoken":"PROFILE_000","encodername":"V_ENC_000","encoding":"H264","width":"2304","height":"1296","fps":"15","bitrate":"1536"}},{"sourcetoken":"V_SRC_001","0":{"profilename":"PROFILE_001","profiletoken":"PROFILE_001","encodername":"V_ENC_001","encoding":"H264","width":"640","height":"360","fps":"15","bitrate":"512"}}]
[2023-08-08 13:06:07]DEBUG : TEST2 "rtsp:\/\/192.168.23.218:8554\/Streaming\/Channels\/101"

Si ça lève une une exception, capture-la avec :

                try {
				    //CODE ici
                } catch (Exception $e) {
                    log::add('camera', 'debug', 'onvif error reason : ' . json_encode($onvif->getLastResponse()));
                }

Merci.
J’ai également retiré le ptz_Stop qui peut être ajouté en action après commande.
Certaines caméras supportent pas ptz_Stop…
On peut voir les commandes qui passent pas avec try catch :

                try {
				    $onvif->ptz_Stop($profileToken, "true", "false");
                } catch (Exception $e) {
                    log::add('camera', 'debug', 'onvif error reason : ' . json_encode($onvif->getLastResponse()));
                }

L’erreur était déjà présente avant la modification comme au-dessus.
Faudrait afficher en log les variables pour comprendre votre problème.

		if ($this->getLogicalId() == 'sendSnapshot') {
			if (!isset($_options['title'])) {
				$_options['title'] = '';
			}
			if (!isset($_options['message'])) {
				$_options['message'] = '';
			}
			$eqLogic->recordCam($_options['title'], $_options['message']);
			return true;
		}
		if ($eqLogic->getConfiguration('device') == 'onvif') {
			$profileToken = $eqLogic->getConfiguration('cameraStreamProfileToken');
			$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();
			if ($this->getLogicalId() == 'ptzleft') {
                try {
				    $onvif->ptz_ContinuousMove($profileToken,-1,0);
                } catch (Exception $e) {
                    log::add('camera', 'debug', 'onvif error reason : ' . json_encode($onvif->getLastResponse()));
                }
                return true;
			}
			if ($this->getLogicalId() == 'ptzright') {
                try {
				    $onvif->ptz_ContinuousMove($profileToken,1,0);
                } catch (Exception $e) {
                    log::add('camera', 'debug', 'onvif error reason : ' . json_encode($onvif->getLastResponse()));
                }
                return true;
			}
			if ($this->getLogicalId() == 'ptzup') {
                try {
				    $onvif->ptz_ContinuousMove($profileToken,0,1);
                } catch (Exception $e) {
                    log::add('camera', 'debug', 'onvif error reason : ' . json_encode($onvif->getLastResponse()));
                }
                return true;
			}
			if ($this->getLogicalId() == 'ptzdown') {
                try {
				    $onvif->ptz_ContinuousMove($profileToken,0,-1);
                } catch (Exception $e) {
                    log::add('camera', 'debug', 'onvif error reason : ' . json_encode($onvif->getLastResponse()));
                }
                return true;
			}
			if ($this->getLogicalId() == 'ptzstop') {
                try {
                    $onvif->ptz_ContinuousMove($profileToken,0,0); // seems to work anywhere
				    //$onvif->ptz_Stop($profileToken, 0, 0); // ActionNotSupported: error on some cameras                } catch (Exception $e) {
                    log::add('camera', 'debug', 'onvif error reason : ' . json_encode($onvif->getLastResponse()));
                }
                return true;
			}
		}
		if (strpos($request, '#') === 0) {
			$cmd = cmd::byId(str_replace('#', '', $request));
			if (is_object($cmd)) {
				$cmd->execCmd();
			}
		} else {

2 caméras de même marque, l’une accepte la commande :

$onvif->ptz_Stop($profileToken, "true", "true"); // ActionNotSupported: error on some cameras

et arrête le mouvement.
L’autre refuse avec l’erreur "onvif error reason : {"Envelope":{"Body":{"Fault":{"Code":{"Value":"s:Receiver","Subcode":{"Value":"ter:ActionNotSupported"}},"Reason":{"Text":"Action Not Implemented"}}}}}"

Mais cette commande semble fonctionner partout et arrêter le mouvement :

$onvif->ptz_ContinuousMove($profileToken,0,0); // seems to work anywhere

J’ai fait les modification et j’ai ça maintenant :

String could not be parsed as XML

Je pense qu’il faut rajouter des éléments pour débuguer. Mais où…

Je viens de regarder le plugin PTZ Onvif et il se base sur la même classe… Donc pas de raison que Camera ne marche pas… Après dans les diffs, entre les 2 sur PTZ Onvif tu saisis ton Token.

Le message apparait dans la bulle en rouge ?

Bon, je viens de regarder et en fait le problème de XML est sur la classe ponvif. Du coup, j’ai résolvé ce problème en prenant la classe mise à jour par @freddye83 sur PTZ Onvif.

J’ai un nouveau problème de GetStreamUri: Communication error. Et c’est dû au fait que mon $onvif->getSources();ne me retourne rien. Je pense qu’utiliser cette fonction part du postulat que la caméra est dans un onvif pure, propre et standard… Du coup ça coupe un gros % de cam’ chinoises…

L’idéal (et c’est ce que fait PTZ Onvif) serait, à mon avis, de rajouter un champ pour renseigner le profiletoken si on ne ne trouve pas comme ça.

Autant corriger sur le plugin camera également.
Je regarde les différences.

Ca, c’est facilement réalisable.

J’ai pu mettre des log dans les 2 class et voici ce qu’il en sort :

_getActiveSources

log::add('camera','debug', __CLASS__ . ' >>> $videoSources = ' . json_encode($videoSources));

Ponvif >>> $videoSources = {"@attributes":{"token":"000"},"Framerate":"25","Resolution":{"Width":"3840","Height":"2160"},"Imaging":{"Brightness":"128","ColorSaturation":"128","Contrast":"128","IrCutFilter":"ON","Sharpness":"128"}}

Le count($videoSources) me renvoi 4.

dans cette boucle un log::add sur $videoSources[$i] me renvoi un null donc je rentre jamais dans la condition if (isset($videoSources[$i]['@attributes'])), je me retrouve donc avec un return $sources; qui est vide.

configOnvif

log::add('camera','debug', __CLASS__ . ' >>> $sources = ' . json_encode($sources));

renvoie donc camera >>> $sources = []

Je pense que tu es dans mon cas: une implémentation pas propre d’onvif et du coup tu découvres mal. D’où le fait de le saisir depuis l’IHM de Jeedom plutôt que d’essayer de le découvrir.

Bah je dirait plutôt le contraire car le code d’origine marche, donc je dirait que ma cam a une bonne implémentation.