diff --git a/core/ajax/frigate.ajax.php b/core/ajax/frigate.ajax.php index 848cb94c..d256d80f 100644 --- a/core/ajax/frigate.ajax.php +++ b/core/ajax/frigate.ajax.php @@ -173,6 +173,14 @@ ajax::success(); } + if (init('action') == 'frigateDebug') { + $data = frigate::showEvents(false, true); + + ajax::success(array( + "object" => $data, + "json" => json_encode($data), + )); + } throw new Exception(__('Aucune méthode correspondante à', __FILE__) . ' : ' . init('action')); /* * *********Catch exeption*************** */ diff --git a/core/class/frigate.class.php b/core/class/frigate.class.php index 58b76c30..92abbea2 100644 --- a/core/class/frigate.class.php +++ b/core/class/frigate.class.php @@ -84,6 +84,23 @@ private static function configSave(array $array = []): void } } } + + public static function setConfigEqlogic() + { + $eqLogics = self::byType('frigate'); + foreach ($eqLogics as $eqLogic) { + $refresh = config::byKey('refresh_snapshot', 'frigate', 5); + if ($eqLogic->getConfiguration('normal::refresh') === null) { + $eqLogic->setConfiguration('normal::refresh', $refresh); + $eqLogic->save(); + } + if ($eqLogic->getConfiguration('normal::mobilerefresh') === null) { + $eqLogic->setConfiguration('normal::mobilerefresh', $refresh); + $eqLogic->save(); + } + } + } + private static function execCron($frequence) { log::add(__CLASS__, 'debug', "╔════════════════════════ :fg-success:START CRON:/fg: ════════════════════════"); @@ -285,7 +302,8 @@ public function postInsert() {} public function preUpdate() {} // Fonction exécutée automatiquement après la mise à jour de l'équipement - public function postUpdate() {} + public function postUpdate() { + } // Fonction exécutée automatiquement avant la sauvegarde (création ou mise à jour) de l'équipement public function preSave() @@ -407,8 +425,18 @@ public function toHtml($_version = 'dashboard') $replace['#cameraEqlogicId#'] = $this->getLogicalId(); $replace['#cameraName#'] = $this->getConfiguration("name"); $replace['#imgUrl#'] = $this->getConfiguration("img"); - $replace['#enabled#'] = $this->getCmd('info', 'info_enabled') ? $this->getCmd('info', 'info_enabled')->execCmd() : 1; - $replace['#refresh#'] = (float)(config::byKey('refresh_snapshot', 'frigate')) * 1000; + $enabledCmd = $this->getCmd('info', 'info_enabled'); + if ($enabledCmd) { + $value = $enabledCmd->execCmd(); + $replace['#enabled#'] = ($value !== null && $value !== '') ? $value : 1; + } else { + $replace['#enabled#'] = 1; + } + if ($this->getConfiguration('normal::refresh') != '') { + $replace['#refresh#'] = (float)$this->getConfiguration('normal::refresh') * 1000; + } else { + $replace['#refresh#'] = (float)(config::byKey('refresh_snapshot', 'frigate', 5)) * 1000; + } $replace['#actions#'] = $this->buildActions(); $replace['#iaActions#'] = $this->buildIaActions(); @@ -483,7 +511,7 @@ private function buildActions(): string return $html; } - private function buildIaToggleRow(string $startLogical, string $stopLogical, string $infoLogical, string $label): string + private function buildIaToggleRow(string $startLogical, string $stopLogical, string $infoLogical, string $label, string $title = ''): string { $on = $this->getCmd('action', $startLogical); $off = $this->getCmd('action', $stopLogical); @@ -496,18 +524,19 @@ private function buildIaToggleRow(string $startLogical, string $stopLogical, str $cmdId = $isActive ? $off->getId() : $on->getId(); return '
' - . '' . $label . '' + . '' . $label . '' . '' . '
'; } private function buildIaActions(): string { - return $this->buildIaToggleRow('action_start_review_alerts', 'action_stop_review_alerts', 'info_review_alerts', '{{Review alerts}}') - . $this->buildIaToggleRow('action_start_review_detections', 'action_stop_review_detections', 'info_review_detections', '{{Review detections}}') - . $this->buildIaToggleRow('action_start_review_descriptions', 'action_stop_review_descriptions', 'info_review_descriptions', '{{Review descriptions}}') - . $this->buildIaToggleRow('action_start_object_descriptions', 'action_stop_object_descriptions', 'info_object_descriptions', '{{Object descriptions}}') - . $this->buildIaToggleRow('action_start_enabled', 'action_stop_enabled', 'info_enabled', '{{Activations}}'); + return + $this->buildIaToggleRow('action_start_enabled', 'action_stop_enabled', 'info_enabled', '{{Activer la caméra}}', '{{Désactive temporairement la caméra jusqu\'au redémarrage de Frigate. La désactivation interrompt complètement le traitement des flux de la caméra par Frigate. La détection, l\'enregistrement et le débogage deviennent alors indisponibles.}}') + . $this->buildIaToggleRow('action_start_review_alerts', 'action_stop_review_alerts', 'info_review_alerts', '{{Activités : alertes}}', '{{Active ou désactive temporairement les alertes pour cette caméra jusqu\'au redémarrage de Frigate. Lorsque cette option est désactivée, aucune activité nouvelle n\'est générée.}}') + . $this->buildIaToggleRow('action_start_review_detections', 'action_stop_review_detections', 'info_review_detections', '{{Activités : détections}}', '{{Active ou désactive temporairement les alertes et les détections pour cette caméra jusqu\'au redémarrage de Frigate. Lorsque cette option est désactivée, aucune activité nouvelle n\'est générée.}}') + . $this->buildIaToggleRow('action_start_review_descriptions', 'action_stop_review_descriptions', 'info_review_descriptions', '{{Descriptions des activités}}', '{{Activez ou désactivez temporairement les descriptions d\'activités par IA générative jusqu\'au redémarrage. Si désactivé, l\'IA ne sera plus sollicitée pour décrire les activités sur cette caméra.}}') + . $this->buildIaToggleRow('action_start_object_descriptions', 'action_stop_object_descriptions', 'info_object_descriptions', '{{Descriptions d\'objets}}', '{{Activez ou désactivez temporairement les descriptions par IA générative jusqu\'au redémarrage. Si désactivé, l\'IA ne sera plus sollicitée pour décrire les objets suivis sur cette caméra.}}'); } private function buildDetectNow(): string { @@ -714,8 +743,19 @@ private static function getcURL($function, $url, $params = null, $decodeJson = t curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); } + // log de la commande curl + $curl_cmd = "curl -k -X " . $method; + if ($method !== 'DELETE') { + $curl_cmd .= " -H 'Content-Type: application/json'"; + $curl_cmd .= " -d '" . json_encode($params) . "'"; + } + $curl_cmd .= " '" . $url . "'"; + log::add(__CLASS__, 'debug', "║ Commande exécutée : " . $curl_cmd); + // Fin du log + $data = curl_exec($ch); + log::add(__CLASS__, 'debug', "║ Réponse reçue : " . $data); if (curl_errno($ch)) { log::add(__CLASS__, "error", "║ Erreur getcURL (" . $method . "): " . curl_error($ch)); return null; @@ -733,6 +773,9 @@ private static function postcURL($function, $url, $params = null, $decodeJson = private static function putcURL($function, $url, $params = null, $decodeJson = true) { + if (empty($params)) { + $params = new stdClass(); + } return self::getcURL($function, $url, $params, $decodeJson, 'PUT'); } @@ -1519,8 +1562,8 @@ public static function getFolderSize() } catch (Exception $e) { return 0; } - $t1 = microtime(true); - log::add(__CLASS__, 'debug', "║ Taille du dossier calculée via PHP : " . round($size / (1024 * 1024), 2) . " Mo en " . round($t1 - $t0, 2) . "s"); + $t1 = microtime(true); + log::add(__CLASS__, 'debug', "║ Taille du dossier calculée via PHP : " . round($size / (1024 * 1024), 2) . " Mo en " . round($t1 - $t0, 2) . "s"); return round($size / (1024 * 1024), 2); } @@ -1635,44 +1678,55 @@ public static function deleteEvent($id, $all = false) log::add(__CLASS__, 'debug', "╚════════════════════════════════════════════════════════"); return "OK"; } - public static function showEvents() + public static function showEvents(bool $_onlyEnable = FALSE, bool $_allType = FALSE) { $result = []; - $events = frigate_events::all(); + + $events = frigate_events::all($_onlyEnable, $_allType); foreach ($events as $event) { $date = date("d-m-Y H:i:s", $event->getStartTime()); $duree = round($event->getEndTime() - $event->getStartTime(), 0); - $box = $event->getBox(); - $boxArray = is_array($box) ? $box : json_decode($box, true); $result[] = array( - "id" => $event->getId(), - "img" => $event->getLasted(), - "camera" => $event->getCamera(), - "label" => $event->getLabel(), - "box" => $boxArray, - "date" => $date, - "duree" => $duree, - "startTime" => $event->getStartTime(), - "endTime" => $event->getEndTime(), - "snapshot" => $event->getSnapshot(), - "clip" => $event->getClip(), - "thumbnail" => $event->getThumbnail(), - "hasSnapshot" => $event->getHasSnapshot(), - "hasClip" => $event->getHasClip(), - "eventId" => $event->getEventId(), - "score" => $event->getScore(), - "top_score" => $event->getTopScore(), - "type" => $event->getType(), - "isFavorite" => $event->getIsFavorite() ?? 0, - "zones" => $event->getZones() ?? '', - "description" => $event->getRecognition_description() + "id" => $event->getId(), + "eventId" => $event->getEventId(), + "img" => $event->getLasted(), + "camera" => $event->getCamera(), + "label" => $event->getLabel(), + "subLabel" => $event->getSubLabel(), + "box" => json_decode($event->getBox(), true), + "date" => $date, + "duree" => $duree, + "startTime" => $event->getStartTime(), + "endTime" => $event->getEndTime(), + "falsePositive" => $event->getFalsePositive(), + "snapshot" => $event->getSnapshot(), + "clip" => $event->getClip(), + "thumbnail" => $event->getThumbnail(), + "hasSnapshot" => $event->getHasSnapshot(), + "hasClip" => $event->getHasClip(), + "score" => $event->getScore(), + "top_score" => $event->getTopScore(), + "plusId" => $event->getPlusId(), + "retain" => $event->getRetain(), + "type" => $event->getType(), + "isFavorite" => $event->getIsFavorite() ?? 0, + "zones" => $event->getZones() ?? '', + "data" => $event->getData(), + "recognition_type" => $event->getRecognition_type(), + "description" => $event->getRecognition_description(), + "recognition_name" => $event->getRecognition_name(), + "recognition_subname" => $event->getRecognition_subname(), + "recognition_attributes" => $event->getRecognition_attributes(), + "recognition_plate" => $event->getRecognition_plate(), + "recognition_score" => $event->getRecognition_score() ); } - usort($result, [self::class, 'orderByDate']); - + if (!empty($result)) { + usort($result, 'frigate::orderByDate'); + } return $result; } @@ -2021,33 +2075,33 @@ private static function updateCommands($eqlogicId, $type, $frigateEvent) { log::add(__CLASS__, 'debug', "║ MAJ Commandes pour le type : $type"); - $update = function ($label, $subtype, $unit, $logicalId, $value) use ($eqlogicId) { - $cmd = self::createCmd($eqlogicId, $label, $subtype, $unit, $logicalId, "", 0, null, 0); + $update = function ($label, $subtype, $unit, $logicalId, $genericType, $value) use ($eqlogicId) { + $cmd = self::createCmd($eqlogicId, $label, $subtype, $unit, $logicalId, $genericType, 0, null, 0); $cmd->save(); $cmd->event($value ?? ''); $cmd->save(); }; - $update("Reconnaissance - Type", "string", "", "info_detection_type", $type); + $update("Reconnaissance - Type", "string", "", "info_detection_type", "", $type); $withNameScore = ['face', 'lpr', 'classification']; if (in_array($type, $withNameScore)) { - $update("Reconnaissance - Nom", "string", "", "info_detection_name", $frigateEvent->getRecognition_name()); - $update("Reconnaissance - Score", "numeric", "%", "info_detection_score", $frigateEvent->getRecognition_score()); + $update("Reconnaissance - Nom", "string", "", "info_detection_name", "", $frigateEvent->getRecognition_name()); + $update("Reconnaissance - Score", "numeric", "%", "info_detection_score", "", $frigateEvent->getRecognition_score()); } if ($type === 'description') { log::add(__CLASS__, 'debug', "║ Mise à jour de la description : " . $frigateEvent->getRecognition_description()); - $update("Reconnaissance - Description", "string", "", "info_description", $frigateEvent->getRecognition_description()); + $update("Reconnaissance - Description", "string", "", "info_description", "", $frigateEvent->getRecognition_description()); } if ($type === 'lpr') { - $update("Reconnaissance - Plaque d'immatriculation", "string", "", "info_plate", $frigateEvent->getRecognition_plate()); + $update("Reconnaissance - Plaque d'immatriculation", "string", "", "info_plate", "", $frigateEvent->getRecognition_plate()); } if ($type === 'classification') { - $update("Reconnaissance - Label", "string", "", "info_detection_subname", $frigateEvent->getRecognition_subname()); - $update("Reconnaissance - Attributs", "string", "", "info_detection_attributes", $frigateEvent->getRecognition_attributes()); + $update("Reconnaissance - Label", "string", "", "info_detection_subname", "", $frigateEvent->getRecognition_subname()); + $update("Reconnaissance - Attributs", "string", "", "info_detection_attributes", "", $frigateEvent->getRecognition_attributes()); } } @@ -3164,7 +3218,7 @@ public static function handleMqttMessage($_message) if (version_compare($version, "0.14", "<")) { log::add("frigate_MQTT", 'info', ' => Traitement mqtt events <0.14'); log::add("frigate_MQTT", 'warning', ' => Version < 0.14, mettre à jour votre serveur frigate !'); - message::add("frigate",__("Version de Frigate détectée : " . $version . ", certaines fonctionnalités du plugin peuvent ne pas fonctionner correctement. Veuillez mettre à jour votre serveur Frigate pour une expérience optimale.", __FILE__)); + message::add("frigate", __("Version de Frigate détectée : " . $version . ", certaines fonctionnalités du plugin peuvent ne pas fonctionner correctement. Veuillez mettre à jour votre serveur Frigate pour une expérience optimale.", __FILE__)); self::getEvents(true, [$value['after']], $value['type']); event::add('frigate::events', array('message' => 'mqtt_update', 'type' => 'event')); } diff --git a/core/class/frigate_events.class.php b/core/class/frigate_events.class.php index 6fa972a6..b4d070c5 100644 --- a/core/class/frigate_events.class.php +++ b/core/class/frigate_events.class.php @@ -57,14 +57,26 @@ class frigate_events /** * @throws Exception */ - public static function all(bool $_onlyEnable = FALSE) + public static function all(bool $_onlyEnable = FALSE, bool $_allType = FALSE) { - $sql = 'SELECT ' . DB::buildField(__CLASS__) . ' - FROM frigate_events - WHERE type IS NOT NULL'; + $sql = 'SELECT ' . DB::buildField(__CLASS__) . ' FROM frigate_events'; + $where = []; + + if (!$_allType) { + $where[] = 'type IS NOT NULL'; + } + if ($_onlyEnable) { + $where[] = 'enabled = 1'; + } + if (!empty($where)) { + $sql .= ' WHERE ' . implode(' AND ', $where); + } + return DB::Prepare($sql, array(), DB::FETCH_TYPE_ALL, PDO::FETCH_CLASS, __CLASS__); } + + /** * @throws Exception */ diff --git a/core/template/dashboard/widgetCamera.html b/core/template/dashboard/widgetCamera.html index c66c5bcb..2fd2e95f 100644 --- a/core/template/dashboard/widgetCamera.html +++ b/core/template/dashboard/widgetCamera.html @@ -116,6 +116,7 @@ function startImageFetchInterval() { if (!intervalId) { intervalId = setInterval(refreshImage, #refresh#); + console.log('Interval started for camera #id# with refresh rate: ' + #refresh# + ' ms'); } } @@ -234,7 +235,7 @@ position: relative !important; text-align: center; background-color: rgb(var(--defaultBkg-color)) !important; - z-index: 98; + z-index: 100; margin-top: 0px; } @@ -302,7 +303,7 @@ .frigate_widget .btn-ptz-stop { position: absolute; text-align: center; - z-index: 100; + z-index: 99; } .frigate_widget .btn-ptz-left { @@ -360,7 +361,7 @@ .frigate_widget .btn-ptz-zoom-out { position: absolute; text-align: center; - z-index: 100; + z-index: 89; width: 20px; height: 20px; background-color: rgba(0, 0, 0, 0.5); @@ -504,11 +505,11 @@ position: absolute; inset: 0; background-color: rgb(var(--defaultBkg-color)); - opacity: 0.9; + opacity: 0.95; width: 435px !important; height: 300px !important; display: none; - z-index: 20; + z-index: 90; border-radius: inherit; } @@ -563,7 +564,7 @@ justify-content: center; gap: 12px; background-color: rgb(var(--defaultBkg-color)); - z-index: 5; + z-index: 80; color: rgb(var(--defaultText-color)); } diff --git a/core/template/dashboard/widgetPanel.html b/core/template/dashboard/widgetPanel.html index 35e82fed..a9c0e496 100644 --- a/core/template/dashboard/widgetPanel.html +++ b/core/template/dashboard/widgetPanel.html @@ -109,6 +109,7 @@ function startImageFetchInterval() { if (!intervalId) { intervalId = setInterval(refreshImage, #refresh#); + console.log('Interval started for camera #id# with refresh rate: ' + #refresh# + ' ms'); } } @@ -228,7 +229,7 @@ position: relative !important; text-align: center; background-color: rgb(var(--defaultBkg-color)) !important; - z-index: 98; + z-index: 100; margin-top: 0px; } @@ -296,7 +297,7 @@ .frigate_widget .btn-ptz-stop { position: absolute; text-align: center; - z-index: 100; + z-index: 99; } .frigate_widget .btn-ptz-left { @@ -354,7 +355,7 @@ .frigate_widget .btn-ptz-zoom-out { position: absolute; text-align: center; - z-index: 100; + z-index: 89; width: 20px; height: 20px; background-color: rgba(0, 0, 0, 0.5); @@ -498,11 +499,11 @@ position: absolute; inset: 0; background-color: rgb(var(--defaultBkg-color)); - opacity: 0.9; + opacity: 0.95; width: 435px !important; height: 300px !important; display: none; - z-index: 20; + z-index: 90; border-radius: inherit; } @@ -557,7 +558,7 @@ justify-content: center; gap: 12px; background-color: rgb(var(--defaultBkg-color)); - z-index: 5; + z-index: 80; color: rgb(var(--defaultText-color)); } diff --git a/desktop/js/frigate.js b/desktop/js/frigate.js index 4d73ab36..61436b44 100644 --- a/desktop/js/frigate.js +++ b/desktop/js/frigate.js @@ -528,9 +528,24 @@ function printEqLogic(_eqLogic) { observer.observe(imgElement); function startImageFetchInterval() { - if (!intervalId) { - intervalId = setInterval(refreshImage, refresh); + const eqRefresh = $('.eqLogicAttr[data-l1key="configuration"]') + .filter(function () { + return $(this).attr('data-l2key') === 'normal::refresh'; + }) + .val(); + refreshSnap = refresh; + if (eqRefresh && !isNaN(eqRefresh) && eqRefresh > 0) { + refreshSnap = eqRefresh * 1000; + } + + // On arrête toujours l'intervalle existant avant d'en créer un nouveau + if (intervalId) { + clearInterval(intervalId); + intervalId = null; } + + console.log('Refresh interval in milliseconds: ' + refreshSnap); + intervalId = setInterval(refreshImage, refreshSnap); } function stopImageFetchInterval() { @@ -657,12 +672,12 @@ document.getElementById('addCmdHttp').addEventListener('click', function () { }, dataType: 'json', - error: function (error) { - jeedomUtils.showAlert({ - message: error.message, - level: 'danger' - }); - }, + error: function (error) { + jeedomUtils.showAlert({ + message: error.message, + level: 'danger' + }); + }, success: function (data) { jeedomUtils.showAlert({ message: '{{Création de la commande réussie.}}', @@ -675,6 +690,31 @@ document.getElementById('addCmdHttp').addEventListener('click', function () { }) }); +document.getElementById('frigateDebug').addEventListener('click', function () { + domUtils.ajax({ + type: "POST", + url: "plugins/frigate/core/ajax/frigate.ajax.php", + data: { + action: "frigateDebug", + data: { function: "getAllEvents" } + }, + dataType: 'json', + error: function (error) { + jeedomUtils.showAlert({ + message: error.message, + level: 'danger' + }); + }, + success: function (data) { + var json = data.result.json; + const blob = new Blob([json], { + type: "text/plain;charset=utf-8" + }); + saveAs(blob, "getAllEvents.json.txt"); + } + }) +}); + function editHTTP(cmd) { var id = cmd.id; // Récupère l'id var data = cmd.getAttribute('data-request'); // Récupère la valeur de data-request @@ -698,12 +738,12 @@ function editHTTP(cmd) { }, dataType: 'json', - error: function (error) { - jeedomUtils.showAlert({ - message: error.message, - level: 'danger' - }); - }, + error: function (error) { + jeedomUtils.showAlert({ + message: error.message, + level: 'danger' + }); + }, success: function (data) { jeedomUtils.showAlert({ message: '{{Modification de la commande réussie.}}', diff --git a/desktop/php/frigate.php b/desktop/php/frigate.php index 5f5aacd9..9226e5ae 100644 --- a/desktop/php/frigate.php +++ b/desktop/php/frigate.php @@ -13,7 +13,6 @@ sendVarToJS('frigateURL', $urlFrigate); $urlExterne = config::byKey('URLexterne', 'frigate'); sendVarToJS('frigateURLexterne', $urlExterne); - $refresh = (float)(config::byKey('refresh_snapshot', 'frigate')) * 1000; sendVarToJS('refresh', $refresh); ?> @@ -68,6 +67,11 @@
{{Logs Frigate}} +
+ +
+ {{Json}} +
+
+ +
+ +
+
+ +
+
diff --git a/docs/fr_FR/changelog.md b/docs/fr_FR/changelog.md index 278a1a94..9752ea10 100644 --- a/docs/fr_FR/changelog.md +++ b/docs/fr_FR/changelog.md @@ -4,10 +4,16 @@ > >S'il n'y a pas d'information sur la mise à jour, c'est que celle-ci concerne uniquement de la mise à jour de documentation, de traduction ou de texte. -# 13/05/2026 Beta 1.5.2 +# 06/05/2026 Beta & Stable 1.5.4 +- nouveau bouton pour télécharger la liste des évènements (utile pour le debug dev) + +# 19/04/2026 Beta 1.5.3 +- Correction du put curl pour l'activation et la désactivation des caméras via API + +# 13/04/2026 Beta 1.5.2 - Correction widget dashboard -# 09/05/2026 Beta 1.5.1 +# 09/04/2026 Beta 1.5.1 - Amélioration du processus de nettoyage des évènements - Correction maj sur cron via http diff --git a/plugin_info/info.json b/plugin_info/info.json index 50bc434e..560ab001 100644 --- a/plugin_info/info.json +++ b/plugin_info/info.json @@ -1,7 +1,7 @@ { "id": "frigate", "name": "Frigate", - "pluginVersion": "1.5.2", + "pluginVersion": "1.5.4", "installation": "Il est nécessaire d'avoir le plugin mqtt-manager installé avec un broker MQTT sécurisé (user / mdp) pour profiter de toutes les fonctionnalités. Dans le cas contraire, le plugin Frigate sera limité. Voir la documentation pour plus d'informations.", "description": { "fr_FR": "Plugin Frigate pour Jeedom. Ne fonctionne qu'avec les versions de Frigate > 0.13.0. Le plugin n'installe pas le serveur Frigate mais permet de le controler.", diff --git a/plugin_info/install.php b/plugin_info/install.php index 4cda66e3..de69b966 100644 --- a/plugin_info/install.php +++ b/plugin_info/install.php @@ -31,6 +31,7 @@ function frigate_install() frigate::setConfig(); frigate::setConfigCron(); + frigate::setConfigEqlogic(); frigate::addMessages(); Log::add("frigate", 'info', 'Finish Install'); } @@ -93,6 +94,7 @@ function frigate_update() } frigate::setConfig(); + frigate::setConfigEqlogic(); frigate::addMessages(); frigate::deleteLatestFile(); Log::add("frigate", 'info', 'Finish Update');