From c65fc28b218a5884cfa3951b74ea93f3f230aeb7 Mon Sep 17 00:00:00 2001 From: Julien C Date: Tue, 30 Apr 2024 13:18:00 +0200 Subject: [PATCH 001/128] feat: new maxsizeLog parameter, and log::chunck in cronHourly --- core/class/jeedom.class.php | 7 +++++++ core/class/log.class.php | 17 ++++++++++++++--- core/config/default.config.ini | 1 + desktop/php/administration.php | 6 ++++++ 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/core/class/jeedom.class.php b/core/class/jeedom.class.php index b0d465631a..7945ec04b8 100644 --- a/core/class/jeedom.class.php +++ b/core/class/jeedom.class.php @@ -1293,6 +1293,13 @@ public static function cronHourly() { } catch (Error $e) { log::add('jeedom', 'error', $e->getMessage()); } + try{ + log::chunk('', 'hourly'); + }catch (Exception $e) { + log::add('jeedom', 'error', $e->getMessage()); + } catch (Error $e) { + log::add('jeedom', 'error', $e->getMessage()); + } } /*************************************************************************************/ diff --git a/core/class/log.class.php b/core/class/log.class.php index 7417b366a5..7e2c97f1f3 100644 --- a/core/class/log.class.php +++ b/core/class/log.class.php @@ -124,13 +124,14 @@ public static function add($_log, $_type, $_message, $_logicalId = '') { } } - public static function chunk($_log = '') { + public static function chunk($_log = '', $_callCronHourly = '') { $paths = array(); if ($_log != '') { $paths = array(self::getPathToLog($_log)); } else { $relativeLogPaths = array('', 'scenarioLog/'); foreach ($relativeLogPaths as $relativeLogPath) { + $logPath = self::getPathToLog($relativeLogPath); $logs = ls($logPath, '*'); foreach ($logs as $log) { @@ -140,12 +141,12 @@ public static function chunk($_log = '') { } foreach ($paths as $path) { if (is_file($path)) { - self::chunkLog($path); + self::chunkLog($path, $_callCronHourly = ''); } } } - public static function chunkLog($_path) { + public static function chunkLog($_path, $_callCronHourly = '') { if (strpos($_path, '.htaccess') !== false) { return; } @@ -153,6 +154,16 @@ public static function chunkLog($_path) { if ($maxLineLog < self::DEFAULT_MAX_LINE) { $maxLineLog = self::DEFAULT_MAX_LINE; } + if($_callCronHourly){ + $maxSizeLog = self::getConfig('maxSizeLog'); + if (filesize($_path) >= $maxSizeLog) { + try { + com_shell::execute(system::getCmdSudo() . 'chmod 664 ' . $_path . ' > /dev/null 2>&1;echo "$(tail -n ' . $maxLineLog . ' ' . $_path . ')" > ' . $_path); + } catch (\Exception $e) { + } + } + return; + } try { com_shell::execute(system::getCmdSudo() . 'chmod 664 ' . $_path . ' > /dev/null 2>&1;echo "$(tail -n ' . $maxLineLog . ' ' . $_path . ')" > ' . $_path); } catch (\Exception $e) { diff --git a/core/config/default.config.ini b/core/config/default.config.ini index 7bafc45386..7f66a9d2dc 100644 --- a/core/config/default.config.ini +++ b/core/config/default.config.ini @@ -78,6 +78,7 @@ security::whiteips = "127.0.0.1;192.168.*.*;10.*.*.*;172.*.*.*" ;Log maxLineLog = 500 +maxSizeLog = 5 log::level = 400 log::syslogudpport = 514 log::syslogudpfacility = user diff --git a/desktop/php/administration.php b/desktop/php/administration.php index ea1fb1a0a3..fc068bd201 100644 --- a/desktop/php/administration.php +++ b/desktop/php/administration.php @@ -994,6 +994,12 @@ +
+ +
+ +
+
From 7f77ef4940da5292f9d868b7dca5f30b40f432be Mon Sep 17 00:00:00 2001 From: Julien C Date: Tue, 30 Apr 2024 14:24:07 +0200 Subject: [PATCH 002/128] fix variableName and re clear chunkLog --- core/class/jeedom.class.php | 2 +- core/class/log.class.php | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/core/class/jeedom.class.php b/core/class/jeedom.class.php index 7945ec04b8..60d3119504 100644 --- a/core/class/jeedom.class.php +++ b/core/class/jeedom.class.php @@ -1294,7 +1294,7 @@ public static function cronHourly() { log::add('jeedom', 'error', $e->getMessage()); } try{ - log::chunk('', 'hourly'); + log::chunk('', $_onlyIfSizeExceeded = true); }catch (Exception $e) { log::add('jeedom', 'error', $e->getMessage()); } catch (Error $e) { diff --git a/core/class/log.class.php b/core/class/log.class.php index 7e2c97f1f3..c16e952469 100644 --- a/core/class/log.class.php +++ b/core/class/log.class.php @@ -124,7 +124,7 @@ public static function add($_log, $_type, $_message, $_logicalId = '') { } } - public static function chunk($_log = '', $_callCronHourly = '') { + public static function chunk($_log = '', $_onlyIfSizeExceeded = '') { $paths = array(); if ($_log != '') { $paths = array(self::getPathToLog($_log)); @@ -141,12 +141,22 @@ public static function chunk($_log = '', $_callCronHourly = '') { } foreach ($paths as $path) { if (is_file($path)) { - self::chunkLog($path, $_callCronHourly = ''); + if($_onlyIfSizeExceeded){ + $maxSizeLog = self::getConfig('maxSizeLog'); + if (filesize($_path) >= $maxSizeLog) { + try { + com_shell::execute(system::getCmdSudo() . 'chmod 664 ' . $_path . ' > /dev/null 2>&1;echo "$(tail -n ' . $maxLineLog . ' ' . $_path . ')" > ' . $_path); + } catch (\Exception $e) { + } + continue; + } + } + self::chunkLog($path); } } } - public static function chunkLog($_path, $_callCronHourly = '') { + public static function chunkLog($_path) { if (strpos($_path, '.htaccess') !== false) { return; } @@ -154,16 +164,6 @@ public static function chunkLog($_path, $_callCronHourly = '') { if ($maxLineLog < self::DEFAULT_MAX_LINE) { $maxLineLog = self::DEFAULT_MAX_LINE; } - if($_callCronHourly){ - $maxSizeLog = self::getConfig('maxSizeLog'); - if (filesize($_path) >= $maxSizeLog) { - try { - com_shell::execute(system::getCmdSudo() . 'chmod 664 ' . $_path . ' > /dev/null 2>&1;echo "$(tail -n ' . $maxLineLog . ' ' . $_path . ')" > ' . $_path); - } catch (\Exception $e) { - } - } - return; - } try { com_shell::execute(system::getCmdSudo() . 'chmod 664 ' . $_path . ' > /dev/null 2>&1;echo "$(tail -n ' . $maxLineLog . ' ' . $_path . ')" > ' . $_path); } catch (\Exception $e) { From 83ac82e3353b64342a687db7d5fe1820899b9c3d Mon Sep 17 00:00:00 2001 From: Julien C Date: Tue, 30 Apr 2024 14:49:34 +0200 Subject: [PATCH 003/128] fix code --- core/class/jeedom.class.php | 2 +- core/class/log.class.php | 19 ++++++------------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/core/class/jeedom.class.php b/core/class/jeedom.class.php index 60d3119504..23cb83b270 100644 --- a/core/class/jeedom.class.php +++ b/core/class/jeedom.class.php @@ -1294,7 +1294,7 @@ public static function cronHourly() { log::add('jeedom', 'error', $e->getMessage()); } try{ - log::chunk('', $_onlyIfSizeExceeded = true); + log::chunk('', True); }catch (Exception $e) { log::add('jeedom', 'error', $e->getMessage()); } catch (Error $e) { diff --git a/core/class/log.class.php b/core/class/log.class.php index c16e952469..f8909c7663 100644 --- a/core/class/log.class.php +++ b/core/class/log.class.php @@ -124,7 +124,7 @@ public static function add($_log, $_type, $_message, $_logicalId = '') { } } - public static function chunk($_log = '', $_onlyIfSizeExceeded = '') { + public static function chunk($_log = '', $_onlyIfSizeExceeded = False) { $paths = array(); if ($_log != '') { $paths = array(self::getPathToLog($_log)); @@ -140,19 +140,12 @@ public static function chunk($_log = '', $_onlyIfSizeExceeded = '') { } } foreach ($paths as $path) { - if (is_file($path)) { - if($_onlyIfSizeExceeded){ - $maxSizeLog = self::getConfig('maxSizeLog'); - if (filesize($_path) >= $maxSizeLog) { - try { - com_shell::execute(system::getCmdSudo() . 'chmod 664 ' . $_path . ' > /dev/null 2>&1;echo "$(tail -n ' . $maxLineLog . ' ' . $_path . ')" > ' . $_path); - } catch (\Exception $e) { - } + if (is_file($path)) { + if($_onlyIfSizeExceeded && filesize($path) < (self::getConfig('maxSizeLog') * 1024 * 1024) ){ continue; - } - } - self::chunkLog($path); - } + } + self::chunkLog($path); + } } } From e3c93edcee081696052ac397aca555e5097f9198 Mon Sep 17 00:00:00 2001 From: "Julien C." Date: Tue, 18 Mar 2025 11:29:36 +0100 Subject: [PATCH 004/128] Update administration.php --- desktop/php/administration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/php/administration.php b/desktop/php/administration.php index fc068bd201..febd2d23ac 100644 --- a/desktop/php/administration.php +++ b/desktop/php/administration.php @@ -995,7 +995,7 @@
- +
From 52eebc17b64b9bd1c0b66623ef747f93b1fcf872 Mon Sep 17 00:00:00 2001 From: "Julien C." Date: Mon, 9 Jun 2025 10:47:58 +0200 Subject: [PATCH 005/128] add disabled on inputId --- desktop/modal/search.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/modal/search.php b/desktop/modal/search.php index 051940aa03..87c1ab5b68 100644 --- a/desktop/modal/search.php +++ b/desktop/modal/search.php @@ -75,7 +75,7 @@
- + From bc8fe60b46ea9e35d1cdb02085cb6e52a8586e67 Mon Sep 17 00:00:00 2001 From: "Julien C." Date: Mon, 9 Jun 2025 11:56:28 +0200 Subject: [PATCH 006/128] Update search.php --- desktop/modal/search.php | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/desktop/modal/search.php b/desktop/modal/search.php index 87c1ab5b68..b57cf26c1b 100644 --- a/desktop/modal/search.php +++ b/desktop/modal/search.php @@ -75,7 +75,7 @@
- + @@ -652,6 +652,24 @@ jeeM.init() + function updateEquipmentInputDisabled() { + var inputIds = [ + 'in_searchFor_equipment', + 'in_searchFor_command', + ]; + inputIds.forEach(function(id) { + var input = document.getElementById(id); + if (input) { + input.disabled = true; + } + }); + } + + updateEquipmentInputDisabled(); + + document.getElementById('sel_searchByType')?.addEventListener('change', updateEquipmentInputDisabled); + + //Manage events outside parents delegations: document.querySelector('#md_search #in_searchFor_string')?.addEventListener('keypress', function(event) { if (event.which === 13) { @@ -785,6 +803,8 @@ if (dataFilter[i] == 1) table.parentNode.seen() else table.parentNode.unseen() } + + updateEquipmentInputDisabled(); return } From 50abbde16b18a549588c0532d1b16f24651f1c13 Mon Sep 17 00:00:00 2001 From: "Julien C." Date: Mon, 9 Jun 2025 12:00:11 +0200 Subject: [PATCH 007/128] Update search.php --- desktop/modal/search.php | 1 - 1 file changed, 1 deletion(-) diff --git a/desktop/modal/search.php b/desktop/modal/search.php index b57cf26c1b..62b4635a10 100644 --- a/desktop/modal/search.php +++ b/desktop/modal/search.php @@ -667,7 +667,6 @@ function updateEquipmentInputDisabled() { updateEquipmentInputDisabled(); - document.getElementById('sel_searchByType')?.addEventListener('change', updateEquipmentInputDisabled); //Manage events outside parents delegations: From fc5b1879846dcf9f479216c8dd2f099c106af5cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc?= <1536036+zoic21@users.noreply.github.com> Date: Tue, 17 Jun 2025 12:29:29 +0200 Subject: [PATCH 008/128] Fix too much refresh https://community.jeedom.com/t/refresh-de-la-tuile-sur-une-cmd-action-color/141380/6 --- core/class/cmd.class.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/class/cmd.class.php b/core/class/cmd.class.php index 9000d3935f..27f07b9827 100644 --- a/core/class/cmd.class.php +++ b/core/class/cmd.class.php @@ -3066,8 +3066,8 @@ public function getDisplay($_key = '', $_default = '') { return utils::getJsonAttr($this->display, $_key, $_default); } - public function setDisplay($_key, $_value) { - if ($this->getDisplay($_key) !== $_value) { + public function setDisplay($_key, $_value = null) { + if ($this->getDisplay($_key,null) !== $_value) { $this->_needRefreshWidget = true; $this->_changed = true; } From d593c0650a009987c020a6b4108c42bb8996077b Mon Sep 17 00:00:00 2001 From: David <79108364+Phpvarious@users.noreply.github.com> Date: Sun, 27 Jul 2025 21:49:16 +0200 Subject: [PATCH 009/128] Update user.class.php --- core/class/user.class.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/class/user.class.php b/core/class/user.class.php index 8f834215c1..95567f5302 100644 --- a/core/class/user.class.php +++ b/core/class/user.class.php @@ -122,7 +122,7 @@ public static function connect(string $_login, string $_mdp) { ->setProfils($profile); $user->save(); log::add("connection", "info", __('User created from the LDAP :', __FILE__) . ' ' . $_login); - jeedom::event('user_connect'); + jeedom::event('user_connect', false, array('trigger_value' => $_login)); // TODO : if username == password => change ldap password log::add('event', 'info', __('User connection accepted', __FILE__) . ' ' . $_login); return $user; @@ -145,7 +145,7 @@ public static function connect(string $_login, string $_mdp) { if (is_object($user)) { $user->setOptions('lastConnection', date('Y-m-d H:i:s')); $user->save(); - jeedom::event('user_connect'); + jeedom::event('user_connect', false, array('trigger_value' => $_login)); log::add('event', 'info', __('Local account found for', __FILE__) . ' ' . $_login); log::add('event', 'info', __('User connection accepted', __FILE__) . ' ' . $_login); } From f0962e567a7cc7518cce6c22214eac2dcb7a2d99 Mon Sep 17 00:00:00 2001 From: Luc SANCHEZ <4697568+ColonelMoutarde@users.noreply.github.com> Date: Wed, 6 Aug 2025 10:21:51 +0200 Subject: [PATCH 010/128] Create .editorconfig MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(editorconfig) : nettoyage et simplification des règles # .editorconfig # Ce fichier définit des règles de style de code (indentation, encodage, fin de ligne, etc.) # afin d'assurer une cohérence entre les différents éditeurs et IDEs utilisés par les développeurs. # Il est automatiquement pris en charge par les éditeurs compatibles (comme VS Code, PHPStorm, etc.). # Plus d'informations : https://editorconfig.org - Simplification des motifs globaux (retrait des ** superflus) - Ajout de insert_final_newline pour une meilleure cohérence des fichiers --- .editorconfig | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..6d70ac906d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ +# This file is for unifying the coding style for different editors and IDEs. +# More information at http://EditorConfig.org + +# Set this file as the root +root = true + +# Global settings for all files +[*] +indent_style = space +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +# PHP files +[*.php] +indent_size = 4 + +# Frontend files +[*.{css,html,js,less,sass,scss}] +indent_size = 2 From 99715ffbd6154c88d0b2f7a43e1c7e9c35394af4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc?= <1536036+zoic21@users.noreply.github.com> Date: Tue, 25 Nov 2025 14:35:25 +0100 Subject: [PATCH 011/128] Fix error Class "SolarData\SolarData" not found --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 25d7fc3830..607694c32b 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,6 @@ { "require": { + "abbadon1334/sun-position-spa-php" : "^2", "dragonmantank/cron-expression": "^3", "symfony/expression-language": "5 - 7", "pragmarx/google2fa-qrcode": "^3", From 593a8ac98d10a3a782e1525e190ca454b267efee Mon Sep 17 00:00:00 2001 From: Salvialf Date: Tue, 25 Nov 2025 18:40:46 +0100 Subject: [PATCH 012/128] update composer.lock --- composer.lock | 206 +++++++++++++++++++++++++------------------------- 1 file changed, 103 insertions(+), 103 deletions(-) diff --git a/composer.lock b/composer.lock index b78c8572c4..3c89db4378 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,59 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6d3817ee337cc5ed162b1e9463030e8a", + "content-hash": "be46045c22daf0366a838f96eed56caf", "packages": [ + { + "name": "abbadon1334/sun-position-spa-php", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/abbadon1334/sun-position-spa-php.git", + "reference": "26244d51284ae80061dc21f26d4b1c31672354bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/abbadon1334/sun-position-spa-php/zipball/26244d51284ae80061dc21f26d4b1c31672354bf", + "reference": "26244d51284ae80061dc21f26d4b1c31672354bf", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "require-dev": { + "codacy/coverage": "dev-master", + "friendsofphp/php-cs-fixer": "dev-master@dev", + "phpmd/phpmd": "2.6.0", + "phpmetrics/phpmetrics": "dev-master@dev", + "phpstan/phpstan": "0.11.5", + "phpunit/phpunit": "*", + "squizlabs/php_codesniffer": "3.4.2", + "symfony/yaml": "~2.1|~3.0|~4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "SolarData\\": "src/", + "SolarData\\Tests\\": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Francesco Danti", + "email": "fdanti@gmail.com" + } + ], + "description": "solar data calculation and sun position", + "support": { + "issues": "https://github.com/abbadon1334/sun-position-spa-php/issues", + "source": "https://github.com/abbadon1334/sun-position-spa-php/tree/master" + }, + "time": "2019-07-08T09:51:15+00:00" + }, { "name": "bacon/bacon-qr-code", "version": "2.0.8", @@ -128,16 +179,16 @@ }, { "name": "dasprid/enum", - "version": "1.0.6", + "version": "1.0.7", "source": { "type": "git", "url": "https://github.com/DASPRiD/Enum.git", - "reference": "8dfd07c6d2cf31c8da90c53b83c026c7696dda90" + "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/8dfd07c6d2cf31c8da90c53b83c026c7696dda90", - "reference": "8dfd07c6d2cf31c8da90c53b83c026c7696dda90", + "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/b5874fa9ed0043116c72162ec7f4fb50e02e7cce", + "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce", "shasum": "" }, "require": { @@ -172,35 +223,34 @@ ], "support": { "issues": "https://github.com/DASPRiD/Enum/issues", - "source": "https://github.com/DASPRiD/Enum/tree/1.0.6" + "source": "https://github.com/DASPRiD/Enum/tree/1.0.7" }, - "time": "2024-08-09T14:30:48+00:00" + "time": "2025-09-16T12:23:56+00:00" }, { "name": "dragonmantank/cron-expression", - "version": "v3.4.0", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "8c784d071debd117328803d86b2097615b457500" + "reference": "1b2de7f4a468165dca07b142240733a1973e766d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/8c784d071debd117328803d86b2097615b457500", - "reference": "8c784d071debd117328803d86b2097615b457500", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/1b2de7f4a468165dca07b142240733a1973e766d", + "reference": "1b2de7f4a468165dca07b142240733a1973e766d", "shasum": "" }, "require": { - "php": "^7.2|^8.0", - "webmozart/assert": "^1.0" + "php": "^7.2|^8.0" }, "replace": { "mtdowling/cron-expression": "^1.0" }, "require-dev": { - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.0", - "phpunit/phpunit": "^7.0|^8.0|^9.0" + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.32|^2.1.31", + "phpunit/phpunit": "^8.5.48|^9.0" }, "type": "library", "extra": { @@ -231,7 +281,7 @@ ], "support": { "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.4.0" + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.5.0" }, "funding": [ { @@ -239,20 +289,20 @@ "type": "github" } ], - "time": "2024-10-09T13:47:03+00:00" + "time": "2025-10-31T18:36:32+00:00" }, { "name": "influxdata/influxdb-client-php", - "version": "3.6.0", + "version": "3.8.0", "source": { "type": "git", "url": "https://github.com/influxdata/influxdb-client-php.git", - "reference": "3606b12214508f22126b7ed0565d53380674312a" + "reference": "59ac11d63ce030973c79d5b05797813761a0d58e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/influxdata/influxdb-client-php/zipball/3606b12214508f22126b7ed0565d53380674312a", - "reference": "3606b12214508f22126b7ed0565d53380674312a", + "url": "https://api.github.com/repos/influxdata/influxdb-client-php/zipball/59ac11d63ce030973c79d5b05797813761a0d58e", + "reference": "59ac11d63ce030973c79d5b05797813761a0d58e", "shasum": "" }, "require": { @@ -287,22 +337,22 @@ ], "support": { "issues": "https://github.com/influxdata/influxdb-client-php/issues", - "source": "https://github.com/influxdata/influxdb-client-php/tree/3.6.0" + "source": "https://github.com/influxdata/influxdb-client-php/tree/3.8.0" }, - "time": "2024-06-24T10:01:53+00:00" + "time": "2025-06-26T05:12:59+00:00" }, { "name": "paragonie/constant_time_encoding", - "version": "v2.7.0", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/paragonie/constant_time_encoding.git", - "reference": "52a0d99e69f56b9ec27ace92ba56897fe6993105" + "reference": "e30811f7bc69e4b5b6d5783e712c06c8eabf0226" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/52a0d99e69f56b9ec27ace92ba56897fe6993105", - "reference": "52a0d99e69f56b9ec27ace92ba56897fe6993105", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/e30811f7bc69e4b5b6d5783e712c06c8eabf0226", + "reference": "e30811f7bc69e4b5b6d5783e712c06c8eabf0226", "shasum": "" }, "require": { @@ -356,7 +406,7 @@ "issues": "https://github.com/paragonie/constant_time_encoding/issues", "source": "https://github.com/paragonie/constant_time_encoding" }, - "time": "2024-05-08T12:18:48+00:00" + "time": "2025-09-24T15:12:37+00:00" }, { "name": "php-http/client-common", @@ -738,21 +788,21 @@ }, { "name": "pragmarx/google2fa-qrcode", - "version": "v3.0.0", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/antonioribeiro/google2fa-qrcode.git", - "reference": "ce4d8a729b6c93741c607cfb2217acfffb5bf76b" + "reference": "c23ebcc3a50de0d1566016a6dd1486e183bb78e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/antonioribeiro/google2fa-qrcode/zipball/ce4d8a729b6c93741c607cfb2217acfffb5bf76b", - "reference": "ce4d8a729b6c93741c607cfb2217acfffb5bf76b", + "url": "https://api.github.com/repos/antonioribeiro/google2fa-qrcode/zipball/c23ebcc3a50de0d1566016a6dd1486e183bb78e1", + "reference": "c23ebcc3a50de0d1566016a6dd1486e183bb78e1", "shasum": "" }, "require": { "php": ">=7.1", - "pragmarx/google2fa": ">=4.0" + "pragmarx/google2fa": "^4.0|^5.0|^6.0|^7.0|^8.0" }, "require-dev": { "bacon/bacon-qr-code": "^2.0", @@ -799,9 +849,9 @@ ], "support": { "issues": "https://github.com/antonioribeiro/google2fa-qrcode/issues", - "source": "https://github.com/antonioribeiro/google2fa-qrcode/tree/v3.0.0" + "source": "https://github.com/antonioribeiro/google2fa-qrcode/tree/v3.0.1" }, - "time": "2021-08-15T12:53:48+00:00" + "time": "2025-09-19T23:02:26+00:00" }, { "name": "psr/cache", @@ -1487,7 +1537,7 @@ }, { "name": "symfony/polyfill-php73", - "version": "v1.31.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", @@ -1543,7 +1593,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-php73/tree/v1.33.0" }, "funding": [ { @@ -1554,6 +1604,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -1563,16 +1617,16 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.31.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", - "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", "shasum": "" }, "require": { @@ -1623,7 +1677,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" }, "funding": [ { @@ -1634,12 +1688,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-01-02T08:10:11+00:00" }, { "name": "symfony/service-contracts", @@ -1796,64 +1854,6 @@ } ], "time": "2024-09-25T14:11:13+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" } ], "packages-dev": [], @@ -1869,5 +1869,5 @@ "platform-overrides": { "php": "7.4" }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } From d40cf1270f7b72605573ecdaee34f8f3147e0ba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc?= <1536036+zoic21@users.noreply.github.com> Date: Wed, 26 Nov 2025 09:01:59 +0100 Subject: [PATCH 013/128] Fix changelog with 4.4.20 version inplace of 4.4.19 for emergency restore cli --- docs/fr_FR/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/fr_FR/changelog.md b/docs/fr_FR/changelog.md index 70127b1bdb..b627fe88e9 100644 --- a/docs/fr_FR/changelog.md +++ b/docs/fr_FR/changelog.md @@ -95,4 +95,4 @@ >**IMPORTANT** > -> La restauration d'un backup 4.4 peut dans certains cas finir par des erreurs dans l'interface web. Rien de grave cela peut facilement se corriger il suffit de faire : `cd /tmp;wget https://github.com/jeedom/core/archive/refs/tags/4.4.19.zip;unzip 4.4.19.zip;cd core-4.4.19;cp -rf * /var/www/html/;rm -rf /tmp/master.zip;rm -rf /tmp/core-4.4.19;`. Vous pouvez lancer cette commande depuis l'interface rescue de jeedom (ajouter `&rescue=1` dans l'url), ou directement en ssh. +> La restauration d'un backup 4.4 peut dans certains cas finir par des erreurs dans l'interface web. Rien de grave cela peut facilement se corriger il suffit de faire : `cd /tmp;wget https://github.com/jeedom/core/archive/refs/tags/4.4.20.zip;unzip 4.4.19.zip;cd core-4.4.20;cp -rf * /var/www/html/;rm -rf /tmp/master.zip;rm -rf /tmp/core-4.4.20;`. Vous pouvez lancer cette commande depuis l'interface rescue de jeedom (ajouter `&rescue=1` dans l'url), ou directement en ssh. From 1b54d4d33ca2085db4754a083081bbfe90c75086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc?= <1536036+zoic21@users.noreply.github.com> Date: Wed, 26 Nov 2025 10:31:46 +0100 Subject: [PATCH 014/128] Fix warning https://community.jeedom.com/t/jeedom-4-5-une-version-plus-moderne-et-plus-fluide/144863/64 --- core/class/system.class.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/class/system.class.php b/core/class/system.class.php index af5962d3d7..347876791f 100644 --- a/core/class/system.class.php +++ b/core/class/system.class.php @@ -793,7 +793,7 @@ public static function installPackageInProgress($_plugin = ''): bool { } return true; } - if (shell_exec('ls /tmp/jeedom_install_in_progress* | wc -l') > 0) { + if (shell_exec('ls /tmp/jeedom_install_in_progress* | wc -l 2> /dev/null') > 0) { return true; } return false; @@ -918,4 +918,4 @@ public static function checkInstallationLog($_plugin = ''): string { } return ''; } -} \ No newline at end of file +} From ce98afe0b85d5179cfcfd471ea73c5e8436e8308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc?= <1536036+zoic21@users.noreply.github.com> Date: Wed, 26 Nov 2025 14:13:08 +0100 Subject: [PATCH 015/128] Fix trigger name issue when it's a scenario --- core/class/scenario.class.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/core/class/scenario.class.php b/core/class/scenario.class.php index 2b96c67bec..96e81124c7 100644 --- a/core/class/scenario.class.php +++ b/core/class/scenario.class.php @@ -912,16 +912,20 @@ public function execute($instance_id = '') { return; } } - $cmd = cmd::byId(str_replace('#', '', $this->getTag('trigger_id'))); - if (is_object($cmd)) { - log::add('event', 'info', __('Exécution du scénario', __FILE__) . ' ' . $this->getHumanName() . ' ' . __('déclenché par :', __FILE__) . ' ' . $cmd->getHumanName()); + if($this->getTag('trigger') == 'scenario'){ + $obj_trigger = scenario::byId(str_replace('#', '', $this->getTag('trigger_id'))); + }else{ + $obj_trigger = cmd::byId(str_replace('#', '', $this->getTag('trigger_id'))); + } + if (is_object($obj_trigger)) { + log::add('event', 'info', __('Exécution du scénario', __FILE__) . ' ' . $this->getHumanName() . ' ' . __('déclenché par :', __FILE__) . ' ' . $obj_trigger->getHumanName()); if ($this->getConfiguration('timeline::enable')) { $timeline = new timeline(); $timeline->setType('scenario'); $timeline->setFolder($this->getConfiguration('timeline::folder')); $timeline->setLink_id($this->getId()); $timeline->setName($this->getHumanName(true, true, true, true)); - $timeline->setOptions(array('trigger' => $cmd->getHumanName(true))); + $timeline->setOptions(array('trigger' => $obj_trigger->getHumanName(true))); $timeline->save(); } } else { From 1fa2b6cb2021c7597f49ef870e7ceb028297746c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc?= <1536036+zoic21@users.noreply.github.com> Date: Thu, 27 Nov 2025 09:29:20 +0100 Subject: [PATCH 016/128] Fix 500 error on delete of some device https://community.jeedom.com/t/probleme-lors-de-la-suppression-dun-equipement/144928 --- core/ajax/eqLogic.ajax.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/ajax/eqLogic.ajax.php b/core/ajax/eqLogic.ajax.php index 99f30677e3..b9479ebc2e 100644 --- a/core/ajax/eqLogic.ajax.php +++ b/core/ajax/eqLogic.ajax.php @@ -425,7 +425,7 @@ } if (count($cmdData['node']) > 0) { foreach ($cmdData['node'] as $name => $data) { - if (cmd::byId(str_replace('cmd', '', $data['id']))->getEqLogic_id() == $eqLogic->getId()) { + if (is_object(cmd::byId(str_replace('cmd', '', $data['id']))) && cmd::byId(str_replace('cmd', '', $data['id']))->getEqLogic_id() == $eqLogic->getId()) { continue; } From b6b8d42458247b4e01359c8fa96378c3f5ec8f0e Mon Sep 17 00:00:00 2001 From: Franck_Jeedom <141225382+franck-jeedom@users.noreply.github.com> Date: Sat, 29 Nov 2025 05:25:32 +0100 Subject: [PATCH 017/128] Update history.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit L'exportation CSV n'est pas dans l'écran principal, elle est disponible dans l'écran Configuration des Commandes. --- docs/fr_FR/history.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/fr_FR/history.md b/docs/fr_FR/history.md index 7930e65b5b..6322cb45b1 100644 --- a/docs/fr_FR/history.md +++ b/docs/fr_FR/history.md @@ -93,10 +93,8 @@ Vous avez aussi accès à une gestion de formules de calcul qui vous permet de l #### Historique de commande -Devant chaque donnée pouvant être affichée, vous retrouvez deux icônes : - -- **Poubelle** : Permet de supprimer les données enregistrées ; lors du clic, Jeedom demande s’il faut supprimer les données avant une certaine date ou toutes les données. -- **Flèche** : Permet d’avoir un export CSV des données historisées. +- Devant chaque donnée pouvant être affichée, vous retrouvez une icône **Poubelle** qui permet de supprimer les données enregistrées ; lors du clic, Jeedom demande s’il faut supprimer les données avant une certaine date ou toutes les données. +- Dans **Configuration** vous trouvez à droite de chaque donnée, une icône **Flèche** qui permet d’avoir un export CSV des données historisées. ### Suppression de valeur incohérente From 7924aa852842ab9251645c04200430e4e222d0d4 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Tue, 2 Dec 2025 11:07:29 +0100 Subject: [PATCH 018/128] init 4.5.1 --- core/config/version | 2 +- docs/fr_FR/changelog.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/core/config/version b/core/config/version index 958d30d86d..4404a17bae 100644 --- a/core/config/version +++ b/core/config/version @@ -1 +1 @@ -4.5 \ No newline at end of file +4.5.1 diff --git a/docs/fr_FR/changelog.md b/docs/fr_FR/changelog.md index b627fe88e9..4eff13863a 100644 --- a/docs/fr_FR/changelog.md +++ b/docs/fr_FR/changelog.md @@ -1,5 +1,9 @@ # Changelog Jeedom V4.5 +# 4.5.1 + + + # 4.5 - [Développeurs] Ajout de la fonction `$listener->removeEvent($_id)` From 50088ee77497518d3e250c308023c52e666fede7 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Tue, 2 Dec 2025 11:17:41 +0100 Subject: [PATCH 019/128] bump nodejs to v22 --- docs/fr_FR/changelog.md | 2 +- resources/install_nodejs.sh | 38 ++++++++++++++++++------------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/fr_FR/changelog.md b/docs/fr_FR/changelog.md index 4eff13863a..a126e34f98 100644 --- a/docs/fr_FR/changelog.md +++ b/docs/fr_FR/changelog.md @@ -2,7 +2,7 @@ # 4.5.1 - +- Montée de version de nodejs 20 vers 22 ([Détails](https://github.com/jeedom/core/issues/3147)) # 4.5 diff --git a/resources/install_nodejs.sh b/resources/install_nodejs.sh index 695498469e..c83ccc1d9f 100644 --- a/resources/install_nodejs.sh +++ b/resources/install_nodejs.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash -installVer='20' #NodeJS major version to be installed -minVer='20' #min NodeJS major version to be accepted +installVer='22' #NodeJS major version to be installed +minVer='22' #min NodeJS major version to be accepted # vérifier si toujours nécessaire, cette source traine encore sur certaines smart et si une source est invalide -> nodejs ne s'installera pas if ls /etc/apt/sources.list.d/deb-multimedia.list* &>/dev/null; then @@ -53,8 +53,8 @@ lsb_release -c | grep jessie if [ $? -eq 0 ] then today=$(date +%Y%m%d) - if [[ "$today" > "20200630" ]]; - then + if [[ "$today" > "20200630" ]]; + then echo "== ATTENTION Debian 8 Jessie n'est officiellement plus supportée depuis le 30 juin 2020, merci de mettre à jour votre distribution !!!" exit 1 fi @@ -65,8 +65,8 @@ lsb_release -c | grep stretch if [ $? -eq 0 ] then today=$(date +%Y%m%d) - if [[ "$today" > "20220630" ]]; - then + if [[ "$today" > "20220630" ]]; + then echo "== ATTENTION Debian 9 Stretch n'est officiellement plus supportée depuis le 30 juin 2022, merci de mettre à jour votre distribution !!!" exit 1 fi @@ -77,8 +77,8 @@ lsb_release -c | grep buster if [ $? -eq 0 ] then today=$(date +%Y%m%d) - if [[ "$today" > "20220630" ]]; - then + if [[ "$today" > "20220630" ]]; + then echo "== ATTENTION Debian 10 Buster n'est officiellement plus supportée depuis le 30 juin 2024, merci de mettre à jour votre distribution !!!" exit 1 fi @@ -87,9 +87,9 @@ fi #x86 32 bits not supported by nodesource anymore bits=$(getconf LONG_BIT) if { [ "$arch" = "i386" ] || [ "$arch" = "i686" ]; } && [ "$bits" -eq "32" ] -then +then echo "== ATTENTION Votre système est x86 en 32bits et NodeJS 12 n'y est pas supporté, merci de passer en 64bits !!!" -exit 1 +exit 1 fi @@ -104,7 +104,7 @@ then else echo "[ KO ]"; echo "Installation de NodeJS $installVer" - + #if npm exists type npm &>/dev/null if [ $? -eq 0 ]; then @@ -112,10 +112,10 @@ else else npmPrefix="/usr" fi - + sudo DEBIAN_FRONTEND=noninteractive apt-get -y --purge autoremove npm &>/dev/null sudo DEBIAN_FRONTEND=noninteractive apt-get -y --purge autoremove nodejs &>/dev/null - + if [[ $arch == "armv6l" ]] then #version to install for armv6 (to check on https://unofficial-builds.nodejs.org) @@ -152,13 +152,13 @@ else sudo apt-get update sudo DEBIAN_FRONTEND=noninteractive apt-get install -y nodejs fi - + npm config set prefix ${npmPrefix} &>/dev/null - if [ $(which node | wc -l) -ne 0 ] && [ $(which nodejs | wc -l) -eq 0 ]; then + if [ $(which node | wc -l) -ne 0 ] && [ $(which nodejs | wc -l) -eq 0 ]; then ln -s $(which node) $(which node)js fi - + new=`nodejs -v`; echo -n "[Check Version NodeJS après install : ${new} : " testVerAfter=$(php -r "echo version_compare('${new}','v${minVer}','>=');") @@ -173,7 +173,7 @@ fi type npm &>/dev/null if [ $? -ne 0 ]; then # Installation de npm car non présent (par sécu) - sudo DEBIAN_FRONTEND=noninteractive apt-get install -y npm + sudo DEBIAN_FRONTEND=noninteractive apt-get install -y npm sudo npm install -g npm fi @@ -183,7 +183,7 @@ if [ $? -eq 0 ]; then npmPrefixSudo=`sudo npm prefix -g` npmPrefixwwwData=`sudo -u www-data npm prefix -g` echo -n "[Check Prefix : $npmPrefix and sudo prefix : $npmPrefixSudo and www-data prefix : $npmPrefixwwwData : " - if [[ "$npmPrefixSudo" != "/usr" ]] && [[ "$npmPrefixSudo" != "/usr/local" ]]; then + if [[ "$npmPrefixSudo" != "/usr" ]] && [[ "$npmPrefixSudo" != "/usr/local" ]]; then echo "[ KO ]" if [[ "$npmPrefixwwwData" == "/usr" ]] || [[ "$npmPrefixwwwData" == "/usr/local" ]]; then echo "Reset prefix ($npmPrefixwwwData) pour npm `sudo whoami`" @@ -202,7 +202,7 @@ if [ $? -eq 0 ]; then sudo npm config set prefix /usr/local fi fi - fi + fi else if [[ "$npmPrefixwwwData" == "/usr" ]] || [[ "$npmPrefixwwwData" == "/usr/local" ]]; then if [[ "$npmPrefixwwwData" == "$npmPrefixSudo" ]]; then From b790b117796fff64f4181a6b83432d3b708f1693 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Tue, 2 Dec 2025 11:40:42 +0100 Subject: [PATCH 020/128] typo --- docs/fr_FR/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/fr_FR/changelog.md b/docs/fr_FR/changelog.md index a126e34f98..bea6d93bff 100644 --- a/docs/fr_FR/changelog.md +++ b/docs/fr_FR/changelog.md @@ -14,7 +14,7 @@ - Les graphiques se mettent à jour automatiquement lors de l'arrivée de nouvelles valeurs [LIEN](https://github.com/jeedom/core/issues/2749) - Jeedom ajoute automatiquement la hauteur de l'image lors de la création des widgets pour éviter les soucis de chevauchement en mobile [LIEN](https://github.com/jeedom/core/issues/2539) - Refonte de la partie backup cloud [LIEN](https://github.com/jeedom/core/issues/2765) -- **DEV** Mise en place d'un système de queue pour l'exécution d'actions [LIEN](https://github.com/jeedom/core/issues/2489) +- [Développeurs] Mise en place d'un système de queue pour l'exécution d'actions [LIEN](https://github.com/jeedom/core/issues/2489) - Les tags des scénarios sont maintenant propres à l'instance du scénario (si vous avez deux lancements de scénarios très proches, les tags du dernier n'écrasent plus le premier) [LIEN](https://github.com/jeedom/core/issues/2763) - Changement sur la partie trigger des scénarios : [LIEN](https://github.com/jeedom/core/issues/2414) - ``triggerId()`` est maintenant deprecated et sera retiré dans les futures mises à jour du core. Si vous avez ``triggerId() == 587`` il faut le remplacer par ``#trigger_id# == 587`` From 08a2f4d6888066e3ea6abb42acb4d28dbb935675 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Tue, 2 Dec 2025 15:17:17 +0100 Subject: [PATCH 021/128] typo trigger_value --- docs/fr_FR/scenario.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/fr_FR/scenario.md b/docs/fr_FR/scenario.md index e002eafb7c..13d3c0d1e0 100644 --- a/docs/fr_FR/scenario.md +++ b/docs/fr_FR/scenario.md @@ -439,7 +439,7 @@ Une boîte à outils de fonctions génériques peut également servir à effectu - ``randText(texte1;texte2;texte…​..)`` : Permet de retourner un des textes aléatoirement (séparer les texte par un ; ). Il n’y a pas de limite dans le nombre de texte. - ``randomColor(min,max)`` : Donne une couleur aléatoire comprise entre 2 bornes ( 0 => rouge, 50 => vert, 100 => bleu). - ``trigger(commande)`` : Permet de connaître le déclencheur du scénario ou de savoir si c’est bien la commande passée en paramètre qui a déclenché le scénario. **=> Deprecated il vaut mieux utiliser le tag #trigger#** -- ``triggerValue()`` : Permet de connaître la valeur du déclencheur du scénario. **=> Deprecated il vaut mieux utiliser le tag #triggerValue#** +- ``triggerValue()`` : Permet de connaître la valeur du déclencheur du scénario. **=> Deprecated il vaut mieux utiliser le tag #trigger_value#** - ``round(valeur,[decimal])`` : Donne un arrondi au-dessus, [decimal] nombre de décimales après la virgule. - ``odd(valeur)`` : Permet de savoir si un nombre est impair ou non. Renvoie 1 si impair 0 sinon. - ``median(commande1,commande2…​.commandeN)`` : Renvoie la médiane des valeurs. From ea2acccb97b4c4051559fb5d42f4012e3fa28768 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Sat, 6 Dec 2025 13:59:39 +0100 Subject: [PATCH 022/128] typo changelog --- docs/fr_FR/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/fr_FR/changelog.md b/docs/fr_FR/changelog.md index bea6d93bff..4c4c0cfbc5 100644 --- a/docs/fr_FR/changelog.md +++ b/docs/fr_FR/changelog.md @@ -99,4 +99,4 @@ >**IMPORTANT** > -> La restauration d'un backup 4.4 peut dans certains cas finir par des erreurs dans l'interface web. Rien de grave cela peut facilement se corriger il suffit de faire : `cd /tmp;wget https://github.com/jeedom/core/archive/refs/tags/4.4.20.zip;unzip 4.4.19.zip;cd core-4.4.20;cp -rf * /var/www/html/;rm -rf /tmp/master.zip;rm -rf /tmp/core-4.4.20;`. Vous pouvez lancer cette commande depuis l'interface rescue de jeedom (ajouter `&rescue=1` dans l'url), ou directement en ssh. +> La restauration d'un backup 4.4 peut dans certains cas finir par des erreurs dans l'interface web. Rien de grave cela peut facilement se corriger il suffit de faire : `cd /tmp;wget https://github.com/jeedom/core/archive/refs/tags/4.4.20.zip;unzip 4.4.20.zip;cd core-4.4.20;cp -rf * /var/www/html/;rm -rf /tmp/master.zip;rm -rf /tmp/core-4.4.20;`. Vous pouvez lancer cette commande depuis l'interface rescue de jeedom (ajouter `&rescue=1` dans l'url), ou directement en ssh. From 1a7fa51b591708fe4ecddbb10e52bc3895347709 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Thu, 11 Dec 2025 09:54:28 +0100 Subject: [PATCH 023/128] fix cd485a0 --- core/class/cmd.class.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/class/cmd.class.php b/core/class/cmd.class.php index 655509c8fb..c667a6fc09 100644 --- a/core/class/cmd.class.php +++ b/core/class/cmd.class.php @@ -1042,10 +1042,10 @@ public function formatValue($_value, $_quote = false) { return intval($binary xor boolval($this->getConfiguration('invertBinary', false))); case 'numeric': if ($this->getConfiguration('historizeRound') !== '' && is_numeric($this->getConfiguration('historizeRound')) && $this->getConfiguration('historizeRound') >= 0) { - if (!is_numeric($_value)) { - log::add('cmd', 'error', __('La formule de calcul doit retourner une valeur numérique uniquement : ', __FILE__) . $this->getHumanName() . ' => ' . $_value); - $_value = (float) (str_replace(',', '.', $_value)); - } + if (!is_numeric($_value)) { + log::add('cmd', 'error', __('La formule de calcul doit retourner une valeur numérique uniquement : ', __FILE__) . $this->getHumanName() . ' => ' . $_value); + $_value = (float) (str_replace(',', '.', $_value)); + } $_value = round($_value, $this->getConfiguration('historizeRound')); } if ($_value > $this->getConfiguration('maxValue', $_value)) { @@ -3070,8 +3070,8 @@ public function getDisplay($_key = '', $_default = '') { return utils::getJsonAttr($this->display, $_key, $_default); } - public function setDisplay($_key, $_value = null) { - if ($this->getDisplay($_key,null) !== $_value) { + public function setDisplay($_key, $_value) { + if ($this->getDisplay($_key, null) !== $_value) { $this->_needRefreshWidget = true; $this->_changed = true; } From 7815619a7a524dc2d766c13c466da430660fcf7b Mon Sep 17 00:00:00 2001 From: Salvialf Date: Sat, 13 Dec 2025 19:40:23 +0100 Subject: [PATCH 024/128] code format --- core/class/scenarioExpression.class.php | 76 ++++++++++++------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/core/class/scenarioExpression.class.php b/core/class/scenarioExpression.class.php index 952352df20..d58875ca5a 100644 --- a/core/class/scenarioExpression.class.php +++ b/core/class/scenarioExpression.class.php @@ -194,17 +194,17 @@ public static function getDatesFromPeriod($_period = '1 hour') { if ($_period == 'day') $_period = '1 day'; if (ctype_digit($_period[0]) && !stristr($_period, "ago")) { - $_startTime = date('Y-m-d H:i:s',(int) strtotime('-' . $_period)); + $_startTime = date('Y-m-d H:i:s', (int) strtotime('-' . $_period)); } else { - $_startTime = date('Y-m-d H:i:s',(int) strtotime($_period)); + $_startTime = date('Y-m-d H:i:s', (int) strtotime($_period)); } $_endTime = date('Y-m-d H:i:s'); if ($_period == 'today') { $_startTime = date('Y-m-d') . ' 00:00:00'; } elseif ($_period == 'yesterday') { - $_startTime = date('Y-m-d',(int) strtotime('-1 day')) . ' 00:00:00'; - $_endTime = date('Y-m-d',(int) strtotime('-1 day')) . ' 23:59:59'; + $_startTime = date('Y-m-d', (int) strtotime('-1 day')) . ' 00:00:00'; + $_endTime = date('Y-m-d', (int) strtotime('-1 day')) . ' 23:59:59'; } return array($_startTime, $_endTime); } @@ -303,8 +303,8 @@ public static function averageBetween($_cmd_id, $_startDate, $_endDate, $_round if (!is_object($cmd) || $cmd->getIsHistorized() == 0) { return ''; } - $_startTime = date('Y-m-d H:i:s',(int) strtotime(self::setTags($_startDate))); - $_endTime = date('Y-m-d H:i:s',(int) strtotime(self::setTags($_endDate))); + $_startTime = date('Y-m-d H:i:s', (int) strtotime(self::setTags($_startDate))); + $_endTime = date('Y-m-d H:i:s', (int) strtotime(self::setTags($_endDate))); $historyStatistique = $cmd->getStatistique($_startTime, $_endTime); if (!isset($historyStatistique['avg'])) { return ''; @@ -328,8 +328,8 @@ public static function averageTemporalBetween($_cmd_id, $_startDate, $_endDate, if (!is_object($cmd) || $cmd->getIsHistorized() == 0) { return ''; } - $_startTime = date('Y-m-d H:i:s',(int) strtotime(self::setTags($_startDate))); - $_endTime = date('Y-m-d H:i:s',(int) strtotime(self::setTags($_endDate))); + $_startTime = date('Y-m-d H:i:s', (int) strtotime(self::setTags($_startDate))); + $_endTime = date('Y-m-d H:i:s', (int) strtotime(self::setTags($_endDate))); return round($cmd->getTemporalAvg($_startTime, $_endTime), $_round); } @@ -458,8 +458,8 @@ public static function maxBetween($_cmd_id, $_startDate, $_endDate, $_round = 1) if (!is_object($cmd) || $cmd->getIsHistorized() == 0) { return ''; } - $_startTime = date('Y-m-d H:i:s',(int) strtotime(self::setTags($_startDate))); - $_endTime = date('Y-m-d H:i:s',(int) strtotime(self::setTags($_endDate))); + $_startTime = date('Y-m-d H:i:s', (int) strtotime(self::setTags($_startDate))); + $_endTime = date('Y-m-d H:i:s', (int) strtotime(self::setTags($_endDate))); $historyStatistique = $cmd->getStatistique($_startTime, $_endTime); if (!isset($historyStatistique['max'])) { return ''; @@ -489,8 +489,8 @@ public static function minBetween($_cmd_id, $_startDate, $_endDate, $_round = 1) if (!is_object($cmd) || $cmd->getIsHistorized() == 0) { return ''; } - $_startTime = date('Y-m-d H:i:s',(int) strtotime(self::setTags($_startDate))); - $_endTime = date('Y-m-d H:i:s',(int) strtotime(self::setTags($_endDate))); + $_startTime = date('Y-m-d H:i:s', (int) strtotime(self::setTags($_startDate))); + $_endTime = date('Y-m-d H:i:s', (int) strtotime(self::setTags($_endDate))); $historyStatistique = $cmd->getStatistique($_startTime, $_endTime); if (!isset($historyStatistique['min'])) { return ''; @@ -642,8 +642,8 @@ public static function stateChangesBetween($_cmd_id, $_value, $_startDate, $_end $_endDate = func_get_arg(2); $_value = null; } - $_startTime = date('Y-m-d H:i:s',(int) strtotime(self::setTags($_startDate))); - $_endTime = date('Y-m-d H:i:s',(int) strtotime(self::setTags($_endDate))); + $_startTime = date('Y-m-d H:i:s', (int) strtotime(self::setTags($_startDate))); + $_endTime = date('Y-m-d H:i:s', (int) strtotime(self::setTags($_endDate))); return history::stateChanges($cmd_id, $_value, $_startTime, $_endTime); } @@ -724,8 +724,8 @@ public static function durationBetween($_cmd_id, $_value, $_startDate, $_endDate $_endDate = date('Y-m-d H:i:s'); } - $_startTime = date('Y-m-d H:i:s',(int) strtotime(self::setTags($_startDate))); - $_endTime = date('Y-m-d H:i:s',(int) strtotime(self::setTags($_endDate))); + $_startTime = date('Y-m-d H:i:s', (int) strtotime(self::setTags($_startDate))); + $_endTime = date('Y-m-d H:i:s', (int) strtotime(self::setTags($_endDate))); $_value = str_replace(',', '.', $_value); $_decimal = strlen(substr(strrchr($_value, "."), 1)); @@ -769,8 +769,8 @@ public static function lastBetween($_cmd_id, $_startDate, $_endDate) { if (!is_object($cmd) || $cmd->getIsHistorized() == 0) { return ''; } - $_startTime = date('Y-m-d H:i:s',(int) strtotime(self::setTags($_startDate))); - $_endTime = date('Y-m-d H:i:s',(int) strtotime(self::setTags($_endDate))); + $_startTime = date('Y-m-d H:i:s', (int) strtotime(self::setTags($_startDate))); + $_endTime = date('Y-m-d H:i:s', (int) strtotime(self::setTags($_endDate))); $historyStatistique = $cmd->getStatistique($_startTime, $_endTime); if (!isset($historyStatistique['last']) || $historyStatistique['last'] === '') { return ''; @@ -802,8 +802,8 @@ public static function statisticsBetween($_cmd_id, $_calc, $_startDate, $_endDat return ''; } $_calc = str_replace(' ', '', $_calc); - $_startTime = date('Y-m-d H:i:s',(int) strtotime(self::setTags($_startDate))); - $_endTime = date('Y-m-d H:i:s',(int) strtotime(self::setTags($_endDate))); + $_startTime = date('Y-m-d H:i:s', (int) strtotime(self::setTags($_startDate))); + $_endTime = date('Y-m-d H:i:s', (int) strtotime(self::setTags($_endDate))); $historyStatistique = $cmd->getStatistique(self::setTags($_startTime), self::setTags($_endTime)); return $historyStatistique[$_calc]; } @@ -960,7 +960,7 @@ public static function trigger($_name = '', &$_scenario = null) { if (trim($_name) == '') { return $_scenario->getTag('trigger_name'); } - if (trim(jeedom::toHumanReadable($_name),'#') == $_scenario->getTag('trigger_name')) { + if (trim(jeedom::toHumanReadable($_name), '#') == $_scenario->getTag('trigger_name')) { return 1; } return 0; @@ -1038,8 +1038,8 @@ public static function time_diff($_date1, $_date2, $_format = 'd', $_rnd = 2) { if ($_date2 < 100) $_date2 = '00' . $_date2; if ($_date2 < 1000) $_date2 = '0' . $_date2; } - $d1 = str_replace(array('"','\'',"'"), '', self::setTags($_date1)); - $d2 = str_replace(array('"','\'',"'"), '', self::setTags($_date2)); + $d1 = str_replace(array('"', '\'', "'"), '', self::setTags($_date1)); + $d2 = str_replace(array('"', '\'', "'"), '', self::setTags($_date2)); $date1 = new DateTime($d1); $date2 = new DateTime($d2); $duree = $date2->getTimestamp() - $date1->getTimestamp(); @@ -1386,7 +1386,7 @@ public function execute(&$scenario = null) { } $this->checkBackground(); if ($this->getOptions('background', 0) == 1) { - $key = 'scenarioElement'.$this->getId().'::' . config::genKey(16).'::'.strtotime('now'); + $key = 'scenarioElement' . $this->getId() . '::' . config::genKey(16) . '::' . strtotime('now'); cache::set($key, array('scenarioExpression' => $this, 'scenario' => $scenario), 60); $cmd = __DIR__ . '/../php/jeeScenarioExpression.php'; $cmd .= ' key=' . $key; @@ -1571,14 +1571,14 @@ public function execute(&$scenario = null) { } $this->setLog($scenario, $GLOBALS['JEEDOM_SCLOG_TEXT']['launchScenario']['txt'] . $actionScenario->getName() . ' ' . __('options :', __FILE__) . ' ' . json_encode($actionScenario->getTags())); if ($scenario !== null) { - $actionScenario->addTag('trigger','scenario'); - $actionScenario->addTag('trigger_message',$GLOBALS['JEEDOM_SCLOG_TEXT']['startByScenario']['txt'] . $scenario->getHumanName()); - $actionScenario->addTag('trigger_name',trim($scenario->getHumanName(),'#')); - $actionScenario->addTag('trigger_id',$scenario->getId()); + $actionScenario->addTag('trigger', 'scenario'); + $actionScenario->addTag('trigger_message', $GLOBALS['JEEDOM_SCLOG_TEXT']['startByScenario']['txt'] . $scenario->getHumanName()); + $actionScenario->addTag('trigger_name', trim($scenario->getHumanName(), '#')); + $actionScenario->addTag('trigger_id', $scenario->getId()); return $actionScenario->launch(); } else { - $actionScenario->addTag('trigger','other'); - $actionScenario->addTag('trigger_message',$GLOBALS['JEEDOM_SCLOG_TEXT']['startCausedBy']['txt']); + $actionScenario->addTag('trigger', 'other'); + $actionScenario->addTag('trigger_message', $GLOBALS['JEEDOM_SCLOG_TEXT']['startCausedBy']['txt']); return $actionScenario->launch(); } break; @@ -1597,14 +1597,14 @@ public function execute(&$scenario = null) { } $this->setLog($scenario, $GLOBALS['JEEDOM_SCLOG_TEXT']['launchScenario']['txt'] . $actionScenario->getName() . ' ' . __('options :', __FILE__) . ' ' . json_encode($actionScenario->getTags())); if ($scenario !== null) { - $actionScenario->addTag('trigger','scenario'); - $actionScenario->addTag('trigger_message',$GLOBALS['JEEDOM_SCLOG_TEXT']['startByScenario']['txt'] . $scenario->getHumanName()); - $actionScenario->addTag('trigger_name',trim($scenario->getHumanName(),'#')); - $actionScenario->addTag('trigger_id',$scenario->getId()); + $actionScenario->addTag('trigger', 'scenario'); + $actionScenario->addTag('trigger_message', $GLOBALS['JEEDOM_SCLOG_TEXT']['startByScenario']['txt'] . $scenario->getHumanName()); + $actionScenario->addTag('trigger_name', trim($scenario->getHumanName(), '#')); + $actionScenario->addTag('trigger_id', $scenario->getId()); return $actionScenario->launch(true); } else { - $actionScenario->addTag('trigger','other'); - $actionScenario->addTag('trigger_message',$GLOBALS['JEEDOM_SCLOG_TEXT']['startCausedBy']['txt']); + $actionScenario->addTag('trigger', 'other'); + $actionScenario->addTag('trigger_message', $GLOBALS['JEEDOM_SCLOG_TEXT']['startCausedBy']['txt']); return $actionScenario->launch(true); } break; @@ -1791,8 +1791,8 @@ public function execute(&$scenario = null) { $tmp_file = jeedom::getTmpFolder('history_export') . '/' . $options['name'] . '.csv'; $cmd_parameters = array('files' => [$tmp_file], 'title' => $options['name'], 'message' => $options['name']); - $start = date('Y-m-d H:i:s',(int) strtotime($options['start'])); - $end = date('Y-m-d H:i:s',(int) strtotime($options['end'])); + $start = date('Y-m-d H:i:s', (int) strtotime($options['start'])); + $end = date('Y-m-d H:i:s', (int) strtotime($options['end'])); $this->setLog($scenario, __('Export de l\'historique du', __FILE__) . ' ' . $start . ' ' . __('au', __FILE__) . ' ' . $end); $histories = array(); From c2e061ef7373a09e0d03e2c63b876cd70278c99b Mon Sep 17 00:00:00 2001 From: Salvialf Date: Sat, 13 Dec 2025 19:40:42 +0100 Subject: [PATCH 025/128] $value must be a string --- core/class/scenarioExpression.class.php | 1 + 1 file changed, 1 insertion(+) diff --git a/core/class/scenarioExpression.class.php b/core/class/scenarioExpression.class.php index d58875ca5a..7fa2e9e653 100644 --- a/core/class/scenarioExpression.class.php +++ b/core/class/scenarioExpression.class.php @@ -1275,6 +1275,7 @@ public static function setTags(&$_expression, &$_scenario = null, $_quote = fals } if ($_quote) { foreach ($replace1 as &$value) { + $value = (string)$value; if (strpos($value, ' ') !== false || preg_match("/[a-zA-Z]/", $value) || $value === '') { $value = '"' . trim($value, '"') . '"'; } From e416ee5726f8c326f5780a14b4894c0e4c89bfdb Mon Sep 17 00:00:00 2001 From: Salvialf Date: Sat, 13 Dec 2025 20:38:30 +0100 Subject: [PATCH 026/128] improve color picker --- core/template/dashboard/cmd.action.color.picker.html | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/template/dashboard/cmd.action.color.picker.html b/core/template/dashboard/cmd.action.color.picker.html index 3c4bf73cca..62d1f17f5a 100644 --- a/core/template/dashboard/cmd.action.color.picker.html +++ b/core/template/dashboard/cmd.action.color.picker.html @@ -19,7 +19,13 @@ if ('#time#' == 'duration' || '#time#' == 'date') { jeedom.cmd.displayDuration(_options.valueDate, cmd.querySelector('.timeCmd'), '#time#') } - cmd.querySelector('.action_colorpicker_change').value = (_options.display_value != '') ? _options.display_value.substr(0, 7) : '#000000' + if (_options.display_value.length == 0) { + cmd.querySelector('.action_colorpicker_change').value = '#000000' + } else if (_options.display_value.startsWith('#')) { + cmd.querySelector('.action_colorpicker_change').value = _options.display_value.substr(0, 7) + } else { + cmd.querySelector('.action_colorpicker_change').value = '#' + _options.display_value.substr(0, 6) + } } }) From a58a5842736dbf00fbe4f06dee67f72eaa434377 Mon Sep 17 00:00:00 2001 From: TonioBDS <49921859+TonioBDS@users.noreply.github.com> Date: Mon, 15 Dec 2025 09:38:35 +0100 Subject: [PATCH 027/128] =?UTF-8?q?Mise=20=C3=A0=20jour=20du=20changelog?= =?UTF-8?q?=20avec=20d=C3=A9pr=C3=A9ciations=20et=20clarifications?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ajout d'informations sur la dépréciation de la fonction `trigger()` et mise à jour de la description de `#trigger_name#` dans le changelog. --- docs/fr_FR/changelog.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/fr_FR/changelog.md b/docs/fr_FR/changelog.md index 70127b1bdb..3fd6192f81 100644 --- a/docs/fr_FR/changelog.md +++ b/docs/fr_FR/changelog.md @@ -15,6 +15,7 @@ - Changement sur la partie trigger des scénarios : [LIEN](https://github.com/jeedom/core/issues/2414) - ``triggerId()`` est maintenant deprecated et sera retiré dans les futures mises à jour du core. Si vous avez ``triggerId() == 587`` il faut le remplacer par ``#trigger_id# == 587`` - ``triggerValue()`` est maintenant deprecated et sera retiré dans les futures mises à jour du core. Si vous avez ``triggerValue() == 10`` il faut le remplacer par ``#trigger_value# == 10`` + - ``trigger()`` est maintenant deprecated et sera retiré dans les futures mises à jour du core. Si vous avez ``trigger(#[objet][equipement][commande]#)`` il faut le remplacer par ``#trigger_name# == '[objet][equipement][commande]'`` - ``#trigger#`` : Peut être : - ``api`` si le lancement a été déclenché par l'API, - ``TYPEcmd`` si le lancement a été déclenché par une commande, avec TYPE remplacé par l'id du plugin (ex virtualCmd), @@ -22,7 +23,7 @@ - ``user`` s'il a été lancé manuellement, - ``start`` pour un lancement au démarrage de Jeedom. - ``#trigger_id#`` : Si c'est une commande qui a déclenché le scénario alors ce tag prend la valeur de l'id de la commande qui l'a déclenché - - ``#trigger_name#`` : Si c'est une commande qui a déclenché le scénario alors ce tag prend la valeur du nom de la commande (sous forme [objet][équipement][commande]) + - ``#trigger_name#`` : Si c'est une commande qui a déclenché le scénario alors ce tag prend la valeur du nom de la commande (sous forme '[objet][équipement][commande]'). Notez qu'en utilisant la syntaxe : ``#trigger_name# == '[objet][equipement][commande]'``, en cas de modification de nom de votre objet ou équipement ou commande, ce ne sera pas mis à jour automatiquement dans votre code. - ``#trigger_value#`` : Si c'est une commande qui a déclenché le scénario alors ce tag prend la valeur de la commande ayant déclenché le scénario - ``#trigger_message#`` : Message indiquant l'origine du lancement du scénario - Amélioration de la gestion des plugins sur github (plus de dépendances à une librairie tierce) [LIEN](https://github.com/jeedom/core/issues/2567) From 252265ca0bda130f3ee151f941a34fbc49e43883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc?= <1536036+zoic21@users.noreply.github.com> Date: Mon, 15 Dec 2025 13:55:46 +0100 Subject: [PATCH 028/128] Try to ignore case and accent for function fromHumanReadable --- core/class/cmd.class.php | 10 +++++----- core/class/scenario.class.php | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/core/class/cmd.class.php b/core/class/cmd.class.php index 7c0ffb79b5..d8178ded1b 100644 --- a/core/class/cmd.class.php +++ b/core/class/cmd.class.php @@ -538,8 +538,8 @@ public static function byObjectNameEqLogicNameCmdName($_object_name, $_eqLogic_n $sql = 'SELECT ' . DB::buildField(__CLASS__, 'c') . ' FROM cmd c INNER JOIN eqLogic el ON c.eqLogic_id=el.id - WHERE c.name=:cmd_name - AND el.name=:eqLogic_name + WHERE c.name COLLATE utf8mb4_0900_ai_ci =:cmd_name + AND el.name COLLATE utf8mb4_0900_ai_ci =:eqLogic_name AND el.object_id IS NULL'; } else { $values['object_name'] = $_object_name; @@ -547,9 +547,9 @@ public static function byObjectNameEqLogicNameCmdName($_object_name, $_eqLogic_n FROM cmd c INNER JOIN eqLogic el ON c.eqLogic_id=el.id INNER JOIN object ob ON el.object_id=ob.id - WHERE c.name=:cmd_name - AND el.name=:eqLogic_name - AND ob.name=:object_name'; + WHERE c.name COLLATE utf8mb4_0900_ai_ci =:cmd_name + AND el.name COLLATE utf8mb4_0900_ai_ci =:eqLogic_name + AND ob.name COLLATE utf8mb4_0900_ai_ci = :object_name'; } return self::cast(DB::Prepare($sql, $values, DB::FETCH_TYPE_ROW, PDO::FETCH_CLASS, __CLASS__)); } diff --git a/core/class/scenario.class.php b/core/class/scenario.class.php index 96e81124c7..057e41c8a4 100644 --- a/core/class/scenario.class.php +++ b/core/class/scenario.class.php @@ -606,14 +606,14 @@ public static function byObjectNameGroupNameScenarioName($_object_name, $_group_ if ($_group_name == __('Aucun', __FILE__)) { $sql = 'SELECT ' . DB::buildField(__CLASS__, 's') . ' FROM scenario s - WHERE s.name=:scenario_name + WHERE s.name COLLATE utf8mb4_0900_ai_ci =:scenario_name AND (`group` IS NULL OR `group`="" OR `group`="Aucun" OR `group`="None") AND s.object_id IS NULL'; } else { $values['group_name'] = $_group_name; $sql = 'SELECT ' . DB::buildField(__CLASS__, 's') . ' FROM scenario s - WHERE s.name=:scenario_name + WHERE s.name COLLATE utf8mb4_0900_ai_ci =:scenario_name AND s.object_id IS NULL AND `group`=:group_name'; } @@ -623,17 +623,17 @@ public static function byObjectNameGroupNameScenarioName($_object_name, $_group_ $sql = 'SELECT ' . DB::buildField(__CLASS__, 's') . ' FROM scenario s INNER JOIN object ob ON s.object_id=ob.id - WHERE s.name=:scenario_name - AND ob.name=:object_name + WHERE s.name COLLATE utf8mb4_0900_ai_ci =:scenario_name + AND ob.name COLLATE utf8mb4_0900_ai_ci =:object_name AND (`group` IS NULL OR `group`="" OR `group`="Aucun" OR `group`="None")'; } else { $values['group_name'] = $_group_name; $sql = 'SELECT ' . DB::buildField(__CLASS__, 's') . ' FROM scenario s INNER JOIN object ob ON s.object_id=ob.id - WHERE s.name=:scenario_name - AND ob.name=:object_name - AND `group`=:group_name'; + WHERE s.name COLLATE utf8mb4_0900_ai_ci =:scenario_name + AND ob.name COLLATE utf8mb4_0900_ai_ci =:object_name + AND `group` COLLATE utf8mb4_0900_ai_ci =:group_name'; } } return DB::Prepare($sql, $values, DB::FETCH_TYPE_ROW, PDO::FETCH_CLASS, __CLASS__); From 476da3ce34c008ac726cc2410b0472c01404faf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc?= <1536036+zoic21@users.noreply.github.com> Date: Tue, 16 Dec 2025 09:13:45 +0100 Subject: [PATCH 029/128] Fix random backup hour --- install/consistency.php | 625 ++++++++++++++++++++-------------------- 1 file changed, 312 insertions(+), 313 deletions(-) diff --git a/install/consistency.php b/install/consistency.php index 3d3907538a..f432e2964d 100644 --- a/install/consistency.php +++ b/install/consistency.php @@ -128,333 +128,332 @@ if (is_object($cron)) { $cron->remove(); } - if (method_exists('utils', 'attrChanged')) { - $cron = cron::byClassAndFunction('plugin', 'cronDaily'); - if (!is_object($cron)) { - echo "Create plugin::cronDaily\n"; - $cron = new cron(); - } - $cron->setClass('plugin'); - $cron->setFunction('cronDaily'); - $cron->setSchedule('00 00 * * *'); - $cron->setTimeout(240); - $cron->setEnable(1); - $cron->setDeamon(0); - $cron->save(); - - $cron = cron::byClassAndFunction('jeedom', 'backup'); - if (!is_object($cron)) { - echo "Create jeedom::backup\n"; - $cron = new cron(); - $cron->setClass('jeedom'); - $cron->setFunction('backup'); - $cron->setSchedule(rand(10, 59) . ' 0' . rand(0, 7) . ' * * *'); - $cron->setEnable(1); - $cron->setDeamon(0); - $cron->setTimeout(60); - $cron->save(); - } - - $cron = cron::byClassAndFunction('plugin', 'cronHourly'); - if (!is_object($cron)) { - echo "Create plugin::cronHourly\n"; - $cron = new cron(); - } - $cron->setClass('plugin'); - $cron->setFunction('cronHourly'); - $cron->setSchedule('00 * * * *'); - $cron->setEnable(1); - $cron->setDeamon(0); - $cron->setTimeout(60); - $cron->save(); - - $cron = cron::byClassAndFunction('scenario', 'check'); - if (!is_object($cron)) { - echo "Create scenario::check\n"; - $cron = new cron(); - } - $cron->setClass('scenario'); - $cron->setFunction('check'); - $cron->setSchedule('* * * * *'); - $cron->setEnable(1); - $cron->setDeamon(0); - $cron->setTimeout(30); - $cron->save(); - - $cron = cron::byClassAndFunction('scenario', 'control'); - if (!is_object($cron)) { - echo "Create scenario::control\n"; - $cron = new cron(); - } - $cron->setClass('scenario'); - $cron->setFunction('control'); - $cron->setSchedule('* * * * *'); - $cron->setEnable(1); - $cron->setDeamon(0); - $cron->setTimeout(30); - $cron->save(); - - $cron = cron::byClassAndFunction('jeedom', 'cronDaily'); - if (!is_object($cron)) { - echo "Create jeedom::cronDaily\n"; - $cron = new cron(); - } - $cron->setClass('jeedom'); - $cron->setFunction('cronDaily'); - $cron->setSchedule(rand(0, 59) . ' ' . rand(0, 3) . ' * * *'); - $cron->setEnable(1); - $cron->setDeamon(0); - $cron->setTimeout(240); - $cron->save(); - - $cron = cron::byClassAndFunction('jeedom', 'cronHourly'); - if (!is_object($cron)) { - echo "Create jeedom::cronHourly\n"; - $cron = new cron(); - } - $cron->setClass('jeedom'); - $cron->setFunction('cronHourly'); - $cron->setSchedule(rand(0, 59) . ' * * * *'); - $cron->setEnable(1); - $cron->setDeamon(0); - $cron->setTimeout(60); - $cron->save(); - - $cron = cron::byClassAndFunction('jeedom', 'cron5'); - if (!is_object($cron)) { - echo "Create jeedom::cron5\n"; - $cron = new cron(); - } - $cron->setClass('jeedom'); - $cron->setFunction('cron5'); - $cron->setSchedule('*/5 * * * *'); - $cron->setEnable(1); - $cron->setDeamon(0); - $cron->setTimeout(5); - $cron->save(); - - $cron = cron::byClassAndFunction('jeedom', 'cron10'); - if (!is_object($cron)) { - echo "Create jeedom::cron10\n"; - $cron = new cron(); - } - $cron->setClass('jeedom'); - $cron->setFunction('cron10'); - $cron->setSchedule('*/10 * * * *'); - $cron->setEnable(1); - $cron->setDeamon(0); - $cron->setTimeout(10); - $cron->save(); - - $cron = cron::byClassAndFunction('jeedom', 'cron'); - if (!is_object($cron)) { - echo "Create jeedom::cron\n"; - $cron = new cron(); - } - $cron->setClass('jeedom'); - $cron->setFunction('cron'); - $cron->setSchedule('* * * * *'); - $cron->setTimeout(2); - $cron->setDeamon(0); - $cron->save(); - - $cron = cron::byClassAndFunction('plugin', 'cron'); - if (!is_object($cron)) { - echo "Create plugin::cron\n"; - $cron = new cron(); - } - $cron->setClass('plugin'); - $cron->setFunction('cron'); - $cron->setSchedule('* * * * *'); - $cron->setTimeout(2); - $cron->setDeamon(0); - $cron->save(); - - $cron = cron::byClassAndFunction('queue', 'cron'); - if (!is_object($cron)) { - echo "Create queue::cron\n"; - $cron = new cron(); - } - $cron->setClass('queue'); - $cron->setFunction('cron'); - $cron->setSchedule('* * * * *'); - $cron->setTimeout(2); - $cron->setDeamon(0); - $cron->save(); - - $cron = cron::byClassAndFunction('plugin', 'cron5'); - if (!is_object($cron)) { - echo "Create plugin::cron5\n"; - $cron = new cron(); - } - $cron->setClass('plugin'); - $cron->setFunction('cron5'); - $cron->setSchedule('*/5 * * * *'); - $cron->setTimeout(5); - $cron->setDeamon(0); - $cron->save(); - - $cron = cron::byClassAndFunction('plugin', 'cron10'); - if (!is_object($cron)) { - echo "Create plugin::cron10\n"; - $cron = new cron(); - } - $cron->setClass('plugin'); + $cron = cron::byClassAndFunction('plugin', 'cronDaily'); + if (!is_object($cron)) { + echo "Create plugin::cronDaily\n"; + $cron = new cron(); + } + $cron->setClass('plugin'); + $cron->setFunction('cronDaily'); + $cron->setSchedule('00 00 * * *'); + $cron->setTimeout(240); + $cron->setEnable(1); + $cron->setDeamon(0); + $cron->save(); + + $cron = cron::byClassAndFunction('jeedom', 'backup'); + if (!is_object($cron)) { + echo "Create jeedom::backup\n"; + $cron = new cron(); + } + $cron->setClass('jeedom'); + $cron->setFunction('backup'); + $cron->setEnable(1); + $cron->setDeamon(0); + $cron->setTimeout(60); + $cron->setSchedule(rand(10, 59) . ' 0' . rand(0, 9) . ' * * *'); + $cron->save(); + + $cron = cron::byClassAndFunction('plugin', 'cronHourly'); + if (!is_object($cron)) { + echo "Create plugin::cronHourly\n"; + $cron = new cron(); + } + $cron->setClass('plugin'); + $cron->setFunction('cronHourly'); + $cron->setSchedule('00 * * * *'); + $cron->setEnable(1); + $cron->setDeamon(0); + $cron->setTimeout(60); + $cron->save(); + + $cron = cron::byClassAndFunction('scenario', 'check'); + if (!is_object($cron)) { + echo "Create scenario::check\n"; + $cron = new cron(); + } + $cron->setClass('scenario'); + $cron->setFunction('check'); + $cron->setSchedule('* * * * *'); + $cron->setEnable(1); + $cron->setDeamon(0); + $cron->setTimeout(30); + $cron->save(); + + $cron = cron::byClassAndFunction('scenario', 'control'); + if (!is_object($cron)) { + echo "Create scenario::control\n"; + $cron = new cron(); + } + $cron->setClass('scenario'); + $cron->setFunction('control'); + $cron->setSchedule('* * * * *'); + $cron->setEnable(1); + $cron->setDeamon(0); + $cron->setTimeout(30); + $cron->save(); + + $cron = cron::byClassAndFunction('jeedom', 'cronDaily'); + if (!is_object($cron)) { + echo "Create jeedom::cronDaily\n"; + $cron = new cron(); + } + $cron->setClass('jeedom'); + $cron->setFunction('cronDaily'); + $cron->setSchedule(rand(0, 59) . ' ' . rand(0, 3) . ' * * *'); + $cron->setEnable(1); + $cron->setDeamon(0); + $cron->setTimeout(240); + $cron->save(); + + $cron = cron::byClassAndFunction('jeedom', 'cronHourly'); + if (!is_object($cron)) { + echo "Create jeedom::cronHourly\n"; + $cron = new cron(); + } + $cron->setClass('jeedom'); + $cron->setFunction('cronHourly'); + $cron->setSchedule(rand(0, 59) . ' * * * *'); + $cron->setEnable(1); + $cron->setDeamon(0); + $cron->setTimeout(60); + $cron->save(); + + $cron = cron::byClassAndFunction('jeedom', 'cron5'); + if (!is_object($cron)) { + echo "Create jeedom::cron5\n"; + $cron = new cron(); + } + $cron->setClass('jeedom'); + $cron->setFunction('cron5'); + $cron->setSchedule('*/5 * * * *'); + $cron->setEnable(1); + $cron->setDeamon(0); + $cron->setTimeout(5); + $cron->save(); + + $cron = cron::byClassAndFunction('jeedom', 'cron10'); + if (!is_object($cron)) { + echo "Create jeedom::cron10\n"; + $cron = new cron(); + } + $cron->setClass('jeedom'); + $cron->setFunction('cron10'); + $cron->setSchedule('*/10 * * * *'); + $cron->setEnable(1); + $cron->setDeamon(0); + $cron->setTimeout(10); + $cron->save(); + + $cron = cron::byClassAndFunction('jeedom', 'cron'); + if (!is_object($cron)) { + echo "Create jeedom::cron\n"; + $cron = new cron(); + } + $cron->setClass('jeedom'); + $cron->setFunction('cron'); + $cron->setSchedule('* * * * *'); + $cron->setTimeout(2); + $cron->setDeamon(0); + $cron->save(); + + $cron = cron::byClassAndFunction('plugin', 'cron'); + if (!is_object($cron)) { + echo "Create plugin::cron\n"; + $cron = new cron(); + } + $cron->setClass('plugin'); + $cron->setFunction('cron'); + $cron->setSchedule('* * * * *'); + $cron->setTimeout(2); + $cron->setDeamon(0); + $cron->save(); + + $cron = cron::byClassAndFunction('queue', 'cron'); + if (!is_object($cron)) { + echo "Create queue::cron\n"; + $cron = new cron(); + } + $cron->setClass('queue'); + $cron->setFunction('cron'); + $cron->setSchedule('* * * * *'); + $cron->setTimeout(2); + $cron->setDeamon(0); + $cron->save(); + + $cron = cron::byClassAndFunction('plugin', 'cron5'); + if (!is_object($cron)) { + echo "Create plugin::cron5\n"; + $cron = new cron(); + } + $cron->setClass('plugin'); + $cron->setFunction('cron5'); + $cron->setSchedule('*/5 * * * *'); + $cron->setTimeout(5); + $cron->setDeamon(0); + $cron->save(); + + $cron = cron::byClassAndFunction('plugin', 'cron10'); + if (!is_object($cron)) { + echo "Create plugin::cron10\n"; + $cron = new cron(); + } + $cron->setClass('plugin'); + $cron->setFunction('cron10'); + $cron->setSchedule('*/10 * * * *'); + $cron->setTimeout(10); + $cron->setDeamon(0); + $cron->save(); + + $cron = cron::byClassAndFunction('plugin', 'cron15'); + if (!is_object($cron)) { + echo "Create plugin::cron15\n"; + $cron = new cron(); + } + $cron->setClass('plugin'); + $cron->setFunction('cron15'); + $cron->setSchedule('*/15 * * * *'); + $cron->setTimeout(15); + $cron->setDeamon(0); + $cron->save(); + + $cron = cron::byClassAndFunction('plugin', 'cron30'); + if (!is_object($cron)) { + echo "Create plugin::cron30\n"; + $cron = new cron(); + } + $cron->setClass('plugin'); + $cron->setFunction('cron30'); + $cron->setSchedule('*/30 * * * *'); + $cron->setTimeout(30); + $cron->setDeamon(0); + $cron->save(); + + $cron = cron::byClassAndFunction('plugin', 'checkDeamon'); + if (!is_object($cron)) { + echo "Create plugin::checkDeamon\n"; + $cron = new cron(); + } + $cron->setClass('plugin'); + $cron->setFunction('checkDeamon'); + $cron->setSchedule('*/5 * * * *'); + $cron->setTimeout(5); + $cron->setDeamon(0); + $cron->save(); + + $cron = cron::byClassAndFunction('cache', 'persist'); + if (!is_object($cron)) { + echo "Create cache::persist\n"; + $cron = new cron(); + } + $cron->setClass('cache'); + $cron->setFunction('persist'); + $cron->setSchedule('*/30 * * * *'); + $cron->setTimeout(30); + $cron->setDeamon(0); + $cron->save(); + + $cron = cron::byClassAndFunction('history', 'archive'); + if (!is_object($cron)) { + echo "Create history::archive\n"; + $cron = new cron(); + } + $cron->setClass('history'); + $cron->setFunction('archive'); + $cron->setSchedule('00 5 * * *'); + $cron->setTimeout(240); + $cron->setDeamon(0); + $cron->save(); + + $cron = cron::byClassAndFunction('plugin', 'heartbeat'); + if (!is_object($cron)) { + echo "Create plugin::heartbeat\n"; + $cron = new cron(); + } + $cron->setClass('plugin'); + $cron->setFunction('heartbeat'); + $cron->setSchedule('*/5 * * * *'); + $cron->setEnable(1); + $cron->setDeamon(0); + $cron->setTimeout(10); + $cron->save(); + + $cron = cron::byClassAndFunction('network', 'cron10'); + if (!is_object($cron)) { + echo "Create network::cron10\n"; + $cron = new cron(); + $cron->setClass('network'); $cron->setFunction('cron10'); - $cron->setSchedule('*/10 * * * *'); - $cron->setTimeout(10); - $cron->setDeamon(0); - $cron->save(); - - $cron = cron::byClassAndFunction('plugin', 'cron15'); - if (!is_object($cron)) { - echo "Create plugin::cron15\n"; - $cron = new cron(); - } - $cron->setClass('plugin'); - $cron->setFunction('cron15'); - $cron->setSchedule('*/15 * * * *'); - $cron->setTimeout(15); - $cron->setDeamon(0); - $cron->save(); - - $cron = cron::byClassAndFunction('plugin', 'cron30'); - if (!is_object($cron)) { - echo "Create plugin::cron30\n"; - $cron = new cron(); - } - $cron->setClass('plugin'); - $cron->setFunction('cron30'); - $cron->setSchedule('*/30 * * * *'); - $cron->setTimeout(30); - $cron->setDeamon(0); - $cron->save(); - - $cron = cron::byClassAndFunction('plugin', 'checkDeamon'); - if (!is_object($cron)) { - echo "Create plugin::checkDeamon\n"; - $cron = new cron(); - } - $cron->setClass('plugin'); - $cron->setFunction('checkDeamon'); - $cron->setSchedule('*/5 * * * *'); - $cron->setTimeout(5); - $cron->setDeamon(0); - $cron->save(); - - $cron = cron::byClassAndFunction('cache', 'persist'); - if (!is_object($cron)) { - echo "Create cache::persist\n"; - $cron = new cron(); - } - $cron->setClass('cache'); - $cron->setFunction('persist'); - $cron->setSchedule('*/30 * * * *'); - $cron->setTimeout(30); - $cron->setDeamon(0); - $cron->save(); - - $cron = cron::byClassAndFunction('history', 'archive'); - if (!is_object($cron)) { - echo "Create history::archive\n"; - $cron = new cron(); + $rand_min = rand(0,9); + $cronString = ''; + for($i=0;$i<6;$i++){ + $cronString .= ($rand_min+($i*10)).','; } - $cron->setClass('history'); - $cron->setFunction('archive'); - $cron->setSchedule('00 5 * * *'); - $cron->setTimeout(240); - $cron->setDeamon(0); - $cron->save(); - - $cron = cron::byClassAndFunction('plugin', 'heartbeat'); - if (!is_object($cron)) { - echo "Create plugin::heartbeat\n"; - $cron = new cron(); - } - $cron->setClass('plugin'); - $cron->setFunction('heartbeat'); - $cron->setSchedule('*/5 * * * *'); + $cronString = trim($cronString,',').' * * * *'; + $cron->setSchedule($cronString); $cron->setEnable(1); $cron->setDeamon(0); - $cron->setTimeout(10); + $cron->setTimeout(60); $cron->save(); + } - $cron = cron::byClassAndFunction('network', 'cron10'); - if (!is_object($cron)) { - echo "Create network::cron10\n"; - $cron = new cron(); - $cron->setClass('network'); - $cron->setFunction('cron10'); - $rand_min = rand(0,9); - $cronString = ''; - for($i=0;$i<6;$i++){ - $cronString .= ($rand_min+($i*10)).','; - } - $cronString = trim($cronString,',').' * * * *'; - $cron->setSchedule($cronString); - $cron->setEnable(1); - $cron->setDeamon(0); - $cron->setTimeout(60); - $cron->save(); - } + if (!file_exists(__DIR__ . '/../plugins')) { + mkdir(__DIR__ . '/../plugins'); + } + try { + echo "\nCheck filesystem right..."; + jeedom::cleanFileSystemRight(); + echo "OK\n"; + } catch (Exception $e) { + echo "NOK\n"; + } - if (!file_exists(__DIR__ . '/../plugins')) { - mkdir(__DIR__ . '/../plugins'); - } + config::save('hardware_name', ''); + if (config::byKey('api') == '') { + echo "Fix default apikey...\n"; + config::save('api', config::genKey()); + } + if (file_exists(__DIR__ . '/../core/nodeJS')) { + echo "Remove unused nodejs folder...\n"; + shell_exec(system::getCmdSudo() . 'rm -rf ' . __DIR__ . '/../core/nodeJS'); + } + if (file_exists(__DIR__ . '/../script/ngrok')) { + echo "Remove unused ngrok folder...\n"; + shell_exec(system::getCmdSudo() . 'rm -rf ' . __DIR__ . '/../script/ngrok'); + } + + echo "Check jeedom object..."; + foreach (jeeObject::all() as $object) { try { - echo "\nCheck filesystem right..."; - jeedom::cleanFileSystemRight(); - echo "OK\n"; - } catch (Exception $e) { - echo "NOK\n"; + $object->save(); + } catch (Exception $exc) { } + } + echo "OK\n"; - config::save('hardware_name', ''); - if (config::byKey('api') == '') { - echo "Fix default apikey...\n"; - config::save('api', config::genKey()); - } - if (file_exists(__DIR__ . '/../core/nodeJS')) { - echo "Remove unused nodejs folder...\n"; - shell_exec(system::getCmdSudo() . 'rm -rf ' . __DIR__ . '/../core/nodeJS'); - } - if (file_exists(__DIR__ . '/../script/ngrok')) { - echo "Remove unused ngrok folder...\n"; - shell_exec(system::getCmdSudo() . 'rm -rf ' . __DIR__ . '/../script/ngrok'); - } - - echo "Check jeedom object..."; - foreach (jeeObject::all() as $object) { - try { - $object->save(); - } catch (Exception $exc) { + echo "Check jeedom cmd..."; + foreach (cmd::all() as $cmd) { + try { + $changed = false; + if ($cmd->getConfiguration('jeedomCheckCmdCmdActionId') != '') { + $cmd->setConfiguration('jeedomCheckCmdCmdActionId', ''); + $changed = true; } - } - echo "OK\n"; - - echo "Check jeedom cmd..."; - foreach (cmd::all() as $cmd) { - try { - $changed = false; - if ($cmd->getConfiguration('jeedomCheckCmdCmdActionId') != '') { - $cmd->setConfiguration('jeedomCheckCmdCmdActionId', ''); - $changed = true; - } - if (trim($cmd->getTemplate('dashboard')) != '' && strpos($cmd->getTemplate('dashboard'), '::') === false) { - $cmd->setTemplate('dashboard', 'core::' . $cmd->getTemplate('dashboard')); - $changed = true; - } - if (trim($cmd->getTemplate('mobile')) != '' && strpos($cmd->getTemplate('mobile'), '::') === false) { - $cmd->setTemplate('mobile', 'core::' . $cmd->getTemplate('mobile')); - $changed = true; - } - if ($changed) { - $cmd->save(true); - } - } catch (Exception $exc) { + if (trim($cmd->getTemplate('dashboard')) != '' && strpos($cmd->getTemplate('dashboard'), '::') === false) { + $cmd->setTemplate('dashboard', 'core::' . $cmd->getTemplate('dashboard')); + $changed = true; + } + if (trim($cmd->getTemplate('mobile')) != '' && strpos($cmd->getTemplate('mobile'), '::') === false) { + $cmd->setTemplate('mobile', 'core::' . $cmd->getTemplate('mobile')); + $changed = true; + } + if ($changed) { + $cmd->save(true); } + } catch (Exception $exc) { } } + echo "OK\n"; if (!file_exists(__DIR__ . '/../data/php/user.function.class.php')) { From 8f7dbdeeb6041fbaf157c513a146a3d9230902cd Mon Sep 17 00:00:00 2001 From: Salvialf Date: Tue, 16 Dec 2025 09:49:30 +0100 Subject: [PATCH 030/128] update doc & changelog #3109 --- docs/fr_FR/changelog.md | 1 + docs/fr_FR/scenario.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/fr_FR/changelog.md b/docs/fr_FR/changelog.md index 4c4c0cfbc5..8b7a330a54 100644 --- a/docs/fr_FR/changelog.md +++ b/docs/fr_FR/changelog.md @@ -2,6 +2,7 @@ # 4.5.1 +- Le déclencheur de scénarios `#user_connect#` inclut dorénavant le tag `#trigger_value#` qui renseigne le nom de l'utilisateur venant de se connecter ([Détails](https://github.com/jeedom/core/pull/3109)) - Montée de version de nodejs 20 vers 22 ([Détails](https://github.com/jeedom/core/issues/3147)) # 4.5 diff --git a/docs/fr_FR/scenario.md b/docs/fr_FR/scenario.md index 13d3c0d1e0..779d802487 100644 --- a/docs/fr_FR/scenario.md +++ b/docs/fr_FR/scenario.md @@ -236,7 +236,7 @@ Il existe des déclencheurs spécifiques (autre que ceux fournis par les command - ``#end_update#`` : Événement envoyé à la fin d’une mise à jour. - ``#begin_restore#`` : Événement envoyé au début d’une restauration. - ``#end_restore#`` : Événement envoyé à la fin d’une restauration. -- ``#user_connect#`` : Connexion d'un utilisateur +- ``#user_connect#`` : Connexion d'un utilisateur, le tag `#trigger_value#` contient le nom de l'utilisateur. - ``#variable(nom_variable)#`` : Changement de valeur de la variable nom_variable. - ``#genericType(GENERIC, #[Object]#)#`` : Changement d'une commande info de Type Generic GENERIC, dans l'objet Object. - ``#new_eqLogic#`` : Événement envoyé lors de la création d'un nouvelle équipement, vous avez dans les tags id (id de l'équipement crée), name (nom de l'équipement crée) et eqType (type/plugin de l'équipement crée) From 241cf2c5fb3b9040b34474d2b0e8ee6c8ad54b74 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Tue, 16 Dec 2025 10:49:39 +0100 Subject: [PATCH 031/128] continue if value is null --- core/class/scenarioExpression.class.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/class/scenarioExpression.class.php b/core/class/scenarioExpression.class.php index 7fa2e9e653..4208bbcd78 100644 --- a/core/class/scenarioExpression.class.php +++ b/core/class/scenarioExpression.class.php @@ -1275,7 +1275,9 @@ public static function setTags(&$_expression, &$_scenario = null, $_quote = fals } if ($_quote) { foreach ($replace1 as &$value) { - $value = (string)$value; + if ($value === null) { + continue; + } if (strpos($value, ' ') !== false || preg_match("/[a-zA-Z]/", $value) || $value === '') { $value = '"' . trim($value, '"') . '"'; } From 3cf20ec1917d238729b5e0f6a0d651c7ed1daf23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc?= <1536036+zoic21@users.noreply.github.com> Date: Wed, 17 Dec 2025 10:10:39 +0100 Subject: [PATCH 032/128] Update changelog.md --- docs/fr_FR/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/fr_FR/changelog.md b/docs/fr_FR/changelog.md index 8b7a330a54..dd8fc45589 100644 --- a/docs/fr_FR/changelog.md +++ b/docs/fr_FR/changelog.md @@ -4,6 +4,8 @@ - Le déclencheur de scénarios `#user_connect#` inclut dorénavant le tag `#trigger_value#` qui renseigne le nom de l'utilisateur venant de se connecter ([Détails](https://github.com/jeedom/core/pull/3109)) - Montée de version de nodejs 20 vers 22 ([Détails](https://github.com/jeedom/core/issues/3147)) +- La sauvegarde se fera maintenant a une heure aléatoire entre 00:10 et 9:59 +- Correction d'un warning sur les valeurs de tags null # 4.5 From d0a6536d5813ddc29228e98ea21f3e0d8d1a5495 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Wed, 17 Dec 2025 11:14:33 +0100 Subject: [PATCH 033/128] init 4.5.2 --- core/config/version | 2 +- docs/fr_FR/changelog.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/core/config/version b/core/config/version index 4404a17bae..6cedcff630 100644 --- a/core/config/version +++ b/core/config/version @@ -1 +1 @@ -4.5.1 +4.5.2 diff --git a/docs/fr_FR/changelog.md b/docs/fr_FR/changelog.md index dd8fc45589..a405427e77 100644 --- a/docs/fr_FR/changelog.md +++ b/docs/fr_FR/changelog.md @@ -1,5 +1,8 @@ # Changelog Jeedom V4.5 +# 4.5.2 + + # 4.5.1 - Le déclencheur de scénarios `#user_connect#` inclut dorénavant le tag `#trigger_value#` qui renseigne le nom de l'utilisateur venant de se connecter ([Détails](https://github.com/jeedom/core/pull/3109)) From d76426dd6b244d8d3ba3cb39c2b01cc49f3c3019 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Wed, 17 Dec 2025 12:58:23 +0100 Subject: [PATCH 034/128] code format --- install/update.php | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/install/update.php b/install/update.php index 76d0fc3dea..11c8abf3c2 100644 --- a/install/update.php +++ b/install/update.php @@ -30,7 +30,7 @@ $update_begin = false; try { require_once __DIR__ . '/../core/php/core.inc.php'; - + echo "[PROGRESS][1]\n"; if (count(system::ps('install/update.php', 'sudo')) > 1) { echo "Update in progress. I will wait 10s\n"; @@ -76,7 +76,7 @@ echo "[PROGRESS][5]\n"; try { echo "Check rights..."; - if(method_exists('jeedom','cleanFileSystemRight')){ + if (method_exists('jeedom', 'cleanFileSystemRight')) { jeedom::cleanFileSystemRight(); } echo "OK\n"; @@ -111,9 +111,9 @@ $tmp = $tmp_dir . '/jeedom_update.zip'; try { if (config::byKey('core::repo::provider') == 'default') { - if(strpos(config::byKey('core::branch'),'tag::') === 0){ - $url = 'https://github.com/jeedom/core/archive/refs/tags/'.str_replace('tag::','',config::byKey('core::branch')).'.zip'; - }else{ + if (strpos(config::byKey('core::branch'), 'tag::') === 0) { + $url = 'https://github.com/jeedom/core/archive/refs/tags/' . str_replace('tag::', '', config::byKey('core::branch')) . '.zip'; + } else { $url = 'https://github.com/jeedom/core/archive/' . config::byKey('core::branch') . '.zip'; } echo "Download url : " . $url . "\n"; @@ -161,14 +161,14 @@ echo "[PROGRESS][35]\n"; echo "Unzip in progress..."; $zip = new ZipArchive; - $open = $zip->open($tmp); + $open = $zip->open($tmp); if ($open === TRUE) { if (!$zip->extractTo($cibDir)) { - throw new Exception('Can not unzip file => '.$zip->getStatusString()); + throw new Exception('Can not unzip file => ' . $zip->getStatusString()); } $zip->close(); } else { - throw new Exception('Unable to unzip file : ' . $tmp.' =>'.$open); + throw new Exception('Unable to unzip file : ' . $tmp . ' =>' . $open); } echo "OK\n"; if (disk_free_space($cibDir) < 10) { @@ -181,7 +181,7 @@ $cibDir = $cibDir . '/' . $files[0]; } } - + if (init('preUpdate') == 1) { echo "Update updater..."; rmove($cibDir . '/install/update.php', __DIR__ . '/update.php', false, array(), array('log' => true, 'ignoreFileSizeUnder' => 1)); @@ -215,9 +215,9 @@ shell_exec('rm -rf ' . $cibDir . '/vendor'); echo "OK\n"; echo "[PROGRESS][46]\n"; - + echo "Update modification date of unzip file..."; - shell_exec('find '.$cibDir.'/ -exec touch {} +'); + shell_exec('find ' . $cibDir . '/ -exec touch {} +'); echo "OK\n"; echo "[PROGRESS][47]\n"; @@ -237,7 +237,7 @@ } echo "OK\n"; echo "[PROGRESS][52]\n"; - if(strpos(config::byKey('core::branch'),'tag::') !== 0){ + if (strpos(config::byKey('core::branch'), 'tag::') !== 0) { echo "Remove useless files...\n"; foreach (array('3rdparty', 'desktop', 'mobile', 'core', 'docs', 'install', 'script') as $folder) { echo 'Cleaning ' . $folder . "\n"; @@ -248,16 +248,16 @@ echo "[PROGRESS][53]\n"; if (exec('which composer | wc -l') == 0) { echo "\nNeed to install composer..."; - echo shell_exec(system::getCmdSudo().' ' . __DIR__ . '/../resources/install_composer.sh'); + echo shell_exec(system::getCmdSudo() . ' ' . __DIR__ . '/../resources/install_composer.sh'); echo "OK\n"; } echo "Update composer file...\n"; if (exec('which composer | wc -l') > 0) { - shell_exec(system::getCmdSudo(). ' rm '. __DIR__ . '/../composer.lock'); - shell_exec('export COMPOSER_HOME="/tmp/composer";export COMPOSER_ALLOW_SUPERUSER=1;'.system::getCmdSudo().' composer self-update > /dev/null 2>&1'); - shell_exec('cd ' . __DIR__ . '/../;export COMPOSER_ALLOW_SUPERUSER=1;export COMPOSER_HOME="/tmp/composer";'.system::getCmdSudo().' composer update --no-interaction --no-plugins --no-scripts --no-ansi --no-dev --no-progress --optimize-autoloader --with-all-dependencies --no-cache > /dev/null 2>&1'); - shell_exec(system::getCmdSudo().' rm /tmp/composer 2>/dev/null'); - if(method_exists('jeedom','cleanFileSystemRight')){ + shell_exec(system::getCmdSudo() . ' rm ' . __DIR__ . '/../composer.lock'); + shell_exec('export COMPOSER_HOME="/tmp/composer";export COMPOSER_ALLOW_SUPERUSER=1;' . system::getCmdSudo() . ' composer self-update > /dev/null 2>&1'); + shell_exec('cd ' . __DIR__ . '/../;export COMPOSER_ALLOW_SUPERUSER=1;export COMPOSER_HOME="/tmp/composer";' . system::getCmdSudo() . ' composer update --no-interaction --no-plugins --no-scripts --no-ansi --no-dev --no-progress --optimize-autoloader --with-all-dependencies --no-cache > /dev/null 2>&1'); + shell_exec(system::getCmdSudo() . ' rm /tmp/composer 2>/dev/null'); + if (method_exists('jeedom', 'cleanFileSystemRight')) { jeedom::cleanFileSystemRight(); } } From 664d6b633ee177d01fb686fda4102c7d11a3d9d8 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Wed, 17 Dec 2025 13:00:35 +0100 Subject: [PATCH 035/128] prevent warning --- install/update.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/update.php b/install/update.php index 11c8abf3c2..652834ed41 100644 --- a/install/update.php +++ b/install/update.php @@ -396,7 +396,7 @@ function incrementVersion($_version) { $version = explode('.', $_version); - if ($version[2] < 100) { + if (isset($version[2]) && $version[2] < 100) { $version[2]++; } else { if ($version[1] < 100) { From 4f186f6d2cf498d725617e36b4428aa43dd368f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc?= <1536036+zoic21@users.noreply.github.com> Date: Thu, 18 Dec 2025 10:03:15 +0100 Subject: [PATCH 036/128] Random backup only for cloud backup enable --- install/consistency.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/install/consistency.php b/install/consistency.php index f432e2964d..6cedb59a55 100644 --- a/install/consistency.php +++ b/install/consistency.php @@ -145,13 +145,16 @@ if (!is_object($cron)) { echo "Create jeedom::backup\n"; $cron = new cron(); + $cron->setSchedule(rand(10, 59) . ' 0' . rand(0, 9) . ' * * *'); + } + if(config::byKey('service::backup::enable')){ + $cron->setSchedule(rand(10, 59) . ' 0' . rand(0, 9) . ' * * *'); } $cron->setClass('jeedom'); $cron->setFunction('backup'); $cron->setEnable(1); $cron->setDeamon(0); $cron->setTimeout(60); - $cron->setSchedule(rand(10, 59) . ' 0' . rand(0, 9) . ' * * *'); $cron->save(); $cron = cron::byClassAndFunction('plugin', 'cronHourly'); From d082cb686fafbdec458b8cef63494b3eb5e418df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc?= <1536036+zoic21@users.noreply.github.com> Date: Thu, 18 Dec 2025 10:57:59 +0100 Subject: [PATCH 037/128] Update consistency.php --- install/consistency.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/consistency.php b/install/consistency.php index 6cedb59a55..7434197e0c 100644 --- a/install/consistency.php +++ b/install/consistency.php @@ -147,7 +147,7 @@ $cron = new cron(); $cron->setSchedule(rand(10, 59) . ' 0' . rand(0, 9) . ' * * *'); } - if(config::byKey('service::backup::enable')){ + if(config::byKey('market::cloudUpload', 0) == 1){ $cron->setSchedule(rand(10, 59) . ' 0' . rand(0, 9) . ' * * *'); } $cron->setClass('jeedom'); From 1b78dc170d9fac5487841396e25737e52544f006 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Thu, 18 Dec 2025 21:40:52 +0100 Subject: [PATCH 038/128] fix 664d6b6 --- install/update.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/install/update.php b/install/update.php index 652834ed41..c1486b89ea 100644 --- a/install/update.php +++ b/install/update.php @@ -396,7 +396,10 @@ function incrementVersion($_version) { $version = explode('.', $_version); - if (isset($version[2]) && $version[2] < 100) { + if (!isset($version[2])) { + $version[2] = 0; + } + if ($version[2] < 100) { $version[2]++; } else { if ($version[1] < 100) { From 9962eb6b10b315ce655f997c98ee7a5fee9aa9a3 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Fri, 19 Dec 2025 14:22:09 +0100 Subject: [PATCH 039/128] remove jeeNetwork leftovers but keep system halt & reboot --- core/api/proApi.php | 143 +++----------------------------------------- 1 file changed, 7 insertions(+), 136 deletions(-) diff --git a/core/api/proApi.php b/core/api/proApi.php index cdf7f18ac9..31e91712b9 100644 --- a/core/api/proApi.php +++ b/core/api/proApi.php @@ -147,7 +147,7 @@ $result = jeedom::version(); $advice = ''; $productName = config::byKey('product_name'); - $health[] = array('plugin' => 'core', 'type' => 'Version '. $productName , 'defaut' => $defaut, 'result' => $result, 'advice' => $advice); + $health[] = array('plugin' => 'core', 'type' => 'Version ' . $productName, 'defaut' => $defaut, 'result' => $result, 'advice' => $advice); $defaut = 0; $result = phpversion(); @@ -199,7 +199,7 @@ $result = 'OK'; } else { $filename = __DIR__ . '/../../cache.tar.gz'; - $result = 'OK (' . date('Y-m-d H:i:s',(int) filemtime($filename)) . ')'; + $result = 'OK (' . date('Y-m-d H:i:s', (int) filemtime($filename)) . ')'; } } else { $result = 'NOK'; @@ -569,8 +569,8 @@ $jsonrpc->makeSuccess($scenario->stop()); } if ($params['state'] == 'run') { - $scenario->addTag('trigger','api'); - $scenario->addTag('trigger_message',__('Scénario exécuté sur appel API', __FILE__)); + $scenario->addTag('trigger', 'api'); + $scenario->addTag('trigger_message', __('Scénario exécuté sur appel API', __FILE__)); $jsonrpc->makeSuccess($scenario->launch()); } if ($params['state'] == 'enable') { @@ -584,147 +584,18 @@ throw new Exception(__('Le paramètre "state" ne peut être vide et doit avoir pour valeur [run,stop,enable,disable]', __FILE__)); } - /* * ************************JeeNetwork*************************** */ - if ($jsonrpc->getMethod() == 'jeeNetwork::handshake') { - if (config::byKey('jeeNetwork::mode') != 'slave') { - throw new Exception(__('Impossible d\'ajouter une box Jeedom non esclave à un réseau Jeedom', __FILE__)); - } - $auiKey = config::byKey('auiKey'); - if ($auiKey == '') { - $auiKey = config::genKey(255); - config::save('auiKey', $auiKey); - } - $return = array( - 'mode' => config::byKey('jeeNetwork::mode'), - 'nbUpdate' => update::nbNeedUpdate(), - 'version' => jeedom::version(), - 'nbMessage' => message::nbMessage(), - 'auiKey' => $auiKey, - 'jeedom::url' => config::byKey('jeedom::url'), - 'ngrok::port' => config::byKey('ngrok::port'), - ); - if (!filter_var(network::getNetworkAccess('external', 'ip'), FILTER_VALIDATE_IP) && network::getNetworkAccess('external', 'ip') != '') { - $return['jeedom::url'] = network::getNetworkAccess('internal'); - } - foreach (plugin::listPlugin(true) as $plugin) { - if ($plugin->getAllowRemote() == 1) { - $return['plugin'][] = $plugin->getId(); - } - } - $address = (isset($params['address']) && $params['address'] != '') ? $params['address'] : getClientIp(); - config::save('jeeNetwork::master::ip', $address); - config::save('jeeNetwork::master::apikey', $params['apikey_master']); - config::save('jeeNetwork::slave::id', $params['slave_id']); - if (config::byKey('internalAddr') == '') { - config::save('internalAddr', $params['slave_ip']); - } - jeeNetwork::testMaster(); - $jsonrpc->makeSuccess($return); - } + /* * ************************System*************************** */ - if ($jsonrpc->getMethod() == 'jeeNetwork::reload') { - foreach (plugin::listPlugin(true) as $plugin) { - try { - $plugin->launch('slaveReload'); - } catch (Exception $ex) { - } - } - $jsonrpc->makeSuccess('ok'); - } - - if ($jsonrpc->getMethod() == 'jeeNetwork::halt') { + if ($jsonrpc->getMethod() == 'system::halt') { jeedom::haltSystem(); $jsonrpc->makeSuccess('ok'); } - if ($jsonrpc->getMethod() == 'jeeNetwork::reboot') { + if ($jsonrpc->getMethod() == 'system::reboot') { jeedom::rebootSystem(); $jsonrpc->makeSuccess('ok'); } - if ($jsonrpc->getMethod() == 'jeeNetwork::update') { - jeedom::update('', 0); - $jsonrpc->makeSuccess('ok'); - } - - if ($jsonrpc->getMethod() == 'jeeNetwork::checkUpdate') { - update::checkAllUpdate(); - $jsonrpc->makeSuccess('ok'); - } - - if ($jsonrpc->getMethod() == 'jeeNetwork::receivedBackup') { - if (config::byKey('jeeNetwork::mode') == 'slave') { - throw new Exception(__('Seul un maître peut recevoir une sauvegarde', __FILE__)); - } - $jeeNetwork = jeeNetwork::byId($params['slave_id']); - if (!is_object($jeeNetwork)) { - throw new Exception(__('Aucun esclave correspondant à l\'ID :', __FILE__) . ' ' . secureXSS($params['slave_id'])); - } - if (substr(config::byKey('backup::path'), 0, 1) != '/') { - $backup_dir = __DIR__ . '/../../' . config::byKey('backup::path'); - } else { - $backup_dir = config::byKey('backup::path'); - } - $uploaddir = $backup_dir . '/slave/'; - if (!file_exists($uploaddir)) { - mkdir($uploaddir); - } - if (!file_exists($uploaddir)) { - throw new Exception(__('Répertoire de téléversement non trouvé :', __FILE__) . ' ' . secureXSS($uploaddir)); - } - $_file = $_FILES['file']; - $extension = strtolower(strrchr($_file['name'], '.')); - if (!in_array($extension, array('.tar.gz', '.gz', '.tar'))) { - throw new Exception(__('Extension du fichier non valide (autorisé .tar.gz, .tar et .gz) :', __FILE__) . ' ' . secureXSS($extension)); - } - if (filesize($_file['tmp_name']) > 50000000) { - throw new Exception(__('La taille du fichier est trop importante (maximum 50Mo)', __FILE__)); - } - $uploadfile = $uploaddir . $jeeNetwork->getId() . '-' . $jeeNetwork->getName() . '-' . $jeeNetwork->getConfiguration('version') . '-' . date('Y-m-d_H\hi') . '.tar' . $extension; - if (!move_uploaded_file($_file['tmp_name'], $uploadfile)) { - throw new Exception(__('Impossible de téléverser le fichier', __FILE__)); - } - system('find ' . $uploaddir . $jeeNetwork->getId() . '*' . ' -mtime +' . config::byKey('backup::keepDays') . ' -print | xargs -r rm'); - $jsonrpc->makeSuccess('ok'); - } - - if ($jsonrpc->getMethod() == 'jeeNetwork::restoreBackup') { - if (config::byKey('jeeNetwork::mode') != 'slave') { - throw new Exception(__('Seul un esclave peut restaurer une sauvegarde', __FILE__)); - } - if (substr(config::byKey('backup::path'), 0, 1) != '/') { - $uploaddir = __DIR__ . '/../../' . config::byKey('backup::path'); - } else { - $uploaddir = config::byKey('backup::path'); - } - if (!file_exists($uploaddir)) { - mkdir($uploaddir); - } - if (!file_exists($uploaddir)) { - throw new Exception(__('Répertoire de téléversement non trouvé :', __FILE__) . ' ' . secureXSS($uploaddir)); - } - $_file = $_FILES['file']; - $extension = strtolower(strrchr($_file['name'], '.')); - if (!in_array($extension, array('.tar.gz', '.gz', '.tar'))) { - throw new Exception(__('Extension du fichier non valide (autorisé .tar.gz, .tar et .gz) :', __FILE__) . ' ' . secureXSS($extension)); - } - if (filesize($_file['tmp_name']) > 50000000) { - throw new Exception(__('La taille du fichier est trop importante (maximum 50Mo)', __FILE__)); - } - $backup_name = 'backup-' . jeedom::version() . '-' . date("d-m-Y-H\hi") . '.tar.gz'; - $uploadfile = $uploaddir . '/' . $backup_name; - if (!move_uploaded_file($_file['tmp_name'], $uploadfile)) { - throw new Exception(__('Impossible de téléverser le fichier', __FILE__)); - } - jeedom::restore($uploadfile, true); - $jsonrpc->makeSuccess('ok'); - } - - if ($jsonrpc->getMethod() == 'jeeNetwork::backup') { - jeedom::backup(true); - $jsonrpc->makeSuccess('ok'); - } - /* * ************************Backup*************************** */ if ($jsonrpc->getMethod() == 'backup::list') { From 91d5f590ff06ff696cf46a40d8fcef3f26599a2d Mon Sep 17 00:00:00 2001 From: noodom Date: Thu, 25 Dec 2025 19:10:29 +0100 Subject: [PATCH 040/128] fix error in scenario search --- desktop/js/scenario.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/desktop/js/scenario.js b/desktop/js/scenario.js index 531d350dcb..ab6f03f5b5 100644 --- a/desktop/js/scenario.js +++ b/desktop/js/scenario.js @@ -1999,8 +1999,11 @@ document.getElementById('div_editScenario').querySelector('div.floatingbar').add _target.setAttribute('data-state', '1') //open code blocks for later search: document.querySelectorAll('#div_scenarioElement div.elementCODE.elementCollapse').forEach(_code => { - _code.removeClass('elementCollapse') - _code.querySelector('textarea[data-l1key="expression"]').show() + _code.classList.remove('elementCollapse'); + const textarea = _code.querySelector('textarea[data-l1key="expression"]'); + if (textarea) { + textarea.style.display = 'block'; + } }) jeeP.setEditors() document.querySelectorAll('textarea[data-l1key="expression"]').unseen() From de3a5c51b2d19e1867c3912fbac8aa4919715929 Mon Sep 17 00:00:00 2001 From: "Julien C." Date: Tue, 30 Dec 2025 16:51:25 +0100 Subject: [PATCH 041/128] Update market.display.repo.php --- core/repo/market.display.repo.php | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/core/repo/market.display.repo.php b/core/repo/market.display.repo.php index cb0aa4a257..551e03f3e6 100644 --- a/core/repo/market.display.repo.php +++ b/core/repo/market.display.repo.php @@ -114,21 +114,25 @@ ?>

getCertification() == 'Premium') { - echo '{{Nous Contacter}}'; + echo '{{Nous Contacter}}'; } else { - if ($market->getCost() > 0) { - if ($market->getPurchase() == 1 && isset($purchase_info['user_id']) && is_numeric($purchase_info['user_id'])) { - echo '{{Plugin deja acheté et/ou inclus dans votre service Pack}}'; - }else{ - if ($market->getCost() != $market->getRealCost()) { + if (isset($purchase_info['user_id']) && is_numeric($purchase_info['user_id']) && $market->getPurchase() == 1) { + echo '{{Plugin deja acheté et/ou inclus dans votre service Pack}}'; + } else { + if ($market->getCost() > 0) { + if ($market->getCost() != $market->getRealCost()) { echo '' . number_format($market->getRealCost(), 2) . ' € '; - } - echo '' . number_format($market->getCost(), 2) . ' € TTC'; - } - } else { - echo '{{Gratuit}}'; - } + } + echo '' . number_format($market->getCost(), 2) . ' € TTC'; + } else { + echo '{{Gratuit}}'; + } + } } ?>
From 48720580f477f1e62f018933c2c1d83ea2bc045b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc?= <1536036+zoic21@users.noreply.github.com> Date: Thu, 1 Jan 2026 09:29:48 +0100 Subject: [PATCH 042/128] bugfix --- core/class/jeedom.class.php | 12 ------------ core/config/version | 2 +- docs/fr_FR/changelog.md | 4 ++++ 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/core/class/jeedom.class.php b/core/class/jeedom.class.php index d090860d81..b44d49f722 100644 --- a/core/class/jeedom.class.php +++ b/core/class/jeedom.class.php @@ -1036,18 +1036,6 @@ public static function isDateOk() { return false; } } - $minDateValue = new \DateTime('2020-01-01'); - $mindate = strtotime($minDateValue->format('Y-m-d 00:00:00')); - $maxDateValue = $minDateValue->modify('+6 year')->format('Y-m-d 00:00:00'); - $maxdate = strtotime($maxDateValue); - if (strtotime('now') < $mindate || strtotime('now') > $maxdate) { - self::forceSyncHour(); - sleep(3); - if (strtotime('now') < $mindate || strtotime('now') > $maxdate) { - log::add('core', 'error', __('La date du système est incorrecte (avant ' . $minDateValue . ' ou après ' . $maxDateValue . ') :', __FILE__) . ' ' . (new \DateTime())->format('Y-m-d H:i:s'), 'dateCheckFailed'); - return false; - } - } return true; } diff --git a/core/config/version b/core/config/version index 4404a17bae..689f7fbd33 100644 --- a/core/config/version +++ b/core/config/version @@ -1 +1 @@ -4.5.1 +4.5.2 \ No newline at end of file diff --git a/docs/fr_FR/changelog.md b/docs/fr_FR/changelog.md index dd8fc45589..ab2c7f8ea5 100644 --- a/docs/fr_FR/changelog.md +++ b/docs/fr_FR/changelog.md @@ -1,5 +1,9 @@ # Changelog Jeedom V4.5 +# 4.5.2 + +Mise à jour vivement recommandée, elle corrige un bug sur la verification de la date qui empeche tout lancement de scénario ou de tache planifiée. + # 4.5.1 - Le déclencheur de scénarios `#user_connect#` inclut dorénavant le tag `#trigger_value#` qui renseigne le nom de l'utilisateur venant de se connecter ([Détails](https://github.com/jeedom/core/pull/3109)) From a48dc35c498e62c9e10d13b0d33ba46fe4be8a7f Mon Sep 17 00:00:00 2001 From: BisonJeedom <110299206+BisonJeedom@users.noreply.github.com> Date: Sat, 3 Jan 2026 10:59:36 +0100 Subject: [PATCH 043/128] fix graphUpdate function in history class --- core/js/history.class.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/js/history.class.js b/core/js/history.class.js index 9af112f932..0d1d80f807 100644 --- a/core/js/history.class.js +++ b/core/js/history.class.js @@ -160,7 +160,7 @@ jeedom.history.graphUpdate = function(_params) { continue; } for(var chart in jeedom.history.chart){ - for(var serie in jeedom.history.chart[chart]){ + for(var serie in jeedom.history.chart[chart].chart.series){ if(jeedom.history.chart[chart].chart.series[serie] && jeedom.history.chart[chart].chart.series[serie].options.id == _params[i].cmd_id){ jeedom.history.chart[chart].chart.series[serie].addPoint([Date.now()+(-1*(new Date()).getTimezoneOffset()*60*1000),_params[i].value]) } From fd4fc7bf8b848d664a652c2371ebe14c14835142 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Sat, 3 Jan 2026 11:15:02 +0100 Subject: [PATCH 044/128] rebase 4.5.3 from master --- core/class/jeedom.class.php | 28 ++++++++-------------------- core/config/version | 2 +- docs/fr_FR/changelog.md | 3 +++ 3 files changed, 12 insertions(+), 21 deletions(-) diff --git a/core/class/jeedom.class.php b/core/class/jeedom.class.php index d090860d81..89773074c0 100644 --- a/core/class/jeedom.class.php +++ b/core/class/jeedom.class.php @@ -222,7 +222,7 @@ public static function health() { $state = self::isDateOk(); $cache = cache::byKey('hour'); $lastKnowDate = $cache->getValue(); - if($lastKnowDate === ""){ + if ($lastKnowDate === "") { $lastKnowDate = 0; } $return[] = array( @@ -427,7 +427,7 @@ public static function health() { $return[] = array( 'name' => __('Charge', __FILE__), 'state' => ($values[2] < 20), - 'result' => round($values[0],2) . ' - ' . round($values[1],2) . ' - ' . round($values[2],2), + 'result' => round($values[0], 2) . ' - ' . round($values[1], 2) . ' - ' . round($values[2], 2), 'comment' => '', 'key' => 'load' ); @@ -1036,18 +1036,6 @@ public static function isDateOk() { return false; } } - $minDateValue = new \DateTime('2020-01-01'); - $mindate = strtotime($minDateValue->format('Y-m-d 00:00:00')); - $maxDateValue = $minDateValue->modify('+6 year')->format('Y-m-d 00:00:00'); - $maxdate = strtotime($maxDateValue); - if (strtotime('now') < $mindate || strtotime('now') > $maxdate) { - self::forceSyncHour(); - sleep(3); - if (strtotime('now') < $mindate || strtotime('now') > $maxdate) { - log::add('core', 'error', __('La date du système est incorrecte (avant ' . $minDateValue . ' ou après ' . $maxDateValue . ') :', __FILE__) . ' ' . (new \DateTime())->format('Y-m-d H:i:s'), 'dateCheckFailed'); - return false; - } - } return true; } @@ -1252,8 +1240,8 @@ public static function cronDaily() { log::add('jeedom', 'error', log::exception($e)); } $disk_space = self::checkSpaceLeft(); - if($disk_space < 10){ - log::add('jeedom', 'error',__('Espace disque disponible faible : ',__FILE__).$disk_space.'%.'.__('Veuillez faire de la place (suppression de backup, de video/capture du plugin camera, d\'historique...)',__FILE__)); + if ($disk_space < 10) { + log::add('jeedom', 'error', __('Espace disque disponible faible : ', __FILE__) . $disk_space . '%.' . __('Veuillez faire de la place (suppression de backup, de video/capture du plugin camera, d\'historique...)', __FILE__)); } } @@ -1549,7 +1537,7 @@ public static function massReplace($_options = array(), $_eqlogics = array(), $_ if (count($_eqlogics) == 0 && count($_cmds) == 0) { throw new Exception('{{Aucun équipement ou commande à remplacer ou copier}}'); } - foreach (['copyEqProperties', 'hideEqs', 'copyCmdProperties', 'removeCmdHistory', 'copyCmdHistory','disableEqs'] as $key) { + foreach (['copyEqProperties', 'hideEqs', 'copyCmdProperties', 'removeCmdHistory', 'copyCmdHistory', 'disableEqs'] as $key) { if (!isset($_options[$key])) { $_options[$key] = false; } @@ -1636,7 +1624,7 @@ public static function massReplace($_options = array(), $_eqlogics = array(), $_ $targetEq->save(); $return['eqlogics'] += 1; } - } + } if ($_options['hideEqs'] == "true") { foreach ($_eqlogics as $_sourceId => $_targetId) { $sourceEq = eqLogic::byId($_sourceId); @@ -1747,7 +1735,7 @@ public static function checkSpaceLeft($_dir = null) { } public static function getTmpFolder($_plugin = '') { - if(isset(self::$cache['getTmpFolder::' . $_plugin])){ + if (isset(self::$cache['getTmpFolder::' . $_plugin])) { return self::$cache['getTmpFolder::' . $_plugin]; } $return = '/' . trim(config::byKey('folder::tmp'), '/'); @@ -1804,7 +1792,7 @@ public static function getHardwareName() { $result = 'Atlas'; } else if (strpos($hostname, 'Luna') !== false) { $result = 'Luna'; - } else if (file_exists('/proc/1/sched') && strpos(shell_exec('cat /proc/1/sched | head -n 1'),'systemd') === false){ + } else if (file_exists('/proc/1/sched') && strpos(shell_exec('cat /proc/1/sched | head -n 1'), 'systemd') === false) { $result = 'docker'; } config::save('hardware_name', $result); diff --git a/core/config/version b/core/config/version index 6cedcff630..4e298cc965 100644 --- a/core/config/version +++ b/core/config/version @@ -1 +1 @@ -4.5.2 +4.5.3 diff --git a/docs/fr_FR/changelog.md b/docs/fr_FR/changelog.md index a405427e77..0fc5abd4b6 100644 --- a/docs/fr_FR/changelog.md +++ b/docs/fr_FR/changelog.md @@ -1,7 +1,10 @@ # Changelog Jeedom V4.5 +# 4.5.3 + # 4.5.2 +- Mise à jour vivement recommandée qui corrige un bug sur la vérification de la date empêchant tout lancement de scénario ou de tâche planifiée. # 4.5.1 From 577a5a75d90e38fbf0114530b4126e2b06a8064b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc?= <1536036+zoic21@users.noreply.github.com> Date: Tue, 6 Jan 2026 15:34:26 +0100 Subject: [PATCH 045/128] Correction bug envoi backup market avec un caractere specifique dans le login --- core/repo/market.repo.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/core/repo/market.repo.php b/core/repo/market.repo.php index 97b7c08c33..5b51416be3 100644 --- a/core/repo/market.repo.php +++ b/core/repo/market.repo.php @@ -231,7 +231,7 @@ public static function objectInfo($_update) { /* * ***********************BACKUP*************************** */ public static function backup_createFolderIsNotExist() { - $request_http = new com_http(config::byKey('service::backup::url').'/webdav/'.config::byKey('market::username'),config::byKey('market::username'),config::byKey('market::password')); + $request_http = new com_http(config::byKey('service::backup::url').'/webdav/'.urlencode(config::byKey('market::username')),config::byKey('market::username'),config::byKey('market::password')); $request_http->setCURLOPT(array( CURLOPT_CUSTOMREQUEST => "PROPFIND" )); @@ -243,7 +243,8 @@ public static function backup_createFolderIsNotExist() { if($file->propstat->prop->getcontenttype){ continue; } - $folder = trim(trim(str_replace('http://backup.jeedom.com/webdav/'.config::byKey('market::username'),'',$file->href),'/')); + $folder = trim(trim(str_replace('http://backup.jeedom.com/webdav/'.urlencode(config::byKey('market::username')),'',$file->href),'/')); + $folder = trim(trim(str_replace('http://backup.jeedom.com/webdav/'.config::byKey('market::username'),'',$folder),'/')); if($folder == ''){ continue; } @@ -253,7 +254,7 @@ public static function backup_createFolderIsNotExist() { } } if (!$found) { - $request_http = new com_http(config::byKey('service::backup::url').'/webdav/'.config::byKey('market::username').'/'.rawurldecode(config::byKey('market::cloud::backup::name')),config::byKey('market::username'),config::byKey('market::password')); + $request_http = new com_http(config::byKey('service::backup::url').'/webdav/'.urlencode(config::byKey('market::username')).'/'.rawurldecode(config::byKey('market::cloud::backup::name')),config::byKey('market::username'),config::byKey('market::password')); $request_http->setCURLOPT(array( CURLOPT_CUSTOMREQUEST => "MKCOL" )); @@ -280,7 +281,7 @@ public static function backup_send($_path) { com_shell::execute('sudo chmod 777 -R /tmp/jeedom_gnupg'); $cmd = 'echo "' . config::byKey('market::cloud::backup::password') . '" | gpg --homedir /tmp/jeedom_gnupg --batch --yes --passphrase-fd 0 -c ' . $_path; com_shell::execute($cmd); - $cmd = "curl --user '".config::byKey('market::username').":".config::byKey('market::password')."' -T '".$_path . '.gpg'."' '".config::byKey('service::backup::url').'/webdav/'.config::byKey('market::username'). '/' . rawurldecode(config::byKey('market::cloud::backup::name'))."/'"; + $cmd = "curl --user '".config::byKey('market::username').":".config::byKey('market::password')."' -T '".$_path . '.gpg'."' '".config::byKey('service::backup::url').'/webdav/'.urlencode(config::byKey('market::username')). '/' . rawurldecode(config::byKey('market::cloud::backup::name'))."/'"; com_shell::execute($cmd); unlink($_path . '.gpg'); rrmdir('/tmp/jeedom_gnupg'); @@ -298,7 +299,7 @@ public static function backup_clean($_path) { $limit = 3700; self::backup_createFolderIsNotExist(); - $request_http = new com_http(config::byKey('service::backup::url').'/webdav/'.config::byKey('market::username'),config::byKey('market::username'),config::byKey('market::password')); + $request_http = new com_http(config::byKey('service::backup::url').'/webdav/'.urlencode(config::byKey('market::username')),config::byKey('market::username'),config::byKey('market::password')); $request_http->setCURLOPT(array( CURLOPT_CUSTOMREQUEST => "PROPFIND" )); @@ -353,7 +354,7 @@ public static function backup_list() { return array(); } self::backup_createFolderIsNotExist(); - $request_http = new com_http(config::byKey('service::backup::url').'/webdav/'.config::byKey('market::username'). '/' . rawurldecode(config::byKey('market::cloud::backup::name')),config::byKey('market::username'),config::byKey('market::password')); + $request_http = new com_http(config::byKey('service::backup::url').'/webdav/'.urlencode(config::byKey('market::username')). '/' . rawurldecode(config::byKey('market::cloud::backup::name')),config::byKey('market::username'),config::byKey('market::password')); $request_http->setCURLOPT(array( CURLOPT_CUSTOMREQUEST => "PROPFIND" )); From 84afa13459ae2136a4e87743aee507647dfe3212 Mon Sep 17 00:00:00 2001 From: "Julien C." Date: Wed, 7 Jan 2026 14:26:44 +0100 Subject: [PATCH 046/128] Use floatval instead of intval for history values Replaced intval with floatval when rounding historical average, min, and max values to ensure decimal precision is preserved. This change affects the calculation and display of historical statistics in the cmd class. --- core/class/cmd.class.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/class/cmd.class.php b/core/class/cmd.class.php index c667a6fc09..4dfc3ff7fa 100644 --- a/core/class/cmd.class.php +++ b/core/class/cmd.class.php @@ -1757,13 +1757,13 @@ public function toHtml($_version = 'dashboard', $_options = '') { $replace['#hide_history#'] = ''; $historyStatistique = $this->getStatistique($startHist, date('Y-m-d H:i:s')); if ($historyStatistique['avg'] == 0 && $historyStatistique['min'] == 0 && $historyStatistique['max'] == 0) { - $replace['#averageHistoryValue#'] = round(intval($replace['#state#']), 1); - $replace['#minHistoryValue#'] = round(intval($replace['#state#']), 1); - $replace['#maxHistoryValue#'] = round(intval($replace['#state#']), 1); + $replace['#averageHistoryValue#'] = round(floatval($replace['#state#']), 1); + $replace['#minHistoryValue#'] = round(floatval($replace['#state#']), 1); + $replace['#maxHistoryValue#'] = round(floatval($replace['#state#']), 1); } else { - $replace['#averageHistoryValue#'] = round(intval($historyStatistique['avg']), 1); - $replace['#minHistoryValue#'] = round(intval($historyStatistique['min']), 1); - $replace['#maxHistoryValue#'] = round(intval($historyStatistique['max']), 1); + $replace['#averageHistoryValue#'] = round(floatval($historyStatistique['avg']), 1); + $replace['#minHistoryValue#'] = round(floatval($historyStatistique['min']), 1); + $replace['#maxHistoryValue#'] = round(floatval($historyStatistique['max']), 1); } $startHist = date('Y-m-d H:i:s', strtotime(date('Y-m-d H:i:s') . ' -' . config::byKey('historyCalculTendance') . ' hour')); $tendance = $this->getTendance($startHist, date('Y-m-d H:i:s')); From a06a7cfb03db551cbe3562bdf694940a0ed03c5e Mon Sep 17 00:00:00 2001 From: Salvialf Date: Mon, 12 Jan 2026 14:05:28 +0100 Subject: [PATCH 047/128] code format --- core/class/cache.class.php | 141 ++++++++++++++++++------------------- 1 file changed, 69 insertions(+), 72 deletions(-) diff --git a/core/class/cache.class.php b/core/class/cache.class.php index 277a5293a4..75e8beef7b 100644 --- a/core/class/cache.class.php +++ b/core/class/cache.class.php @@ -30,17 +30,17 @@ class cache { /* * ***********************Methode static*************************** */ - public static function getEngine(){ - if(self::$_engine != null){ + public static function getEngine() { + if (self::$_engine != null) { return self::$_engine; } self::$_engine = config::byKey('cache::engine'); - if(!class_exists(self::$_engine)){ - config::save('cache::engine','FileCache'); + if (!class_exists(self::$_engine)) { + config::save('cache::engine', 'FileCache'); self::$_engine = 'FileCache'; } - if(method_exists(self::$_engine,'isOk') && !self::$_engine::isOk()){ - config::save('cache::engine','FileCache'); + if (method_exists(self::$_engine, 'isOk') && !self::$_engine::isOk()) { + config::save('cache::engine', 'FileCache'); self::$_engine = 'FileCache'; } return self::$_engine; @@ -51,7 +51,7 @@ public static function set($_key, $_value, $_lifetime = 0) { ->setKey($_key) ->setValue($_value) ->setLifetime($_lifetime) - ->save(); + ->save(); } public static function delete($_key) { @@ -76,7 +76,7 @@ public static function byKey($_key) { return $cache; } - public static function exist($_key){ + public static function exist($_key) { return (self::byKey($_key)->getValue(null) !== null); } @@ -85,31 +85,31 @@ public static function flush() { } public static function persist() { - if(method_exists(self::getEngine(),'persist')){ + if (method_exists(self::getEngine(), 'persist')) { self::getEngine()::persist(); } } public static function isPersistOk(): bool { - if(method_exists(self::getEngine(),'isPersistOk')){ + if (method_exists(self::getEngine(), 'isPersistOk')) { return self::getEngine()::isPersistOk(); } return true; } public static function restore() { - if(method_exists(self::getEngine(),'restore')){ + if (method_exists(self::getEngine(), 'restore')) { self::getEngine()::restore(); } } public static function clean() { - if(method_exists(self::getEngine(),'clean')){ + if (method_exists(self::getEngine(), 'clean')) { self::getEngine()::clean(); } $caches = self::getEngine()::all(); foreach ($caches as $cache) { - if(!is_object($cache)){ + if (!is_object($cache)) { continue; } $matches = null; @@ -188,7 +188,7 @@ public function setLifetime($_lifetime): self { } public function getDatetime() { - return date('Y-m-d H:i:s',(int) $this->timestamp); + return date('Y-m-d H:i:s', (int) $this->timestamp); } public function setDatetime($_datetime): self { @@ -196,11 +196,11 @@ public function setDatetime($_datetime): self { return $this; } - public function getTimestamp(){ + public function getTimestamp() { return $this->timestamp; } - public function setTimestamp($_timestamp){ + public function setTimestamp($_timestamp) { $this->timestamp = $_timestamp; return $this; } @@ -209,73 +209,72 @@ public function setTimestamp($_timestamp){ class MariadbCache { - public static function all(){ + public static function all() { $sql = 'SELECT `key`,`timestamp`,`value`,`lifetime` FROM cache'; - $results = DB::Prepare($sql,array(), DB::FETCH_TYPE_ALL, PDO::FETCH_CLASS,'cache'); + $results = DB::Prepare($sql, array(), DB::FETCH_TYPE_ALL, PDO::FETCH_CLASS, 'cache'); foreach ($results as $cache) { $cache->setValue(unserialize($cache->getValue())); } return $results; } - public static function clean(){ - $sql = 'DELETE + public static function clean() { + $sql = 'DELETE FROM cache WHERE `lifetime` > 0 AND (`timestamp`+`lifetime`) < UNIX_TIMESTAMP()'; - return DB::Prepare($sql,array(), DB::FETCH_TYPE_ROW, PDO::FETCH_CLASS); + return DB::Prepare($sql, array(), DB::FETCH_TYPE_ROW, PDO::FETCH_CLASS); } - public static function fetch($_key){ + public static function fetch($_key) { $sql = 'SELECT `key`,`timestamp`,`value`,`lifetime` FROM cache WHERE `key`=:key'; - $cache = DB::Prepare($sql,array('key' => $_key), DB::FETCH_TYPE_ROW, PDO::FETCH_CLASS,'cache'); - if($cache === false || !is_object($cache)){ + $cache = DB::Prepare($sql, array('key' => $_key), DB::FETCH_TYPE_ROW, PDO::FETCH_CLASS, 'cache'); + if ($cache === false || !is_object($cache)) { return null; } - if($cache->getLifetime() > 0 && ($cache->getTimestamp() + $cache->getLifetime()) < strtotime('now')){ + if ($cache->getLifetime() > 0 && ($cache->getTimestamp() + $cache->getLifetime()) < strtotime('now')) { return null; } $cache->setValue(unserialize($cache->getValue())); return $cache; } - public static function delete($_key){ - $sql = 'DELETE + public static function delete($_key) { + $sql = 'DELETE FROM cache WHERE `key`=:key'; - return DB::Prepare($sql,array('key' => $_key), DB::FETCH_TYPE_ROW); + return DB::Prepare($sql, array('key' => $_key), DB::FETCH_TYPE_ROW); } - public static function deleteAll(){ - return DB::Prepare('TRUNCATE cache',array(), DB::FETCH_TYPE_ROW); + public static function deleteAll() { + return DB::Prepare('TRUNCATE cache', array(), DB::FETCH_TYPE_ROW); } - public static function save($_cache){ + public static function save($_cache) { $value = array( 'key' => $_cache->getKey(), 'value' => serialize($_cache->getValue()), - 'lifetime' =>$_cache->getLifetime(), + 'lifetime' => $_cache->getLifetime(), 'timestamp' => $_cache->getTimestamp() ); $sql = 'REPLACE INTO cache SET `key`=:key, `value`=:value,`timestamp`=:timestamp,`lifetime`=:lifetime'; - return DB::Prepare($sql,$value, DB::FETCH_TYPE_ROW); + return DB::Prepare($sql, $value, DB::FETCH_TYPE_ROW); } - } class RedisCache { private static $connection = null; - public static function isOk(){ + public static function isOk() { return class_exists('redis'); } - public static function getConnection(){ - if(static::$connection !== null){ + public static function getConnection() { + if (static::$connection !== null) { return static::$connection; } $redis = new Redis(); @@ -284,7 +283,7 @@ public static function getConnection(){ return static::$connection; } - public static function all(){ + public static function all() { $return = array(); $keys = self::getConnection()->keys('*'); foreach ($keys as $key) { @@ -293,81 +292,80 @@ public static function all(){ return $return; } - public static function fetch($_key){ + public static function fetch($_key) { $data = self::getConnection()->get($_key); - if($data === false){ + if ($data === false) { return null; } return @unserialize($data); } - public static function delete($_key){ + public static function delete($_key) { self::getConnection()->del($_key); } - public static function deleteAll(){ + public static function deleteAll() { return self::getConnection()->flushDb(); } - public static function save($_cache){ - if($_cache->getLifetime() > 0){ - self::getConnection()->set($_cache->getKey(),serialize($_cache), $_cache->getLifetime()); - }else{ - self::getConnection()->set($_cache->getKey(),serialize($_cache)); + public static function save($_cache) { + if ($_cache->getLifetime() > 0) { + self::getConnection()->set($_cache->getKey(), serialize($_cache), $_cache->getLifetime()); + } else { + self::getConnection()->set($_cache->getKey(), serialize($_cache)); } } - } class FileCache { - public static function all(){ + public static function all() { $return = array(); - foreach (ls(jeedom::getTmpFolder('cache'), '*',false,array('files')) as $file) { + foreach (ls(jeedom::getTmpFolder('cache'), '*', false, array('files')) as $file) { $return[] = self::fetch(base64_decode($file)); } return $return; } - public static function clean(){ - foreach (ls(jeedom::getTmpFolder('cache'), '*',false,array('files')) as $file) { - $cache = unserialize(file_get_contents(jeedom::getTmpFolder('cache').'/'.$file)); - if(!is_object($cache)){ - unlink(jeedom::getTmpFolder('cache').'/'.$file); + public static function clean() { + foreach (ls(jeedom::getTmpFolder('cache'), '*', false, array('files')) as $file) { + $cache = unserialize(file_get_contents(jeedom::getTmpFolder('cache') . '/' . $file)); + if (!is_object($cache)) { + unlink(jeedom::getTmpFolder('cache') . '/' . $file); continue; } - if($cache->getLifetime() > 0 && ($cache->getTimestamp() + $cache->getLifetime()) < strtotime('now')){ - unlink(jeedom::getTmpFolder('cache').'/'.$file); + if ($cache->getLifetime() > 0 && ($cache->getTimestamp() + $cache->getLifetime()) < strtotime('now')) { + unlink(jeedom::getTmpFolder('cache') . '/' . $file); } } } - public static function fetch($_key){ - $data = @file_get_contents(jeedom::getTmpFolder('cache').'/'.base64_encode($_key)); - if($data === false){ - return null; - } - $cache = unserialize($data); - if(!is_object($cache)){ + public static function fetch($_key) { + $data = @file_get_contents(jeedom::getTmpFolder('cache') . '/' . base64_encode($_key)); + if ($data === false) { return null; } - if($cache->getLifetime() > 0 && ($cache->getTimestamp() + $cache->getLifetime()) < strtotime('now')){ + $cache = unserialize($data); + if (!is_object($cache)) { + return null; + } + if ($cache->getLifetime() > 0 && ($cache->getTimestamp() + $cache->getLifetime()) < strtotime('now')) { self::delete($_key); return null; } return $cache; } - public static function delete($_key){ - @unlink(jeedom::getTmpFolder('cache').'/'.base64_encode($_key)); + public static function delete($_key) { + @unlink(jeedom::getTmpFolder('cache') . '/' . base64_encode($_key)); } - public static function deleteAll(){ - return shell_exec(system::getCmdSudo().' rm -rf '.jeedom::getTmpFolder('cache')); + public static function deleteAll() { + return shell_exec(system::getCmdSudo() . ' rm -rf ' . jeedom::getTmpFolder('cache')); } - public static function save($_cache){ - file_put_contents(jeedom::getTmpFolder('cache').'/'.base64_encode($_cache->getKey()),serialize($_cache)); + public static function save($_cache) { + file_put_contents(jeedom::getTmpFolder('cache') . '/' . base64_encode($_cache->getKey()), serialize($_cache)); } public static function persist() { @@ -410,5 +408,4 @@ public static function restore() { $cmd .= 'chmod -R 777 ' . $cache_dir . ' 2>&1 > /dev/null;'; com_shell::execute($cmd); } - } From 82da65fad466eae40f461ba8cb39df588e61a7cb Mon Sep 17 00:00:00 2001 From: Salvialf Date: Mon, 12 Jan 2026 14:07:52 +0100 Subject: [PATCH 048/128] improve cache persist As same as #3184 without irrelevant documentation changes --- core/class/cache.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/class/cache.class.php b/core/class/cache.class.php index 75e8beef7b..88ca33f7be 100644 --- a/core/class/cache.class.php +++ b/core/class/cache.class.php @@ -372,7 +372,7 @@ public static function persist() { $cache_dir = jeedom::getTmpFolder('cache'); try { $cmd = system::getCmdSudo() . 'rm -rf ' . __DIR__ . '/../../cache.tar.gz;cd ' . $cache_dir . ';'; - $cmd .= system::getCmdSudo() . 'tar cfz ' . __DIR__ . '/../../cache.tar.gz * 2>&1 > /dev/null;'; + $cmd .= system::getCmdSudo() . 'tar cfz ' . __DIR__ . '/../../cache.tar.gz . 2>&1 > /dev/null;'; $cmd .= system::getCmdSudo() . 'chmod 774 ' . __DIR__ . '/../../cache.tar.gz;'; $cmd .= system::getCmdSudo() . 'chown ' . system::get('www-uid') . ':' . system::get('www-gid') . ' ' . __DIR__ . '/../../cache.tar.gz;'; $cmd .= system::getCmdSudo() . 'chown -R ' . system::get('www-uid') . ':' . system::get('www-gid') . ' ' . $cache_dir . ';'; From 78134ef147585bfdf3556a24efc705484106acd1 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Mon, 12 Jan 2026 14:34:29 +0100 Subject: [PATCH 049/128] keep same method names as jeeApi --- core/api/proApi.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/api/proApi.php b/core/api/proApi.php index 31e91712b9..4835932406 100644 --- a/core/api/proApi.php +++ b/core/api/proApi.php @@ -586,12 +586,12 @@ /* * ************************System*************************** */ - if ($jsonrpc->getMethod() == 'system::halt') { + if ($jsonrpc->getMethod() == 'jeedom::halt') { jeedom::haltSystem(); $jsonrpc->makeSuccess('ok'); } - if ($jsonrpc->getMethod() == 'system::reboot') { + if ($jsonrpc->getMethod() == 'jeedom::reboot') { jeedom::rebootSystem(); $jsonrpc->makeSuccess('ok'); } From 06ca825811b7a0a6462c8703db71576268c0cda3 Mon Sep 17 00:00:00 2001 From: "Julien C." Date: Tue, 30 Dec 2025 16:51:25 +0100 Subject: [PATCH 050/128] Update market.display.repo.php --- core/repo/market.display.repo.php | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/core/repo/market.display.repo.php b/core/repo/market.display.repo.php index cb0aa4a257..551e03f3e6 100644 --- a/core/repo/market.display.repo.php +++ b/core/repo/market.display.repo.php @@ -114,21 +114,25 @@ ?>

getCertification() == 'Premium') { - echo '{{Nous Contacter}}'; + echo '{{Nous Contacter}}'; } else { - if ($market->getCost() > 0) { - if ($market->getPurchase() == 1 && isset($purchase_info['user_id']) && is_numeric($purchase_info['user_id'])) { - echo '{{Plugin deja acheté et/ou inclus dans votre service Pack}}'; - }else{ - if ($market->getCost() != $market->getRealCost()) { + if (isset($purchase_info['user_id']) && is_numeric($purchase_info['user_id']) && $market->getPurchase() == 1) { + echo '{{Plugin deja acheté et/ou inclus dans votre service Pack}}'; + } else { + if ($market->getCost() > 0) { + if ($market->getCost() != $market->getRealCost()) { echo '' . number_format($market->getRealCost(), 2) . ' € '; - } - echo '' . number_format($market->getCost(), 2) . ' € TTC'; - } - } else { - echo '{{Gratuit}}'; - } + } + echo '' . number_format($market->getCost(), 2) . ' € TTC'; + } else { + echo '{{Gratuit}}'; + } + } } ?>
From 082c28947cd5a9d55399e7249a0dedb3b6a81b79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc?= <1536036+zoic21@users.noreply.github.com> Date: Tue, 6 Jan 2026 15:34:26 +0100 Subject: [PATCH 051/128] Correction bug envoi backup market avec un caractere specifique dans le login --- core/repo/market.repo.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/core/repo/market.repo.php b/core/repo/market.repo.php index 97b7c08c33..5b51416be3 100644 --- a/core/repo/market.repo.php +++ b/core/repo/market.repo.php @@ -231,7 +231,7 @@ public static function objectInfo($_update) { /* * ***********************BACKUP*************************** */ public static function backup_createFolderIsNotExist() { - $request_http = new com_http(config::byKey('service::backup::url').'/webdav/'.config::byKey('market::username'),config::byKey('market::username'),config::byKey('market::password')); + $request_http = new com_http(config::byKey('service::backup::url').'/webdav/'.urlencode(config::byKey('market::username')),config::byKey('market::username'),config::byKey('market::password')); $request_http->setCURLOPT(array( CURLOPT_CUSTOMREQUEST => "PROPFIND" )); @@ -243,7 +243,8 @@ public static function backup_createFolderIsNotExist() { if($file->propstat->prop->getcontenttype){ continue; } - $folder = trim(trim(str_replace('http://backup.jeedom.com/webdav/'.config::byKey('market::username'),'',$file->href),'/')); + $folder = trim(trim(str_replace('http://backup.jeedom.com/webdav/'.urlencode(config::byKey('market::username')),'',$file->href),'/')); + $folder = trim(trim(str_replace('http://backup.jeedom.com/webdav/'.config::byKey('market::username'),'',$folder),'/')); if($folder == ''){ continue; } @@ -253,7 +254,7 @@ public static function backup_createFolderIsNotExist() { } } if (!$found) { - $request_http = new com_http(config::byKey('service::backup::url').'/webdav/'.config::byKey('market::username').'/'.rawurldecode(config::byKey('market::cloud::backup::name')),config::byKey('market::username'),config::byKey('market::password')); + $request_http = new com_http(config::byKey('service::backup::url').'/webdav/'.urlencode(config::byKey('market::username')).'/'.rawurldecode(config::byKey('market::cloud::backup::name')),config::byKey('market::username'),config::byKey('market::password')); $request_http->setCURLOPT(array( CURLOPT_CUSTOMREQUEST => "MKCOL" )); @@ -280,7 +281,7 @@ public static function backup_send($_path) { com_shell::execute('sudo chmod 777 -R /tmp/jeedom_gnupg'); $cmd = 'echo "' . config::byKey('market::cloud::backup::password') . '" | gpg --homedir /tmp/jeedom_gnupg --batch --yes --passphrase-fd 0 -c ' . $_path; com_shell::execute($cmd); - $cmd = "curl --user '".config::byKey('market::username').":".config::byKey('market::password')."' -T '".$_path . '.gpg'."' '".config::byKey('service::backup::url').'/webdav/'.config::byKey('market::username'). '/' . rawurldecode(config::byKey('market::cloud::backup::name'))."/'"; + $cmd = "curl --user '".config::byKey('market::username').":".config::byKey('market::password')."' -T '".$_path . '.gpg'."' '".config::byKey('service::backup::url').'/webdav/'.urlencode(config::byKey('market::username')). '/' . rawurldecode(config::byKey('market::cloud::backup::name'))."/'"; com_shell::execute($cmd); unlink($_path . '.gpg'); rrmdir('/tmp/jeedom_gnupg'); @@ -298,7 +299,7 @@ public static function backup_clean($_path) { $limit = 3700; self::backup_createFolderIsNotExist(); - $request_http = new com_http(config::byKey('service::backup::url').'/webdav/'.config::byKey('market::username'),config::byKey('market::username'),config::byKey('market::password')); + $request_http = new com_http(config::byKey('service::backup::url').'/webdav/'.urlencode(config::byKey('market::username')),config::byKey('market::username'),config::byKey('market::password')); $request_http->setCURLOPT(array( CURLOPT_CUSTOMREQUEST => "PROPFIND" )); @@ -353,7 +354,7 @@ public static function backup_list() { return array(); } self::backup_createFolderIsNotExist(); - $request_http = new com_http(config::byKey('service::backup::url').'/webdav/'.config::byKey('market::username'). '/' . rawurldecode(config::byKey('market::cloud::backup::name')),config::byKey('market::username'),config::byKey('market::password')); + $request_http = new com_http(config::byKey('service::backup::url').'/webdav/'.urlencode(config::byKey('market::username')). '/' . rawurldecode(config::byKey('market::cloud::backup::name')),config::byKey('market::username'),config::byKey('market::password')); $request_http->setCURLOPT(array( CURLOPT_CUSTOMREQUEST => "PROPFIND" )); From 60fa2be1ea0d464187eccdf1d2d0a5d1986bf789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc?= <1536036+zoic21@users.noreply.github.com> Date: Fri, 30 Jan 2026 11:47:14 +0100 Subject: [PATCH 052/128] Update log.class.php --- core/class/log.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/class/log.class.php b/core/class/log.class.php index 4660c278ff..cb634c2dcc 100644 --- a/core/class/log.class.php +++ b/core/class/log.class.php @@ -108,7 +108,7 @@ public static function convertLogLevel($_level = 100) { * @param string $_type message type (info, debug, warning, danger) * @param string $_message message added into log */ - public static function add($_log, $_type, $_message, $_logicalId = '') { + public static function add(string $_log,string $_type, string $_message,string $_logicalId = '') { if (trim($_message) == '') { return; } From 190a2b373184e33d375d07d08a1e6dd8f1471292 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Fri, 30 Jan 2026 14:51:32 +0100 Subject: [PATCH 053/128] update changelog --- docs/fr_FR/changelog.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/fr_FR/changelog.md b/docs/fr_FR/changelog.md index 61ac0bc29f..4e3bc468df 100644 --- a/docs/fr_FR/changelog.md +++ b/docs/fr_FR/changelog.md @@ -2,9 +2,11 @@ # 4.5.3 +- Diverses corrections de bugs et optimisations + # 4.5.2 -- Mise à jour vivement recommandée qui corrige un bug sur la vérification de la date empêchant tout lancement de scénario ou de tâche planifiée. +- Mise à jour indispensable qui corrige un bug sur la vérification de la date empêchant tout lancement de scénario ou de tâche planifiée. # 4.5.1 From c5cf5819530ffc6e2b69cb7e913d61f92ae4f2e5 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Fri, 30 Jan 2026 16:50:34 +0100 Subject: [PATCH 054/128] Exclude system image files from install/update --- install/backup.php | 1 + 1 file changed, 1 insertion(+) diff --git a/install/backup.php b/install/backup.php index 00efd4f7d0..96c83baa1e 100644 --- a/install/backup.php +++ b/install/backup.php @@ -146,6 +146,7 @@ 'python_venv', 'resources/venv', '/vendor', + 'install/update/*.img.gz', config::byKey('backup::path'), ); From 9e6dd712fc1eb913865524c49616cda6b14e7208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc?= <1536036+zoic21@users.noreply.github.com> Date: Fri, 30 Jan 2026 16:55:42 +0100 Subject: [PATCH 055/128] Update core/class/jeedom.class.php Co-authored-by: kwizer15 --- core/class/jeedom.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/class/jeedom.class.php b/core/class/jeedom.class.php index 23cb83b270..cdf2de2baa 100644 --- a/core/class/jeedom.class.php +++ b/core/class/jeedom.class.php @@ -1295,7 +1295,7 @@ public static function cronHourly() { } try{ log::chunk('', True); - }catch (Exception $e) { + }catch (Throwable $e) { log::add('jeedom', 'error', $e->getMessage()); } catch (Error $e) { log::add('jeedom', 'error', $e->getMessage()); From 08c80b7520b47bc6f314cccb0dd7fa92d3bd51b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc?= <1536036+zoic21@users.noreply.github.com> Date: Wed, 4 Feb 2026 14:31:01 +0100 Subject: [PATCH 056/128] Update market.repo.php Cloud backup auto clean itself no need to do that by jeedom --- core/repo/market.repo.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/repo/market.repo.php b/core/repo/market.repo.php index 5b51416be3..a8fb0b0492 100644 --- a/core/repo/market.repo.php +++ b/core/repo/market.repo.php @@ -272,7 +272,7 @@ public static function backup_send($_path) { if (config::byKey('market::cloud::backup::password') != config::byKey('market::cloud::backup::password_confirmation')) { throw new Exception(__('Le mot de passe du backup cloud n\'est pas identique à la confirmation', __FILE__)); } - self::backup_clean($_path); + //self::backup_clean($_path); self::backup_createFolderIsNotExist(); try { if (!file_exists('/tmp/jeedom_gnupg')) { From 32f7579d10d29ed9c333b60393d74c04cab85064 Mon Sep 17 00:00:00 2001 From: aofc Date: Sat, 21 Feb 2026 15:48:29 -0500 Subject: [PATCH 057/128] Fix randText to evaluate string and return random value --- core/class/scenarioExpression.class.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/class/scenarioExpression.class.php b/core/class/scenarioExpression.class.php index 952352df20..8948708d39 100644 --- a/core/class/scenarioExpression.class.php +++ b/core/class/scenarioExpression.class.php @@ -213,18 +213,18 @@ public static function randText($_sValue) { $_sValue = self::setTags($_sValue); $_aValue = explode(";", $_sValue); try { - $result = evaluate($_aValue); + $result = evaluate($_sValue); if (is_string($result)) { $result = $_aValue; } } catch (Exception $e) { $result = $_aValue; } - if (is_array($_aValue)) { - $nbr = mt_rand(0, count($_aValue) - 1); - return $_aValue[$nbr]; + if (is_array($result)) { + $nbr = mt_rand(0, count($result) - 1); + return $result[$nbr]; } else { - return $_aValue; + return $result; } } From 41a773e2daa296a1ef4bef51e4d6d1149fbacc87 Mon Sep 17 00:00:00 2001 From: Mips2648 Date: Sun, 22 Feb 2026 13:13:05 +0100 Subject: [PATCH 058/128] fix: Merge setTags method to append tags instead of replacing them --- core/class/scenario.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/class/scenario.class.php b/core/class/scenario.class.php index 057e41c8a4..1670949d4e 100644 --- a/core/class/scenario.class.php +++ b/core/class/scenario.class.php @@ -2028,7 +2028,7 @@ public function getTags() { * @return $this */ public function setTags($_tags) { - $this->_tags = $_tags; + $this->_tags = array_merge($this->_tags, $_tags); return $this; } From 2fa0cd850e934f86f04379e75642e9bfa008cf7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc?= <1536036+zoic21@users.noreply.github.com> Date: Mon, 23 Feb 2026 10:12:46 +0100 Subject: [PATCH 059/128] Revert "Try to ignore case and accent for function fromHumanReadable" --- core/class/cmd.class.php | 10 +++++----- core/class/scenario.class.php | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/core/class/cmd.class.php b/core/class/cmd.class.php index 66b1475d32..4dfc3ff7fa 100644 --- a/core/class/cmd.class.php +++ b/core/class/cmd.class.php @@ -538,8 +538,8 @@ public static function byObjectNameEqLogicNameCmdName($_object_name, $_eqLogic_n $sql = 'SELECT ' . DB::buildField(__CLASS__, 'c') . ' FROM cmd c INNER JOIN eqLogic el ON c.eqLogic_id=el.id - WHERE c.name COLLATE utf8mb4_0900_ai_ci =:cmd_name - AND el.name COLLATE utf8mb4_0900_ai_ci =:eqLogic_name + WHERE c.name=:cmd_name + AND el.name=:eqLogic_name AND el.object_id IS NULL'; } else { $values['object_name'] = $_object_name; @@ -547,9 +547,9 @@ public static function byObjectNameEqLogicNameCmdName($_object_name, $_eqLogic_n FROM cmd c INNER JOIN eqLogic el ON c.eqLogic_id=el.id INNER JOIN object ob ON el.object_id=ob.id - WHERE c.name COLLATE utf8mb4_0900_ai_ci =:cmd_name - AND el.name COLLATE utf8mb4_0900_ai_ci =:eqLogic_name - AND ob.name COLLATE utf8mb4_0900_ai_ci = :object_name'; + WHERE c.name=:cmd_name + AND el.name=:eqLogic_name + AND ob.name=:object_name'; } return self::cast(DB::Prepare($sql, $values, DB::FETCH_TYPE_ROW, PDO::FETCH_CLASS, __CLASS__)); } diff --git a/core/class/scenario.class.php b/core/class/scenario.class.php index 057e41c8a4..96e81124c7 100644 --- a/core/class/scenario.class.php +++ b/core/class/scenario.class.php @@ -606,14 +606,14 @@ public static function byObjectNameGroupNameScenarioName($_object_name, $_group_ if ($_group_name == __('Aucun', __FILE__)) { $sql = 'SELECT ' . DB::buildField(__CLASS__, 's') . ' FROM scenario s - WHERE s.name COLLATE utf8mb4_0900_ai_ci =:scenario_name + WHERE s.name=:scenario_name AND (`group` IS NULL OR `group`="" OR `group`="Aucun" OR `group`="None") AND s.object_id IS NULL'; } else { $values['group_name'] = $_group_name; $sql = 'SELECT ' . DB::buildField(__CLASS__, 's') . ' FROM scenario s - WHERE s.name COLLATE utf8mb4_0900_ai_ci =:scenario_name + WHERE s.name=:scenario_name AND s.object_id IS NULL AND `group`=:group_name'; } @@ -623,17 +623,17 @@ public static function byObjectNameGroupNameScenarioName($_object_name, $_group_ $sql = 'SELECT ' . DB::buildField(__CLASS__, 's') . ' FROM scenario s INNER JOIN object ob ON s.object_id=ob.id - WHERE s.name COLLATE utf8mb4_0900_ai_ci =:scenario_name - AND ob.name COLLATE utf8mb4_0900_ai_ci =:object_name + WHERE s.name=:scenario_name + AND ob.name=:object_name AND (`group` IS NULL OR `group`="" OR `group`="Aucun" OR `group`="None")'; } else { $values['group_name'] = $_group_name; $sql = 'SELECT ' . DB::buildField(__CLASS__, 's') . ' FROM scenario s INNER JOIN object ob ON s.object_id=ob.id - WHERE s.name COLLATE utf8mb4_0900_ai_ci =:scenario_name - AND ob.name COLLATE utf8mb4_0900_ai_ci =:object_name - AND `group` COLLATE utf8mb4_0900_ai_ci =:group_name'; + WHERE s.name=:scenario_name + AND ob.name=:object_name + AND `group`=:group_name'; } } return DB::Prepare($sql, $values, DB::FETCH_TYPE_ROW, PDO::FETCH_CLASS, __CLASS__); From aa83a2bd73184b5fb31cb7bc46facdf00a266b22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc?= <1536036+zoic21@users.noreply.github.com> Date: Wed, 25 Feb 2026 12:04:40 +0100 Subject: [PATCH 060/128] Update market.repo.php Improve send market backup --- core/repo/market.repo.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/core/repo/market.repo.php b/core/repo/market.repo.php index 5b51416be3..7dfa0cbedd 100644 --- a/core/repo/market.repo.php +++ b/core/repo/market.repo.php @@ -281,8 +281,13 @@ public static function backup_send($_path) { com_shell::execute('sudo chmod 777 -R /tmp/jeedom_gnupg'); $cmd = 'echo "' . config::byKey('market::cloud::backup::password') . '" | gpg --homedir /tmp/jeedom_gnupg --batch --yes --passphrase-fd 0 -c ' . $_path; com_shell::execute($cmd); - $cmd = "curl --user '".config::byKey('market::username').":".config::byKey('market::password')."' -T '".$_path . '.gpg'."' '".config::byKey('service::backup::url').'/webdav/'.urlencode(config::byKey('market::username')). '/' . rawurldecode(config::byKey('market::cloud::backup::name'))."/'"; - com_shell::execute($cmd); + $cmd = "curl --retry 5 --retry-delay 3 --retry-max-time 60 --retry-connrefused --keepalive-time 30 --user '".config::byKey('market::username').":".config::byKey('market::password')."' -T '".$_path . '.gpg'."' '".config::byKey('service::backup::url').'/webdav/'.urlencode(config::byKey('market::username')). '/' . rawurldecode(config::byKey('market::cloud::backup::name'))."/'"; + try { + com_shell::execute($cmd); + } catch (\Exception $e) { + $cmd = "curl --http1.1 --retry 5 --retry-delay 3 --retry-max-time 60 --retry-connrefused --keepalive-time 30 --user '".config::byKey('market::username').":".config::byKey('market::password')."' -T '".$_path . '.gpg'."' '".config::byKey('service::backup::url').'/webdav/'.urlencode(config::byKey('market::username')). '/' . rawurldecode(config::byKey('market::cloud::backup::name'))."/'"; + com_shell::execute($cmd); + } unlink($_path . '.gpg'); rrmdir('/tmp/jeedom_gnupg'); } catch (\Exception $e) { From 34a16045a9818d99d53ea4321b79bb7284714436 Mon Sep 17 00:00:00 2001 From: Michel F <80367602+MrWaloo@users.noreply.github.com> Date: Wed, 4 Mar 2026 23:46:27 +0100 Subject: [PATCH 061/128] Suppression de la double suppression des NULL dans les tables d'historiques MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Devant l'engouement suite au message sur le community qui a déchainé les foules et a reçu plein de réponses, je me suis senti obligé de faire cette PR. --- core/class/history.class.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/class/history.class.php b/core/class/history.class.php index 9c9e4dcd6b..3b4c0d4203 100644 --- a/core/class/history.class.php +++ b/core/class/history.class.php @@ -226,10 +226,6 @@ public static function archive() { DB::Prepare($sql, array()); $sql = 'DELETE FROM historyArch WHERE `value` IS NULL'; DB::Prepare($sql, array()); - $sql = 'DELETE FROM history WHERE `value` IS NULL'; - DB::Prepare($sql, array()); - $sql = 'DELETE FROM historyArch WHERE `value` IS NULL'; - DB::Prepare($sql, array()); if (config::byKey('historyArchivePackage') < 1) { config::save('historyArchivePackage', 1); } From 77da84c7de8864d13a702b714b5d6a3822e7029b Mon Sep 17 00:00:00 2001 From: Mips2648 Date: Sun, 8 Mar 2026 10:09:09 +0100 Subject: [PATCH 062/128] Update Debian version check to support major versions 11 and 12 --- core/class/jeedom.class.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/core/class/jeedom.class.php b/core/class/jeedom.class.php index 224bfe5606..33eb1dffec 100644 --- a/core/class/jeedom.class.php +++ b/core/class/jeedom.class.php @@ -294,17 +294,20 @@ public static function health() { $state = false; } else { $version = trim(strtolower(file_get_contents('/etc/debian_version'))); - if (version_compare($version, '8', '<')) { - if (strpos($version, 'jessie') === false && strpos($version, 'stretch') === false) { + $majorVersion = intval($version); + if ($majorVersion > 0) { + if ($majorVersion < 11 || $majorVersion > 12) { $state = false; } + } else if (strpos($version, 'bullseye') === false && strpos($version, 'bookworm') === false) { + $state = false; } } $return[] = array( 'name' => __('Version OS', __FILE__), 'state' => $state, 'result' => ($state) ? $uname . ' [' . $version . ']' : $uname, - 'comment' => ($state) ? '' : __('Vous n\'êtes pas sur un OS officiellement supporté par l\'équipe Jeedom (toute demande de support pourra donc être refusée). Les OS officiellement supportés sont Debian Strech et Debian Buster', __FILE__), + 'comment' => ($state) ? '' : __('Vous n\'êtes pas sur un OS officiellement supporté par l\'équipe Jeedom (toute demande de support pourra donc être refusée). Les OS officiellement supportés sont consultables dans la documentation officielle.', __FILE__), ); $version = DB::Prepare('select version()', array(), DB::FETCH_TYPE_ROW); From a6ce493eb859c134d15f8c5b2372859bc3928da7 Mon Sep 17 00:00:00 2001 From: Mips2648 Date: Sun, 8 Mar 2026 10:09:35 +0100 Subject: [PATCH 063/128] remove os version reference --- core/class/jeedom.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/class/jeedom.class.php b/core/class/jeedom.class.php index 33eb1dffec..53f48d2bc6 100644 --- a/core/class/jeedom.class.php +++ b/core/class/jeedom.class.php @@ -753,7 +753,7 @@ public static function getUsbMapping($_name = '', $_getGPIO = false) { $usbMapping['Jeedom board'] = '/dev/ttymxc0'; } if (file_exists('/dev/ttyAML1')) { - $usbMapping['Odroid ARMBIAN (Buster)'] = '/dev/ttyAML1'; + $usbMapping['Odroid ARMBIAN'] = '/dev/ttyAML1'; } if (file_exists('/dev/ttyAMA0')) { $usbMapping['Raspberry pi'] = '/dev/ttyAMA0'; From 63026660c60363228614c812b3e079844658bbe6 Mon Sep 17 00:00:00 2001 From: Mips2648 Date: Sun, 8 Mar 2026 11:07:22 +0100 Subject: [PATCH 064/128] Refactor: improve error handling in GitHub repo class --- core/repo/github.repo.php | 49 ++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/core/repo/github.repo.php b/core/repo/github.repo.php index 136428ed4b..dee5e6666f 100644 --- a/core/repo/github.repo.php +++ b/core/repo/github.repo.php @@ -22,19 +22,19 @@ class repo_github { /* * *************************Attributs****************************** */ - + public static $_name = 'Github'; - + public static $_scope = array( 'plugin' => true, 'backup' => false, 'hasConfiguration' => true, 'core' => true, ); - - + + /* * ***********************Méthodes statiques*************************** */ - + public static function getConfigurationOption(){ return array( 'parameters_for_add' => array( @@ -79,7 +79,7 @@ public static function getConfigurationOption(){ ), ); } - + public static function checkUpdate(&$_update) { if (is_array($_update)) { if (count($_update) < 1) { @@ -123,7 +123,7 @@ public static function getBranchInfo($_update){ $request_http->setHeader($headers); return json_decode($request_http->exec(10, 1), true); } - + public static function downloadObject($_update) { $token = $_update->getConfiguration('token',config::byKey('github::token','core','')); $branch = self::getBranchInfo($_update); @@ -141,29 +141,41 @@ public static function downloadObject($_update) { $url = 'https://api.github.com/repos/' . $_update->getConfiguration('user') . '/' . $_update->getConfiguration('repository') . '/zipball/' . $_update->getConfiguration('version', 'master'); log::add('update', 'alert', __('Téléchargement de', __FILE__) . ' ' . $_update->getLogicalId() . '...'); if ($token == '') { - $result = shell_exec('curl -s -L ' . $url . ' > ' . $tmp); + exec('curl -s -f -L -o ' . escapeshellarg($tmp) . ' ' . escapeshellarg($url) . ' 2>&1', $output, $return_code); } else { - $result = shell_exec('curl -s -H "Authorization: token ' . $token . '" -L ' . $url . ' > ' . $tmp); + exec('curl -s -f -L -H "Authorization: token ' . escapeshellarg($token) . '" -o ' . escapeshellarg($tmp) . ' ' . escapeshellarg($url) . ' 2>&1', $output, $return_code); } - log::add('update', 'alert', $result); - + if ($return_code !== 0 || !file_exists($tmp) || filesize($tmp) == 0) { + $error_msg = __('Erreur lors du téléchargement', __FILE__); + if (!empty($output)) { + $error_msg .= ': ' . implode("\n", $output); + } + if (!file_exists($tmp)) { + $error_msg .= ' - ' . __('Fichier non créé', __FILE__); + } elseif (filesize($tmp) == 0) { + $error_msg .= ' - ' . __('Fichier vide', __FILE__); + } + log::add('update', 'error', $error_msg); + throw new Exception($error_msg); + } + log::add('update', 'info', __('Téléchargement réussi', __FILE__) . ' (' . round(filesize($tmp) / 1024 / 1024, 2) . ' MB)'); + if (!isset($branch['commit']) || !isset($branch['commit']['sha'])) { return array('path' => $tmp); } return array('localVersion' => $branch['commit']['sha'], 'path' => $tmp); } - + public static function deleteObjet($_update) { - } - + public static function objectInfo($_update) { return array( 'doc' => 'https://github.com/' . $_update->getConfiguration('user') . '/' . $_update->getConfiguration('repository') . '/blob/' . $_update->getConfiguration('version', 'master') . '/doc/' . config::byKey('language', 'core', 'fr_FR') . '/index.asciidoc', 'changelog' => 'https://github.com/' . $_update->getConfiguration('user') . '/' . $_update->getConfiguration('repository') . '/commits/' . $_update->getConfiguration('version', 'master'), ); } - + public static function downloadCore($_path) { $url = 'https://api.github.com/repos/' . config::byKey('github::core::user', 'core', 'jeedom') . '/' . config::byKey('github::core::repository', 'core', 'core') . '/zipball/' . config::byKey('github::core::branch', 'core', 'stable'); echo __('Téléchargement de', __FILE__) . ' ' . $url . '...'; @@ -174,15 +186,14 @@ public static function downloadCore($_path) { } return; } - + public static function versionCore() { $url = 'https://raw.githubusercontent.com/'.config::byKey('github::core::user', 'core', 'jeedom').'/'.config::byKey('github::core::repository', 'core', 'core').'/' . config::byKey('github::core::branch', 'core', 'stable') . '/core/config/version'; $request_http = new com_http($url); return trim($request_http->exec(30)); } - + /* * *********************Methode d'instance************************* */ - + /* * **********************Getteur Setteur*************************** */ - } From 449366bb79f3883c389a569bbafd1024efc47f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc?= <1536036+zoic21@users.noreply.github.com> Date: Tue, 10 Mar 2026 10:15:34 +0100 Subject: [PATCH 065/128] Issue #3206 --- core/ajax/user.ajax.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/ajax/user.ajax.php b/core/ajax/user.ajax.php index 8cb6103efb..0b56d02e09 100644 --- a/core/ajax/user.ajax.php +++ b/core/ajax/user.ajax.php @@ -52,6 +52,7 @@ throw new Exception(__('Double authentification requise', __FILE__), -32012); } if (!login(init('username'), init('password'), init('twoFactorCode'))) { + log::add('connection', 'info',network::getClientIp(().' - '. __('Mot de passe ou nom d\'utilisateur incorrect', __FILE__)); throw new Exception(__('Mot de passe ou nom d\'utilisateur incorrect', __FILE__)); } } @@ -78,6 +79,7 @@ $_SESSION['user']->save(); @session_write_close(); } + log::add('connection', 'info',network::getClientIp(().' - '. __('Connexion réussie pour : ', __FILE__).$_SESSION['user']->getLogin()); ajax::success(); } From 6d89fc2c68f2a851261ee8022decf111ef9133d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc?= <1536036+zoic21@users.noreply.github.com> Date: Tue, 10 Mar 2026 10:18:45 +0100 Subject: [PATCH 066/128] typo --- core/ajax/user.ajax.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/ajax/user.ajax.php b/core/ajax/user.ajax.php index 0b56d02e09..d2322e2013 100644 --- a/core/ajax/user.ajax.php +++ b/core/ajax/user.ajax.php @@ -52,7 +52,7 @@ throw new Exception(__('Double authentification requise', __FILE__), -32012); } if (!login(init('username'), init('password'), init('twoFactorCode'))) { - log::add('connection', 'info',network::getClientIp(().' - '. __('Mot de passe ou nom d\'utilisateur incorrect', __FILE__)); + log::add('connection', 'info',network::getClientIp().' - '. __('Mot de passe ou nom d\'utilisateur incorrect', __FILE__)); throw new Exception(__('Mot de passe ou nom d\'utilisateur incorrect', __FILE__)); } } @@ -79,7 +79,7 @@ $_SESSION['user']->save(); @session_write_close(); } - log::add('connection', 'info',network::getClientIp(().' - '. __('Connexion réussie pour : ', __FILE__).$_SESSION['user']->getLogin()); + log::add('connection', 'info',network::getClientIp().' - '. __('Connexion réussie pour : ', __FILE__).$_SESSION['user']->getLogin()); ajax::success(); } From 7148450521474abbf3316af8c8f0cf4be475324b Mon Sep 17 00:00:00 2001 From: Mips2648 Date: Wed, 11 Mar 2026 16:17:59 +0100 Subject: [PATCH 067/128] rollback port rename --- core/class/jeedom.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/class/jeedom.class.php b/core/class/jeedom.class.php index 53f48d2bc6..33eb1dffec 100644 --- a/core/class/jeedom.class.php +++ b/core/class/jeedom.class.php @@ -753,7 +753,7 @@ public static function getUsbMapping($_name = '', $_getGPIO = false) { $usbMapping['Jeedom board'] = '/dev/ttymxc0'; } if (file_exists('/dev/ttyAML1')) { - $usbMapping['Odroid ARMBIAN'] = '/dev/ttyAML1'; + $usbMapping['Odroid ARMBIAN (Buster)'] = '/dev/ttyAML1'; } if (file_exists('/dev/ttyAMA0')) { $usbMapping['Raspberry pi'] = '/dev/ttyAMA0'; From dfcda1c8eb38f098585be8b59eb627d481bf5fc3 Mon Sep 17 00:00:00 2001 From: Salvialf <48010158+Salvialf@users.noreply.github.com> Date: Wed, 11 Mar 2026 16:47:54 +0100 Subject: [PATCH 068/128] Update OS support comment --- core/class/jeedom.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/class/jeedom.class.php b/core/class/jeedom.class.php index 33eb1dffec..c655ed39bf 100644 --- a/core/class/jeedom.class.php +++ b/core/class/jeedom.class.php @@ -307,7 +307,7 @@ public static function health() { 'name' => __('Version OS', __FILE__), 'state' => $state, 'result' => ($state) ? $uname . ' [' . $version . ']' : $uname, - 'comment' => ($state) ? '' : __('Vous n\'êtes pas sur un OS officiellement supporté par l\'équipe Jeedom (toute demande de support pourra donc être refusée). Les OS officiellement supportés sont consultables dans la documentation officielle.', __FILE__), + 'comment' => ($state) ? '' : __("Cet OS n'est pas pris en charge, toute demande de support pourra donc être refusée (voir la documentation sur la compatibilité logicielle).", __FILE__) ); $version = DB::Prepare('select version()', array(), DB::FETCH_TYPE_ROW); From ca31a8396cbce8500fbe0c6b7f5957d7c1bddf6c Mon Sep 17 00:00:00 2001 From: Salvialf Date: Wed, 11 Mar 2026 19:11:52 +0100 Subject: [PATCH 069/128] review gpio mapping names --- core/class/jeedom.class.php | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/core/class/jeedom.class.php b/core/class/jeedom.class.php index c655ed39bf..b3b47a9fa9 100644 --- a/core/class/jeedom.class.php +++ b/core/class/jeedom.class.php @@ -734,32 +734,35 @@ public static function getUsbMapping($_name = '', $_getGPIO = false) { } $usbMapping = self::getUsbLegacy($usbMapping); if ($_getGPIO) { + if (file_exists('/dev/ttyLuna-Zigbee')) { + $usbMapping['Jeedom Luna Zigbee'] = '/dev/ttyLuna-Zigbee'; + } if (file_exists('/dev/ttyS0')) { $usbMapping['Cubiboard'] = '/dev/ttyS0'; } if (file_exists('/dev/ttyS1')) { $usbMapping['Jeedom Luna Zwave'] = '/dev/ttyS1'; - } - if (file_exists('/dev/ttyS1')) { - $usbMapping['Odroid C2'] = '/dev/ttyS1'; + $usbMapping['Odroid (old)'] = '/dev/ttyS1'; } if (file_exists('/dev/ttyS2')) { $usbMapping['Jeedom Atlas'] = '/dev/ttyS2'; + $usbMapping['Rock Pi'] = '/dev/ttyS2'; } if (file_exists('/dev/ttyS3')) { - $usbMapping['Orange PI'] = '/dev/ttyS3'; + $usbMapping['Orange Pi'] = '/dev/ttyS3'; } if (file_exists('/dev/ttymxc0')) { $usbMapping['Jeedom board'] = '/dev/ttymxc0'; } - if (file_exists('/dev/ttyAML1')) { - $usbMapping['Odroid ARMBIAN (Buster)'] = '/dev/ttyAML1'; - } if (file_exists('/dev/ttyAMA0')) { - $usbMapping['Raspberry pi'] = '/dev/ttyAMA0'; + $usbMapping['Raspberry Pi'] = '/dev/ttyAMA0'; + } + if (file_exists('/dev/ttyAML1')) { + $usbMapping['Jeedom Smart'] = '/dev/ttyAML1'; + $usbMapping['Odroid'] = '/dev/ttyAML1'; } if (file_exists('/dev/S2')) { - $usbMapping['Banana PI'] = '/dev/S2'; + $usbMapping['Banana Pi'] = '/dev/S2'; } foreach (ls('/dev/', 'ttyAMA*') as $value) { $usbMapping['/dev/' . $value] = '/dev/' . $value; @@ -1300,9 +1303,9 @@ public static function cronHourly() { } catch (Error $e) { log::add('jeedom', 'error', log::exception($e)); } - try{ + try { log::chunk('', True); - }catch (Throwable $e) { + } catch (Throwable $e) { log::add('jeedom', 'error', $e->getMessage()); } catch (Error $e) { log::add('jeedom', 'error', $e->getMessage()); From 09c575de6c1fce9060426a3093fdb911f97aebf3 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Wed, 11 Mar 2026 19:16:25 +0100 Subject: [PATCH 070/128] also list /dev/ttyAML* --- core/class/jeedom.class.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/class/jeedom.class.php b/core/class/jeedom.class.php index b3b47a9fa9..92a47f48ec 100644 --- a/core/class/jeedom.class.php +++ b/core/class/jeedom.class.php @@ -767,6 +767,9 @@ public static function getUsbMapping($_name = '', $_getGPIO = false) { foreach (ls('/dev/', 'ttyAMA*') as $value) { $usbMapping['/dev/' . $value] = '/dev/' . $value; } + foreach (ls('/dev/', 'ttyAML*') as $value) { + $usbMapping['/dev/' . $value] = '/dev/' . $value; + } } cache::set('jeedom::usbMapping', json_encode($usbMapping)); } else { From d6372cd7d6af16d66693ba6aaf68722d4b385ed7 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Wed, 11 Mar 2026 19:35:45 +0100 Subject: [PATCH 071/128] format code & fix potential type error --- core/class/config.class.php | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/core/class/config.class.php b/core/class/config.class.php index a33f7d2832..5b5cb0e64c 100644 --- a/core/class/config.class.php +++ b/core/class/config.class.php @@ -111,8 +111,8 @@ public static function save($_key, $_value, $_plugin = 'core') { /** * Delete key from config - * @param string $_key nom de la clef à supprimer - * @return boolean vrai si ok faux sinon + * @param string $_key + * @return boolean */ public static function remove(string $_key, string $_plugin = 'core') { if ($_key == "*" && $_plugin != 'core') { @@ -122,11 +122,11 @@ public static function remove(string $_key, string $_plugin = 'core') { $sql = 'DELETE FROM config WHERE plugin=:plugin'; DB::Prepare($sql, $values, DB::FETCH_TYPE_ROW); - foreach (self::$cache as $cacheKey => $value) { - if (strpos($cacheKey, $_plugin . '::') === 0) { - unset(self::$cache[$cacheKey]); - } - } + foreach (self::$cache as $cacheKey => $value) { + if (strpos($cacheKey, $_plugin . '::') === 0) { + unset(self::$cache[$cacheKey]); + } + } } else { $values = array( 'plugin' => $_plugin, @@ -136,15 +136,15 @@ public static function remove(string $_key, string $_plugin = 'core') { WHERE `key`=:key AND plugin=:plugin'; DB::Prepare($sql, $values, DB::FETCH_TYPE_ROW); - unset(self::$cache[$_plugin . '::' . $_key]); + unset(self::$cache[$_plugin . '::' . $_key]); } return true; } /** * Get config by key - * @param string $_key nom de la clef dont on veut la valeur - * @return string valeur de la clef + * @param string $_key + * @return string */ public static function byKey($_key, $_plugin = 'core', $_default = '', $_forceFresh = false) { if (!$_forceFresh && isset(self::$cache[$_plugin . '::' . $_key]) && !in_array($_key, self::$nocache)) { @@ -251,9 +251,9 @@ public static function searchKey($_key, $_plugin = 'core') { } public static function genKey($_car = 64) { - if ($_car > 256) { - throw new \Exception('Key length too long'); - } + if ($_car > 256) { + throw new \Exception('Key length too long'); + } $key = ''; $chaine = "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ"; for ($i = 0; $i < $_car; $i++) { @@ -325,7 +325,11 @@ public static function getGenericTypes($_coreOnly = false) { } } } - asort($types['byFamily'], SORT_STRING | SORT_FLAG_CASE); + + if (is_array($types['byFamily'])) { + asort($types['byFamily'], SORT_STRING | SORT_FLAG_CASE); + } + return $types; } From d3e6a446ef2a8703e949a75005841b058e2c3fbe Mon Sep 17 00:00:00 2001 From: Salvialf Date: Thu, 12 Mar 2026 10:38:25 +0100 Subject: [PATCH 072/128] add config searchValue static method --- core/class/config.class.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/core/class/config.class.php b/core/class/config.class.php index 5b5cb0e64c..acf8fd3585 100644 --- a/core/class/config.class.php +++ b/core/class/config.class.php @@ -250,6 +250,27 @@ public static function searchKey($_key, $_plugin = 'core') { return $results; } + /** + * Search raw value in config + * @param mixed $_value + * @param string $_key (optional) + * @return array + */ + public static function searchValue($_value, string $_key = null): array { + $values = array( + 'value' => $_value + ); + + if ($_key) { + $values['key'] = $_key; + $sql = 'SELECT `plugin` FROM config WHERE `value`=:value AND `key`=:key'; + } else { + $sql = 'SELECT `plugin`,`key` FROM config WHERE `value`=:value'; + } + + return DB::Prepare($sql, $values, DB::FETCH_TYPE_ALL); + } + public static function genKey($_car = 64) { if ($_car > 256) { throw new \Exception('Key length too long'); From 11e9b17e1edf59898f662c3c03b2adc4c245eaa5 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Thu, 12 Mar 2026 11:01:54 +0100 Subject: [PATCH 073/128] auto update renamed ports in config --- install/update/4.5.3.php | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 install/update/4.5.3.php diff --git a/install/update/4.5.3.php b/install/update/4.5.3.php new file mode 100644 index 0000000000..1f5fca4279 --- /dev/null +++ b/install/update/4.5.3.php @@ -0,0 +1,25 @@ + 'Odroid (old)', + 'Odroid ARMBIAN (Buster)' => 'Odroid', + 'Banana PI' => 'Banana Pi', + 'Orange PI' => 'Orange Pi', + 'Raspberry pi' => 'Raspberry Pi' +); + +$hardware = strtolower(jeedom::getHardwareName()); +if ($hardware === 'smart') { + $renamed['Odroid ARMBIAN (Buster)'] = 'Jeedom Smart'; +} +if ($hardware !== 'atlas') { + $renamed['Jeedom Atlas'] = 'Rock Pi'; +} + +foreach ($renamed as $previousValue => $newValue) { + foreach ((config::searchValue($previousValue, 'port')) as $config) { + config::save('port', $newValue, $config['plugin']); + } +} From 85080213d15d8c5cce96638eecf18c9111b49849 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Thu, 12 Mar 2026 11:13:28 +0100 Subject: [PATCH 074/128] update changelog --- docs/fr_FR/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/fr_FR/changelog.md b/docs/fr_FR/changelog.md index 4e3bc468df..202522d104 100644 --- a/docs/fr_FR/changelog.md +++ b/docs/fr_FR/changelog.md @@ -3,6 +3,9 @@ # 4.5.3 - Diverses corrections de bugs et optimisations +- Mise à jour dans les contrôles de version +- Mise à jour du nommage des ports de communication +- [Développeurs] Ajout de la méthode `config::searchValue($_value, string $_key = null)` # 4.5.2 From cdcc520cfbfd41891faf7d8a285d0ef6bb54bd08 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Thu, 12 Mar 2026 11:41:23 +0100 Subject: [PATCH 075/128] precise searchValue description --- core/class/config.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/class/config.class.php b/core/class/config.class.php index acf8fd3585..9f8f372775 100644 --- a/core/class/config.class.php +++ b/core/class/config.class.php @@ -251,7 +251,7 @@ public static function searchKey($_key, $_plugin = 'core') { } /** - * Search raw value in config + * Search unencrypted value in config * @param mixed $_value * @param string $_key (optional) * @return array From e37242118576e0671ecfeb6574a924205303c8db Mon Sep 17 00:00:00 2001 From: Salvialf Date: Thu, 12 Mar 2026 13:41:14 +0100 Subject: [PATCH 076/128] add specific config for known boards --- core/class/config.class.php | 26 ++++++++++++++++++++++++-- core/config/specific.config.ini | 27 +++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 core/config/specific.config.ini diff --git a/core/class/config.class.php b/core/class/config.class.php index 9f8f372775..5c6b63ff1c 100644 --- a/core/class/config.class.php +++ b/core/class/config.class.php @@ -29,7 +29,12 @@ class config { /* * ***********************Methode static*************************** */ - public static function getDefaultConfiguration(string $_plugin = 'core') { + /** + * Get default configuration for core or plugin + * @param string $_plugin + * @return array + */ + public static function getDefaultConfiguration(string $_plugin = 'core'): array { if (!isset(self::$defaultConfiguration[$_plugin])) { if ($_plugin == 'core') { self::$defaultConfiguration[$_plugin] = parse_ini_file(__DIR__ . '/../../core/config/default.config.ini', true); @@ -45,10 +50,27 @@ public static function getDefaultConfiguration(string $_plugin = 'core') { } } if (!isset(self::$defaultConfiguration[$_plugin])) { - self::$defaultConfiguration[$_plugin] = array(); + self::$defaultConfiguration[$_plugin] = self::getSpecificConfiguration($_plugin); + } else { + self::$defaultConfiguration[$_plugin] = array_replace_recursive(self::$defaultConfiguration[$_plugin], self::getSpecificConfiguration($_plugin)); } return self::$defaultConfiguration[$_plugin]; } + + /** + * Get specific configuration for known boards + * @param string $_plugin + * @return array + */ + private static function getSpecificConfiguration(string $_plugin): array { + $hardware = strtolower(jeedom::getHardwareName()); + $specific = parse_ini_file(__DIR__ . '/../../core/config/specific.config.ini', true); + if (isset($specific[$hardware][$_plugin])) { + return array($_plugin => $specific[$hardware][$_plugin]); + } + return array(); + } + /** * Save key to config * @param string $_key diff --git a/core/config/specific.config.ini b/core/config/specific.config.ini new file mode 100644 index 0000000000..d1835ccc48 --- /dev/null +++ b/core/config/specific.config.ini @@ -0,0 +1,27 @@ +[atlas] + zwavejs[port] = Jeedom Atlas + + z2m[controller] = ezsp + z2m[port] = /dev/ttyS2 + z2m[z2m::mode] = local + + openenocean[port] = Jeedom Atlas + + eibd[KnxSoft] = knxd + eibd[level] = 3 + eibd[TypeKNXgateway] = ft12cemi + eibd[KNXgateway] = /dev/ttyS2 + +[luna] + zwavejs[port] = Jeedom Luna Zwave + + z2m[controller] = ezsp + z2m[port] = /dev/ttyLuna-Zigbee + z2m[z2m::mode] = local + + openenocean[port] = auto + +[smart] + zwavejs[port] = Jeedom Smart + + openenocean[port] = Jeedom Smart From 47bb630dd1a6f418cdac3ac536a76aa879d5caae Mon Sep 17 00:00:00 2001 From: Salvialf Date: Thu, 12 Mar 2026 13:47:07 +0100 Subject: [PATCH 077/128] update changelog --- docs/fr_FR/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/fr_FR/changelog.md b/docs/fr_FR/changelog.md index 202522d104..98fc13fd61 100644 --- a/docs/fr_FR/changelog.md +++ b/docs/fr_FR/changelog.md @@ -5,6 +5,7 @@ - Diverses corrections de bugs et optimisations - Mise à jour dans les contrôles de version - Mise à jour du nommage des ports de communication +- Ajout de configurations automatiques de certains plugins *(zwavejs, z2m, openenocean, eibd)* pour les boxes officielles - [Développeurs] Ajout de la méthode `config::searchValue($_value, string $_key = null)` # 4.5.2 From 277d9078d10c3530f9ae82df300ce0f76f986d95 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Thu, 12 Mar 2026 14:35:00 +0100 Subject: [PATCH 078/128] update luna z2m controller --- core/config/specific.config.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/config/specific.config.ini b/core/config/specific.config.ini index d1835ccc48..59e80b8377 100644 --- a/core/config/specific.config.ini +++ b/core/config/specific.config.ini @@ -15,7 +15,7 @@ [luna] zwavejs[port] = Jeedom Luna Zwave - z2m[controller] = ezsp + z2m[controller] = ember z2m[port] = /dev/ttyLuna-Zigbee z2m[z2m::mode] = local From 559a010d8ff124511b421ed69d94738d2ec58bd0 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Thu, 12 Mar 2026 15:11:37 +0100 Subject: [PATCH 079/128] not searchValue but byValue --- core/class/config.class.php | 42 ++++++++++++++++++------------------- docs/fr_FR/changelog.md | 2 +- install/update/4.5.3.php | 2 +- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/core/class/config.class.php b/core/class/config.class.php index 5c6b63ff1c..ba9524b875 100644 --- a/core/class/config.class.php +++ b/core/class/config.class.php @@ -249,6 +249,27 @@ public static function byKeys($_keys, $_plugin = 'core', $_default = '') { return $return; } + /** + * Get list of plugins|keys from unencrypted value + * @param mixed $_value + * @param string $_key (optional) + * @return array + */ + public static function byValue($_value, string $_key = null): array { + $values = array( + 'value' => $_value + ); + + if ($_key) { + $values['key'] = $_key; + $sql = 'SELECT `plugin` FROM config WHERE `value`=:value AND `key`=:key'; + } else { + $sql = 'SELECT `plugin`,`key` FROM config WHERE `value`=:value'; + } + + return DB::Prepare($sql, $values, DB::FETCH_TYPE_ALL); + } + public static function searchKey($_key, $_plugin = 'core') { $values = array( 'plugin' => $_plugin, @@ -272,27 +293,6 @@ public static function searchKey($_key, $_plugin = 'core') { return $results; } - /** - * Search unencrypted value in config - * @param mixed $_value - * @param string $_key (optional) - * @return array - */ - public static function searchValue($_value, string $_key = null): array { - $values = array( - 'value' => $_value - ); - - if ($_key) { - $values['key'] = $_key; - $sql = 'SELECT `plugin` FROM config WHERE `value`=:value AND `key`=:key'; - } else { - $sql = 'SELECT `plugin`,`key` FROM config WHERE `value`=:value'; - } - - return DB::Prepare($sql, $values, DB::FETCH_TYPE_ALL); - } - public static function genKey($_car = 64) { if ($_car > 256) { throw new \Exception('Key length too long'); diff --git a/docs/fr_FR/changelog.md b/docs/fr_FR/changelog.md index 98fc13fd61..d5b03aa25e 100644 --- a/docs/fr_FR/changelog.md +++ b/docs/fr_FR/changelog.md @@ -6,7 +6,7 @@ - Mise à jour dans les contrôles de version - Mise à jour du nommage des ports de communication - Ajout de configurations automatiques de certains plugins *(zwavejs, z2m, openenocean, eibd)* pour les boxes officielles -- [Développeurs] Ajout de la méthode `config::searchValue($_value, string $_key = null)` +- [Développeurs] Ajout de la méthode `config::byValue($_value, string $_key = null)` # 4.5.2 diff --git a/install/update/4.5.3.php b/install/update/4.5.3.php index 1f5fca4279..efeb5ddc77 100644 --- a/install/update/4.5.3.php +++ b/install/update/4.5.3.php @@ -19,7 +19,7 @@ } foreach ($renamed as $previousValue => $newValue) { - foreach ((config::searchValue($previousValue, 'port')) as $config) { + foreach ((config::byValue($previousValue, 'port')) as $config) { config::save('port', $newValue, $config['plugin']); } } From 4ad1930f00be1ff4570ed925d6c993b581a5baba Mon Sep 17 00:00:00 2001 From: Salvialf Date: Thu, 12 Mar 2026 19:00:49 +0100 Subject: [PATCH 080/128] hotfix prevent loops --- core/class/config.class.php | 40 ++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/core/class/config.class.php b/core/class/config.class.php index ba9524b875..37574ad1fc 100644 --- a/core/class/config.class.php +++ b/core/class/config.class.php @@ -23,6 +23,7 @@ class config { /* * *************************Attributs****************************** */ private static $defaultConfiguration = array(); + private static $specificConfiguration = array(); private static $cache = array(); private static $encryptKey = array('apipro', 'apitts', 'apimarket', 'samba::backup::password', 'samba::backup::ip', 'samba::backup::username', 'ldap:password', 'ldap:host', 'ldap:username', 'dns::token', 'api'); private static $nocache = array('enableScenario'); @@ -35,25 +36,29 @@ class config { * @return array */ public static function getDefaultConfiguration(string $_plugin = 'core'): array { - if (!isset(self::$defaultConfiguration[$_plugin])) { - if ($_plugin == 'core') { - self::$defaultConfiguration[$_plugin] = parse_ini_file(__DIR__ . '/../../core/config/default.config.ini', true); - if (file_exists(__DIR__ . '/../../data/custom/custom.config.ini')) { - $custom = parse_ini_file(__DIR__ . '/../../data/custom/custom.config.ini', true); - self::$defaultConfiguration[$_plugin]['core'] = array_merge(self::$defaultConfiguration[$_plugin]['core'], $custom['core']); - } - } else { - $filename = __DIR__ . '/../../plugins/' . $_plugin . '/core/config/' . $_plugin . '.config.ini'; - if (file_exists($filename)) { - self::$defaultConfiguration[$_plugin] = parse_ini_file($filename, true); - } + if (isset(self::$defaultConfiguration[$_plugin])) { + return self::$defaultConfiguration[$_plugin]; + } + + if ($_plugin == 'core') { + self::$defaultConfiguration[$_plugin] = parse_ini_file(__DIR__ . '/../../core/config/default.config.ini', true); + if (file_exists(__DIR__ . '/../../data/custom/custom.config.ini')) { + $custom = parse_ini_file(__DIR__ . '/../../data/custom/custom.config.ini', true); + self::$defaultConfiguration[$_plugin]['core'] = array_merge(self::$defaultConfiguration[$_plugin]['core'], $custom['core']); + } + } else { + $filename = __DIR__ . '/../../plugins/' . $_plugin . '/core/config/' . $_plugin . '.config.ini'; + if (file_exists($filename)) { + self::$defaultConfiguration[$_plugin] = parse_ini_file($filename, true); } } + if (!isset(self::$defaultConfiguration[$_plugin])) { self::$defaultConfiguration[$_plugin] = self::getSpecificConfiguration($_plugin); } else { self::$defaultConfiguration[$_plugin] = array_replace_recursive(self::$defaultConfiguration[$_plugin], self::getSpecificConfiguration($_plugin)); } + return self::$defaultConfiguration[$_plugin]; } @@ -63,12 +68,19 @@ public static function getDefaultConfiguration(string $_plugin = 'core'): array * @return array */ private static function getSpecificConfiguration(string $_plugin): array { + if (isset(self::$specificConfiguration[$_plugin])) { + return self::$specificConfiguration[$_plugin]; + } + $hardware = strtolower(jeedom::getHardwareName()); $specific = parse_ini_file(__DIR__ . '/../../core/config/specific.config.ini', true); if (isset($specific[$hardware][$_plugin])) { - return array($_plugin => $specific[$hardware][$_plugin]); + self::$specificConfiguration[$_plugin] = array($_plugin => $specific[$hardware][$_plugin]); + } else { + self::$specificConfiguration[$_plugin] = array(); } - return array(); + + return self::$specificConfiguration[$_plugin]; } /** From 65814beae1c6dd428e30cf836ded60eea00fe8ba Mon Sep 17 00:00:00 2001 From: Salvialf Date: Thu, 12 Mar 2026 20:55:41 +0100 Subject: [PATCH 081/128] continue optimization --- core/class/config.class.php | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/core/class/config.class.php b/core/class/config.class.php index 37574ad1fc..d0bd9f7357 100644 --- a/core/class/config.class.php +++ b/core/class/config.class.php @@ -68,19 +68,16 @@ public static function getDefaultConfiguration(string $_plugin = 'core'): array * @return array */ private static function getSpecificConfiguration(string $_plugin): array { - if (isset(self::$specificConfiguration[$_plugin])) { - return self::$specificConfiguration[$_plugin]; - } - - $hardware = strtolower(jeedom::getHardwareName()); - $specific = parse_ini_file(__DIR__ . '/../../core/config/specific.config.ini', true); - if (isset($specific[$hardware][$_plugin])) { - self::$specificConfiguration[$_plugin] = array($_plugin => $specific[$hardware][$_plugin]); - } else { - self::$specificConfiguration[$_plugin] = array(); + if (empty(self::$specificConfiguration)) { + $specific = parse_ini_file(__DIR__ . '/../../core/config/specific.config.ini', true); + $hardware = strtolower(jeedom::getHardwareName()); + if (isset($specific[$hardware])) { + foreach ($specific[$hardware] as $plugin => $config) { + self::$specificConfiguration[$plugin] = array($plugin => $config); + } + } } - - return self::$specificConfiguration[$_plugin]; + return self::$specificConfiguration[$_plugin] ?? array(); } /** From 0a311aec74a40d74f90ad47ad83138b82e2c2179 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Thu, 12 Mar 2026 21:22:42 +0100 Subject: [PATCH 082/128] further optimization --- core/class/config.class.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core/class/config.class.php b/core/class/config.class.php index d0bd9f7357..44d167b9f1 100644 --- a/core/class/config.class.php +++ b/core/class/config.class.php @@ -23,7 +23,7 @@ class config { /* * *************************Attributs****************************** */ private static $defaultConfiguration = array(); - private static $specificConfiguration = array(); + private static $specificConfiguration = null; private static $cache = array(); private static $encryptKey = array('apipro', 'apitts', 'apimarket', 'samba::backup::password', 'samba::backup::ip', 'samba::backup::username', 'ldap:password', 'ldap:host', 'ldap:username', 'dns::token', 'api'); private static $nocache = array('enableScenario'); @@ -58,7 +58,6 @@ public static function getDefaultConfiguration(string $_plugin = 'core'): array } else { self::$defaultConfiguration[$_plugin] = array_replace_recursive(self::$defaultConfiguration[$_plugin], self::getSpecificConfiguration($_plugin)); } - return self::$defaultConfiguration[$_plugin]; } @@ -68,7 +67,8 @@ public static function getDefaultConfiguration(string $_plugin = 'core'): array * @return array */ private static function getSpecificConfiguration(string $_plugin): array { - if (empty(self::$specificConfiguration)) { + if (self::$specificConfiguration === null) { + self::$specificConfiguration = array(); $specific = parse_ini_file(__DIR__ . '/../../core/config/specific.config.ini', true); $hardware = strtolower(jeedom::getHardwareName()); if (isset($specific[$hardware])) { @@ -275,7 +275,6 @@ public static function byValue($_value, string $_key = null): array { } else { $sql = 'SELECT `plugin`,`key` FROM config WHERE `value`=:value'; } - return DB::Prepare($sql, $values, DB::FETCH_TYPE_ALL); } From 415fde5873fd9a08140109e980e952facc0060ec Mon Sep 17 00:00:00 2001 From: Salvialf Date: Fri, 13 Mar 2026 15:02:51 +0100 Subject: [PATCH 083/128] format code --- core/class/log.class.php | 222 ++++++++++++++++++++++----------------- 1 file changed, 128 insertions(+), 94 deletions(-) diff --git a/core/class/log.class.php b/core/class/log.class.php index 36adeaf5da..c564926c5f 100644 --- a/core/class/log.class.php +++ b/core/class/log.class.php @@ -69,9 +69,9 @@ public static function getConfig($_key, $_default = '') { public static function getLogLevel($_log) { $specific_level = self::getConfig('log::level::' . $_log); - if (!is_array($specific_level) && strpos($_log,'_') !== false) { + if (!is_array($specific_level) && strpos($_log, '_') !== false) { preg_match('/(.*?)\_[a-zA-Z]*?$/m', $_log, $matches); - if(isset($matches[1])){ + if (isset($matches[1])) { $specific_level = self::getConfig('log::level::' . $matches[1]); } } @@ -96,7 +96,7 @@ public static function convertLogLevel($_level = 100) { return 'none'; } foreach (self::$level as $key => $value) { - if($value == $_level){ + if ($value == $_level) { return $key; } } @@ -108,26 +108,25 @@ public static function convertLogLevel($_level = 100) { * @param string $_type message type (info, debug, warning, danger) * @param string $_message message added into log */ - public static function add(string $_log,string $_type, string $_message,string $_logicalId = '') { + public static function add(string $_log, string $_type, string $_message, string $_logicalId = '') { if (trim($_message) == '') { return; } $level = (isset(self::$level[strtolower($_type)])) ? self::$level[strtolower($_type)] : 100; - if($level < self::getLogLevel($_log)){ + if ($level < self::getLogLevel($_log)) { return; } $fp = fopen(self::getPathToLog($_log), 'a'); - fwrite($fp,'['.date('Y-m-d H:i:s').']['.strtoupper($_type).'] '.$_message."\n"); + fwrite($fp, '[' . date('Y-m-d H:i:s') . '][' . strtoupper($_type) . '] ' . $_message . "\n"); fclose($fp); try { - $action = '' . __('Log', __FILE__) . ' ' . $_log . ''; + $action = '' . __('Log', __FILE__) . ' ' . $_log . ''; if ($level == 400 && self::getConfig('addMessageForErrorLog') == 1) { @message::add($_log, $_message, $action, $_logicalId); } elseif ($level >= 500 && $_log != 'update') { @message::add($_log, $_message, $action, $_logicalId); } } catch (Exception $e) { - } } @@ -147,12 +146,12 @@ public static function chunk($_log = '', $_onlyIfSizeExceeded = False) { } } foreach ($paths as $path) { - if (is_file($path)) { - if($_onlyIfSizeExceeded && filesize($path) < (self::getConfig('maxSizeLog') * 1024 * 1024) ){ - continue; - } - self::chunkLog($path); + if (is_file($path)) { + if ($_onlyIfSizeExceeded && filesize($path) < (self::getConfig('maxSizeLog') * 1024 * 1024)) { + continue; } + self::chunkLog($path); + } } } @@ -165,7 +164,7 @@ public static function chunkLog($_path) { $maxLineLog = self::DEFAULT_MAX_LINE; } try { - com_shell::execute(system::getCmdSudo() . 'chmod 664 ' . $_path . ' > /dev/null 2>&1;'. system::getCmdSudo() . 'chown -R ' . system::get('www-uid') . ':' . system::get('www-gid') . ' ' . $_path.' > /dev/null 2>&1;'.system::getCmdSudo() . ' echo "$(tail -n ' . $maxLineLog . ' ' . $_path . ')" > ' . $_path); + com_shell::execute(system::getCmdSudo() . 'chmod 664 ' . $_path . ' > /dev/null 2>&1;' . system::getCmdSudo() . 'chown -R ' . system::get('www-uid') . ':' . system::get('www-gid') . ' ' . $_path . ' > /dev/null 2>&1;' . system::getCmdSudo() . ' echo "$(tail -n ' . $maxLineLog . ' ' . $_path . ')" > ' . $_path); } catch (\Exception $e) { } @chown($_path, system::get('www-uid')); @@ -200,7 +199,7 @@ public static function authorizeClearLog($_log, $_subPath = '') { public static function clear($_log) { if (self::authorizeClearLog($_log)) { $path = self::getPathToLog($_log); - com_shell::execute(system::getCmdSudo() . 'chmod 664 ' . $path . '> /dev/null 2>&1;'. system::getCmdSudo() . 'chown -R ' . system::get('www-uid') . ':' . system::get('www-gid') . ' ' . $path.' > /dev/null 2>&1;'.system::getCmdSudo() . ' cat /dev/null > ' . $path); + com_shell::execute(system::getCmdSudo() . 'chmod 664 ' . $path . '> /dev/null 2>&1;' . system::getCmdSudo() . 'chown -R ' . system::get('www-uid') . ':' . system::get('www-gid') . ' ' . $path . ' > /dev/null 2>&1;' . system::getCmdSudo() . ' cat /dev/null > ' . $path); return true; } return; @@ -226,10 +225,10 @@ public static function remove($_log) { } if (self::authorizeClearLog($_log)) { $path = self::getPathToLog($_log); - com_shell::execute(system::getCmdSudo() . 'chmod 664 ' . $path . ' > /dev/null 2>&1;'. system::getCmdSudo() . 'chown -R ' . system::get('www-uid') . ':' . system::get('www-gid') . ' ' . $path.' > /dev/null 2>&1;'.system::getCmdSudo() . ' cat /dev/null > ' . $path.';rm ' . $path . ' 2>&1 > /dev/null'); + com_shell::execute(system::getCmdSudo() . 'chmod 664 ' . $path . ' > /dev/null 2>&1;' . system::getCmdSudo() . 'chown -R ' . system::get('www-uid') . ':' . system::get('www-gid') . ' ' . $path . ' > /dev/null 2>&1;' . system::getCmdSudo() . ' cat /dev/null > ' . $path . ';rm ' . $path . ' 2>&1 > /dev/null'); + - return true; } } @@ -241,24 +240,24 @@ public static function removeAll() { } /** - * Get $_nbLines from a $_log from $_begin position - * @param string $_log - * @param int $_begin - * @param int $_nbLines - * @return boolean|array - * @deprecated v4.4 - * => removed in v4.6 (use log::getDelta() instead) - * - * Note that log::get($_log, $_begin, $_nbLines) is equivalent to: - * $path = (!file_exists($_log) || !is_file($_log)) ? self::getPathToLog($_log) : $_log; - * if (!file_exists($path)) { - * return false; - * } - * $delta = self::getDelta($_log, $_begin, '', false, false, 0, $_nbLines); - * $arr = explode("\n", $delta['logText']); - * unset($arr[count($arr) - 1]); - * $res = array_reverse($arr); - */ + * Get $_nbLines from a $_log from $_begin position + * @param string $_log + * @param int $_begin + * @param int $_nbLines + * @return boolean|array + * @deprecated v4.4 + * => removed in v4.6 (use log::getDelta() instead) + * + * Note that log::get($_log, $_begin, $_nbLines) is equivalent to: + * $path = (!file_exists($_log) || !is_file($_log)) ? self::getPathToLog($_log) : $_log; + * if (!file_exists($path)) { + * return false; + * } + * $delta = self::getDelta($_log, $_begin, '', false, false, 0, $_nbLines); + * $arr = explode("\n", $delta['logText']); + * unset($arr[count($arr) - 1]); + * $res = array_reverse($arr); + */ public static function get($_log, $_begin, $_nbLines) { $path = (!file_exists($_log) || !is_file($_log)) ? self::getPathToLog($_log) : $_log; if (!file_exists($path)) { @@ -274,7 +273,6 @@ public static function get($_log, $_begin, $_nbLines) { $line = trim($log->current()); //get current line if ($line != '') { array_unshift($page, mb_convert_encoding($line, 'UTF-8')); - } $log->next(); $linesRead++; @@ -284,18 +282,18 @@ public static function get($_log, $_begin, $_nbLines) { } /** - * Get the log delta from $_position to the end of the file - * New position is stored in $_position when eof is reached - * - * @param string $_log Log filename (default 'core') - * @param int $_position Bytes representing position from the begining of the file (default 0) - * @param string $_search Text to find in log file (default '') - * @param bool $_colored Should lines be colored (default false) - * @param bool $_numbered Should lines be numbered (default true) - * @param int $_numStart At what number should lines number start (default 0) - * @param int $_max Max number of returned lines (default is config value "maxLineLog") - * @return array Array containing log to append to buffer and new position for next call - */ + * Get the log delta from $_position to the end of the file + * New position is stored in $_position when eof is reached + * + * @param string $_log Log filename (default 'core') + * @param int $_position Bytes representing position from the begining of the file (default 0) + * @param string $_search Text to find in log file (default '') + * @param bool $_colored Should lines be colored (default false) + * @param bool $_numbered Should lines be numbered (default true) + * @param int $_numStart At what number should lines number start (default 0) + * @param int $_max Max number of returned lines (default is config value "maxLineLog") + * @return array Array containing log to append to buffer and new position for next call + */ public static function getDelta($_log = 'core', $_position = 0, $_search = '', $_colored = false, $_numbered = true, $_numStart = 0, $_max = -1) { // Add path to file if needed $filename = (file_exists($_log) && is_file($_log)) ? $_log : self::getPathToLog($_log); @@ -363,46 +361,82 @@ public static function getDelta($_log = 'core', $_position = 0, $_search = '', $ $logText = preg_replace('/(' . $srch . ')/i', '$1', $logText); } - $search = array(); $replace = array(); - $search[] = '[DEBUG]'; $replace[] = ' D<&>EBUG '; - $search[] = '[INFO]'; $replace[] = ' I<&>NFO '; - $search[] = '[NOTICE]'; $replace[] = 'N<&>OTICE '; - $search[] = '[WARNING]'; $replace[] = 'W<&>ARNING'; - $search[] = '[ERROR]'; $replace[] = ' E<&>RROR '; - $search[] = '[CRITICAL]'; $replace[] = ' C<&>RITI '; - $search[] = '[ALERT]'; $replace[] = ' A<&>LERT '; - $search[] = '[EMERGENCY]'; $replace[] = ' E<&>MERG '; - - $search[] = '[ OK ]'; $replace[] = '[  O<&>K  ]'; - $search[] = '[ KO ]'; $replace[] = '[  K<&>O  ]'; - $search[] = ' OK '; $replace[] = ' O<&>K '; - $search[] = ' KO '; $replace[] = ' K<&>O '; - $search[] = 'ERROR'; $replace[] = 'E<&>RROR'; - $search[] = 'PHP Notice:'; $replace[] = 'PHP N<&>otice:'; - $search[] = 'PHP Warning:'; $replace[] = 'PHP War<&>ning:'; - $search[] = 'PHP Stack trace:'; $replace[] = 'PHP S<&>tack trace:'; - - $search[] = ':br:'; $replace[] = '
'; - $search[] = ':bg-success:'; $replace[] = ''; - $search[] = ':bg-info:'; $replace[] = ''; - $search[] = ':bg-warning:'; $replace[] = ''; - $search[] = ':bg-danger:'; $replace[] = ''; - $search[] = ':/bg:'; $replace[] = ''; - $search[] = ':fg-success:'; $replace[] = ''; - $search[] = ':fg-info:'; $replace[] = ''; - $search[] = ':fg-warning:'; $replace[] = ''; - $search[] = ':fg-danger:'; $replace[] = ''; - $search[] = ':/fg:'; $replace[] = ''; - $search[] = ':b:'; $replace[] = ''; - $search[] = ':/b:'; $replace[] = ''; - $search[] = ':s:'; $replace[] = ''; - $search[] = ':/s:'; $replace[] = ''; - $search[] = ':i:'; $replace[] = ''; - $search[] = ':/i:'; $replace[] = ''; - $search[] = ':hide:'; $replace[] = ''; - - foreach($GLOBALS['JEEDOM_SCLOG_TEXT'] as $item) { + $search = array(); + $replace = array(); + $search[] = '[DEBUG]'; + $replace[] = ' D<&>EBUG '; + $search[] = '[INFO]'; + $replace[] = ' I<&>NFO '; + $search[] = '[NOTICE]'; + $replace[] = 'N<&>OTICE '; + $search[] = '[WARNING]'; + $replace[] = 'W<&>ARNING'; + $search[] = '[ERROR]'; + $replace[] = ' E<&>RROR '; + $search[] = '[CRITICAL]'; + $replace[] = ' C<&>RITI '; + $search[] = '[ALERT]'; + $replace[] = ' A<&>LERT '; + $search[] = '[EMERGENCY]'; + $replace[] = ' E<&>MERG '; + + $search[] = '[ OK ]'; + $replace[] = '[  O<&>K  ]'; + $search[] = '[ KO ]'; + $replace[] = '[  K<&>O  ]'; + $search[] = ' OK '; + $replace[] = ' O<&>K '; + $search[] = ' KO '; + $replace[] = ' K<&>O '; + $search[] = 'ERROR'; + $replace[] = 'E<&>RROR'; + $search[] = 'PHP Notice:'; + $replace[] = 'PHP N<&>otice:'; + $search[] = 'PHP Warning:'; + $replace[] = 'PHP War<&>ning:'; + $search[] = 'PHP Stack trace:'; + $replace[] = 'PHP S<&>tack trace:'; + + $search[] = ':br:'; + $replace[] = '
'; + $search[] = ':bg-success:'; + $replace[] = ''; + $search[] = ':bg-info:'; + $replace[] = ''; + $search[] = ':bg-warning:'; + $replace[] = ''; + $search[] = ':bg-danger:'; + $replace[] = ''; + $search[] = ':/bg:'; + $replace[] = ''; + $search[] = ':fg-success:'; + $replace[] = ''; + $search[] = ':fg-info:'; + $replace[] = ''; + $search[] = ':fg-warning:'; + $replace[] = ''; + $search[] = ':fg-danger:'; + $replace[] = ''; + $search[] = ':/fg:'; + $replace[] = ''; + $search[] = ':b:'; + $replace[] = ''; + $search[] = ':/b:'; + $replace[] = ''; + $search[] = ':s:'; + $replace[] = ''; + $search[] = ':/s:'; + $replace[] = ''; + $search[] = ':i:'; + $replace[] = ''; + $search[] = ':/i:'; + $replace[] = ''; + $search[] = ':hide:'; + $replace[] = ''; + + foreach ($GLOBALS['JEEDOM_SCLOG_TEXT'] as $item) { $search[] = $item['txt']; // Insert a marker into subject string to avoid replacing it multiple times $subject = $item['txt'][0] . '<&>' . substr($item['txt'], 1); @@ -417,7 +451,7 @@ public static function getDelta($_log = 'core', $_position = 0, $_search = '', $ array('txt' => '-------------------- TRUNCATED LOG --------------------', 'replace' => '::') ); - foreach($replacables as $item) { + foreach ($replacables as $item) { if (strlen($item['txt']) >= 2) { $search[] = $item['txt']; // Insert a marker into subject string to avoid replacing it multiple times @@ -436,10 +470,10 @@ public static function getDelta($_log = 'core', $_position = 0, $_search = '', $ } /** - * Efficiently get the last line of a file - * @param string $_log Log filename - * @return string The last non-empty line of the file (or '') - */ + * Efficiently get the last line of a file + * @param string $_log Log filename + * @return string The last non-empty line of the file (or '') + */ public static function getLastLine($_log) { // Add path to file if needed $filename = (file_exists($_log) && is_file($_log)) ? $_log : self::getPathToLog($_log); @@ -516,7 +550,7 @@ public static function exception($e) { if (self::getConfig('log::level') > 100) { return $e->getMessage(); } else { - return $e->getMessage()."\n".$e->getTraceAsString(); + return $e->getMessage() . "\n" . $e->getTraceAsString(); } } From 405c6c290bc726a8c16a46211895365765debbb1 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Fri, 13 Mar 2026 15:10:26 +0100 Subject: [PATCH 084/128] fix 66b1273 prevent operand error from log::chunk called by jeedom::cronHourly (`Unsupported operand types: string * int`) --- core/class/log.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/class/log.class.php b/core/class/log.class.php index c564926c5f..5c06f73494 100644 --- a/core/class/log.class.php +++ b/core/class/log.class.php @@ -59,7 +59,7 @@ public function log($level, $message, array $context = array()) { public static function getConfig($_key, $_default = '') { if (self::$config === null) { - self::$config = array_merge(config::getLogLevelPlugin(), config::byKeys(array('log::engine', 'log::formatter', 'log::level', 'addMessageForErrorLog', 'maxLineLog'))); + self::$config = array_merge(config::getLogLevelPlugin(), config::byKeys(array('log::engine', 'log::formatter', 'log::level', 'addMessageForErrorLog', 'maxLineLog', 'maxSizeLog'))); } if (isset(self::$config[$_key])) { return self::$config[$_key]; From 996a3c5ce0e64b02e436216e2301d2423799f7eb Mon Sep 17 00:00:00 2001 From: Salvialf Date: Fri, 13 Mar 2026 19:28:10 +0100 Subject: [PATCH 085/128] format code --- core/repo/market.repo.php | 80 +++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/core/repo/market.repo.php b/core/repo/market.repo.php index 7dfa0cbedd..32edd3a39d 100644 --- a/core/repo/market.repo.php +++ b/core/repo/market.repo.php @@ -144,7 +144,7 @@ public static function pullInstall() { $update->setType($repo->getType()); $update->setLocalVersion($repo->getDatetime($plugin['version'])); $update->setConfiguration('version', $plugin['version']); - $update->setConfiguration('user',null); + $update->setConfiguration('user', null); $update->save(); $update->doUpdate(); $nbInstall++; @@ -231,32 +231,32 @@ public static function objectInfo($_update) { /* * ***********************BACKUP*************************** */ public static function backup_createFolderIsNotExist() { - $request_http = new com_http(config::byKey('service::backup::url').'/webdav/'.urlencode(config::byKey('market::username')),config::byKey('market::username'),config::byKey('market::password')); + $request_http = new com_http(config::byKey('service::backup::url') . '/webdav/' . urlencode(config::byKey('market::username')), config::byKey('market::username'), config::byKey('market::password')); $request_http->setCURLOPT(array( - CURLOPT_CUSTOMREQUEST => "PROPFIND" + CURLOPT_CUSTOMREQUEST => "PROPFIND" )); $xml = simplexml_load_string($request_http->exec()); $ns = $xml->getNamespaces(true); $child = $xml->children($ns['D']); - $found = false; + $found = false; foreach ($child->response as $file) { - if($file->propstat->prop->getcontenttype){ + if ($file->propstat->prop->getcontenttype) { continue; } - $folder = trim(trim(str_replace('http://backup.jeedom.com/webdav/'.urlencode(config::byKey('market::username')),'',$file->href),'/')); - $folder = trim(trim(str_replace('http://backup.jeedom.com/webdav/'.config::byKey('market::username'),'',$folder),'/')); - if($folder == ''){ + $folder = trim(trim(str_replace('http://backup.jeedom.com/webdav/' . urlencode(config::byKey('market::username')), '', $file->href), '/')); + $folder = trim(trim(str_replace('http://backup.jeedom.com/webdav/' . config::byKey('market::username'), '', $folder), '/')); + if ($folder == '') { continue; } - if($folder == config::byKey('market::cloud::backup::name')){ + if ($folder == config::byKey('market::cloud::backup::name')) { $found = true; break; } } if (!$found) { - $request_http = new com_http(config::byKey('service::backup::url').'/webdav/'.urlencode(config::byKey('market::username')).'/'.rawurldecode(config::byKey('market::cloud::backup::name')),config::byKey('market::username'),config::byKey('market::password')); + $request_http = new com_http(config::byKey('service::backup::url') . '/webdav/' . urlencode(config::byKey('market::username')) . '/' . rawurldecode(config::byKey('market::cloud::backup::name')), config::byKey('market::username'), config::byKey('market::password')); $request_http->setCURLOPT(array( - CURLOPT_CUSTOMREQUEST => "MKCOL" + CURLOPT_CUSTOMREQUEST => "MKCOL" )); $request_http->exec(); } @@ -281,13 +281,13 @@ public static function backup_send($_path) { com_shell::execute('sudo chmod 777 -R /tmp/jeedom_gnupg'); $cmd = 'echo "' . config::byKey('market::cloud::backup::password') . '" | gpg --homedir /tmp/jeedom_gnupg --batch --yes --passphrase-fd 0 -c ' . $_path; com_shell::execute($cmd); - $cmd = "curl --retry 5 --retry-delay 3 --retry-max-time 60 --retry-connrefused --keepalive-time 30 --user '".config::byKey('market::username').":".config::byKey('market::password')."' -T '".$_path . '.gpg'."' '".config::byKey('service::backup::url').'/webdav/'.urlencode(config::byKey('market::username')). '/' . rawurldecode(config::byKey('market::cloud::backup::name'))."/'"; - try { - com_shell::execute($cmd); - } catch (\Exception $e) { - $cmd = "curl --http1.1 --retry 5 --retry-delay 3 --retry-max-time 60 --retry-connrefused --keepalive-time 30 --user '".config::byKey('market::username').":".config::byKey('market::password')."' -T '".$_path . '.gpg'."' '".config::byKey('service::backup::url').'/webdav/'.urlencode(config::byKey('market::username')). '/' . rawurldecode(config::byKey('market::cloud::backup::name'))."/'"; - com_shell::execute($cmd); - } + $cmd = "curl --retry 5 --retry-delay 3 --retry-max-time 60 --retry-connrefused --keepalive-time 30 --user '" . config::byKey('market::username') . ":" . config::byKey('market::password') . "' -T '" . $_path . '.gpg' . "' '" . config::byKey('service::backup::url') . '/webdav/' . urlencode(config::byKey('market::username')) . '/' . rawurldecode(config::byKey('market::cloud::backup::name')) . "/'"; + try { + com_shell::execute($cmd); + } catch (\Exception $e) { + $cmd = "curl --http1.1 --retry 5 --retry-delay 3 --retry-max-time 60 --retry-connrefused --keepalive-time 30 --user '" . config::byKey('market::username') . ":" . config::byKey('market::password') . "' -T '" . $_path . '.gpg' . "' '" . config::byKey('service::backup::url') . '/webdav/' . urlencode(config::byKey('market::username')) . '/' . rawurldecode(config::byKey('market::cloud::backup::name')) . "/'"; + com_shell::execute($cmd); + } unlink($_path . '.gpg'); rrmdir('/tmp/jeedom_gnupg'); } catch (\Exception $e) { @@ -304,17 +304,17 @@ public static function backup_clean($_path) { $limit = 3700; self::backup_createFolderIsNotExist(); - $request_http = new com_http(config::byKey('service::backup::url').'/webdav/'.urlencode(config::byKey('market::username')),config::byKey('market::username'),config::byKey('market::password')); + $request_http = new com_http(config::byKey('service::backup::url') . '/webdav/' . urlencode(config::byKey('market::username')), config::byKey('market::username'), config::byKey('market::password')); $request_http->setCURLOPT(array( - CURLOPT_CUSTOMREQUEST => "PROPFIND" + CURLOPT_CUSTOMREQUEST => "PROPFIND" )); $xml = simplexml_load_string($request_http->exec()); $ns = $xml->getNamespaces(true); $child = $xml->children($ns['D']); $total_size = 0; - $files = []; + $files = []; foreach ($child->response as $file) { - if(!$file->propstat->prop->getcontenttype){ + if (!$file->propstat->prop->getcontenttype) { continue; } $files[] = array( @@ -341,9 +341,9 @@ public static function backup_clean($_path) { } $file = array_shift($files); echo __('Supression du backup cloud :', __FILE__) . ' ' . $file['name'] . "\n"; - $request_http = new com_http($file['href'],config::byKey('market::username'),config::byKey('market::password')); + $request_http = new com_http($file['href'], config::byKey('market::username'), config::byKey('market::password')); $request_http->setCURLOPT(array( - CURLOPT_CUSTOMREQUEST => "DELETE" + CURLOPT_CUSTOMREQUEST => "DELETE" )); $request_http->exec(); $total_size -= $file['size']; @@ -359,24 +359,24 @@ public static function backup_list() { return array(); } self::backup_createFolderIsNotExist(); - $request_http = new com_http(config::byKey('service::backup::url').'/webdav/'.urlencode(config::byKey('market::username')). '/' . rawurldecode(config::byKey('market::cloud::backup::name')),config::byKey('market::username'),config::byKey('market::password')); + $request_http = new com_http(config::byKey('service::backup::url') . '/webdav/' . urlencode(config::byKey('market::username')) . '/' . rawurldecode(config::byKey('market::cloud::backup::name')), config::byKey('market::username'), config::byKey('market::password')); $request_http->setCURLOPT(array( - CURLOPT_CUSTOMREQUEST => "PROPFIND" + CURLOPT_CUSTOMREQUEST => "PROPFIND" )); $xml = simplexml_load_string($request_http->exec()); $ns = $xml->getNamespaces(true); $child = $xml->children($ns['D']); $files = array(); foreach ($child->response as $file) { - if(!$file->propstat->prop->getcontenttype){ + if (!$file->propstat->prop->getcontenttype) { continue; } $files[] = array( - 'name' => (string) $file->propstat->prop->displayname, - 'timestamp' => strtotime($file->propstat->prop->getlastmodified) - ); + 'name' => (string) $file->propstat->prop->displayname, + 'timestamp' => strtotime($file->propstat->prop->getlastmodified) + ); } - function cmp($a,$b){ // $a,$b are reference to first index of array + function cmp($a, $b) { // $a,$b are reference to first index of array return strcmp($a["timestamp"], $b["timestamp"]); } usort($files, "cmp"); @@ -447,7 +447,7 @@ public static function cron5() { try { $result = json_decode($request_http->exec(60, 1), true); if ($result == null || $result['state'] != 'ok') { - sleep(rand(5,45)); + sleep(rand(5, 45)); $result = json_decode($request_http->exec(60, 1), true); } if ($result == null || $result['state'] != 'ok') { @@ -963,13 +963,13 @@ public function install($_version = 'stable') { } $url = config::byKey('market::address') . "/core/php/downloadFile.php?id=" . $this->getId(); - $url .='&version=' . $_version ; - $url .='&jeedomversion=' . jeedom::version(); - $url .='&osversion=' . system::getOsVersion(); - $url .='&hwkey=' . jeedom::getHardwareKey(); - $url .='&username=' . urlencode(config::byKey('market::username')); - $url .='&password=' . self::getPassword(); - $url .='&password_type=sha1'; + $url .= '&version=' . $_version; + $url .= '&jeedomversion=' . jeedom::version(); + $url .= '&osversion=' . system::getOsVersion(); + $url .= '&hwkey=' . jeedom::getHardwareKey(); + $url .= '&username=' . urlencode(config::byKey('market::username')); + $url .= '&password=' . self::getPassword(); + $url .= '&password_type=sha1'; log::add('update', 'alert', __('Téléchargement de', __FILE__) . ' ' . $this->getLogicalId() . '...'); log::add('update', 'alert', __('URL', __FILE__) . ' ' . $url); exec('wget "' . $url . '" -O ' . $tmp . ' >> ' . log::getPathToLog('update') . ' 2>&1'); @@ -1071,7 +1071,7 @@ public function save() { } if ($update->getSource() == 'market') { $update->setConfiguration('version', 'beta'); - $update->setLocalVersion(date('Y-m-d H:i:s',(int) strtotime('+10 minute' . date('Y-m-d H:i:s')))); + $update->setLocalVersion(date('Y-m-d H:i:s', (int) strtotime('+10 minute' . date('Y-m-d H:i:s')))); $update->save(); } $update->checkUpdate(); From ddc635535b5e0457672ef8e35753694ff5221d0e Mon Sep 17 00:00:00 2001 From: Salvialf Date: Fri, 13 Mar 2026 19:31:48 +0100 Subject: [PATCH 086/128] skip random sleep when credentials are not configured Avoids blocking cronHourly for up to 30 minutes when Market credentials are empty. --- core/repo/market.repo.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/repo/market.repo.php b/core/repo/market.repo.php index 32edd3a39d..03aa1855f7 100644 --- a/core/repo/market.repo.php +++ b/core/repo/market.repo.php @@ -417,6 +417,9 @@ public static function backup_restore($_backup) { /* * ***********************CRON*************************** */ public static function cronHourly() { + if (empty(config::byKey('market::username')) || empty(config::byKey('market::password'))) { + return; + } if (strtotime(config::byKey('market::lastCommunication', 'core', 0)) > (strtotime('now') - (24 * 3600))) { return; } From 2aab85496f2756c350dc275b7af9b41f7e9867b9 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Sat, 14 Mar 2026 11:50:56 +0100 Subject: [PATCH 087/128] plugins handle hardware-specific config --- core/class/config.class.php | 35 +++++++++++---------------------- core/config/specific.config.ini | 27 ------------------------- docs/fr_FR/changelog.md | 2 +- 3 files changed, 12 insertions(+), 52 deletions(-) delete mode 100644 core/config/specific.config.ini diff --git a/core/class/config.class.php b/core/class/config.class.php index 44d167b9f1..8f46a09f84 100644 --- a/core/class/config.class.php +++ b/core/class/config.class.php @@ -23,7 +23,6 @@ class config { /* * *************************Attributs****************************** */ private static $defaultConfiguration = array(); - private static $specificConfiguration = null; private static $cache = array(); private static $encryptKey = array('apipro', 'apitts', 'apimarket', 'samba::backup::password', 'samba::backup::ip', 'samba::backup::username', 'ldap:password', 'ldap:host', 'ldap:username', 'dns::token', 'api'); private static $nocache = array('enableScenario'); @@ -42,42 +41,30 @@ public static function getDefaultConfiguration(string $_plugin = 'core'): array if ($_plugin == 'core') { self::$defaultConfiguration[$_plugin] = parse_ini_file(__DIR__ . '/../../core/config/default.config.ini', true); + if (file_exists(__DIR__ . '/../../data/custom/custom.config.ini')) { $custom = parse_ini_file(__DIR__ . '/../../data/custom/custom.config.ini', true); self::$defaultConfiguration[$_plugin]['core'] = array_merge(self::$defaultConfiguration[$_plugin]['core'], $custom['core']); } } else { + self::$defaultConfiguration[$_plugin] = array(); + $filename = __DIR__ . '/../../plugins/' . $_plugin . '/core/config/' . $_plugin . '.config.ini'; if (file_exists($filename)) { self::$defaultConfiguration[$_plugin] = parse_ini_file($filename, true); } - } - - if (!isset(self::$defaultConfiguration[$_plugin])) { - self::$defaultConfiguration[$_plugin] = self::getSpecificConfiguration($_plugin); - } else { - self::$defaultConfiguration[$_plugin] = array_replace_recursive(self::$defaultConfiguration[$_plugin], self::getSpecificConfiguration($_plugin)); - } - return self::$defaultConfiguration[$_plugin]; - } - /** - * Get specific configuration for known boards - * @param string $_plugin - * @return array - */ - private static function getSpecificConfiguration(string $_plugin): array { - if (self::$specificConfiguration === null) { - self::$specificConfiguration = array(); - $specific = parse_ini_file(__DIR__ . '/../../core/config/specific.config.ini', true); - $hardware = strtolower(jeedom::getHardwareName()); - if (isset($specific[$hardware])) { - foreach ($specific[$hardware] as $plugin => $config) { - self::$specificConfiguration[$plugin] = array($plugin => $config); + $specificConf = __DIR__ . '/../../plugins/' . $_plugin . '/core/config/specific.config.ini'; + if (file_exists($specificConf)) { + $specific = parse_ini_file($specificConf, true); + $hardware = strtolower(jeedom::getHardwareName()); + if (isset($specific[$hardware])) { + self::$defaultConfiguration[$_plugin] = array_replace_recursive(self::$defaultConfiguration[$_plugin], $specific[$hardware]); } } } - return self::$specificConfiguration[$_plugin] ?? array(); + + return self::$defaultConfiguration[$_plugin]; } /** diff --git a/core/config/specific.config.ini b/core/config/specific.config.ini deleted file mode 100644 index 59e80b8377..0000000000 --- a/core/config/specific.config.ini +++ /dev/null @@ -1,27 +0,0 @@ -[atlas] - zwavejs[port] = Jeedom Atlas - - z2m[controller] = ezsp - z2m[port] = /dev/ttyS2 - z2m[z2m::mode] = local - - openenocean[port] = Jeedom Atlas - - eibd[KnxSoft] = knxd - eibd[level] = 3 - eibd[TypeKNXgateway] = ft12cemi - eibd[KNXgateway] = /dev/ttyS2 - -[luna] - zwavejs[port] = Jeedom Luna Zwave - - z2m[controller] = ember - z2m[port] = /dev/ttyLuna-Zigbee - z2m[z2m::mode] = local - - openenocean[port] = auto - -[smart] - zwavejs[port] = Jeedom Smart - - openenocean[port] = Jeedom Smart diff --git a/docs/fr_FR/changelog.md b/docs/fr_FR/changelog.md index d5b03aa25e..b361a96201 100644 --- a/docs/fr_FR/changelog.md +++ b/docs/fr_FR/changelog.md @@ -5,7 +5,7 @@ - Diverses corrections de bugs et optimisations - Mise à jour dans les contrôles de version - Mise à jour du nommage des ports de communication -- Ajout de configurations automatiques de certains plugins *(zwavejs, z2m, openenocean, eibd)* pour les boxes officielles +- [Développeurs] Ajout de la prise en charge de configurations spécifiques au matériel via un fichier `core/config/specific.config.ini` dans le dossier du plugin - [Développeurs] Ajout de la méthode `config::byValue($_value, string $_key = null)` # 4.5.2 From d85bbcdee5ab3d6b18484689df20a26ec24516ac Mon Sep 17 00:00:00 2001 From: Salvialf Date: Wed, 18 Mar 2026 16:37:37 +0100 Subject: [PATCH 088/128] format code --- core/class/plugin.class.php | 62 ++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/core/class/plugin.class.php b/core/class/plugin.class.php index 8f43d1e354..db71e29950 100644 --- a/core/class/plugin.class.php +++ b/core/class/plugin.class.php @@ -57,10 +57,10 @@ class plugin { /* * ***********************Méthodes statiques*************************** */ - public static function byId($_id,$_full = false) { + public static function byId($_id, $_full = false) { global $JEEDOM_INTERNAL_CONFIG; - if (is_string($_id) && isset(self::$_cache[$_id.'::'.$_full])) { - return self::$_cache[$_id.'::'.$_full]; + if (is_string($_id) && isset(self::$_cache[$_id . '::' . $_full])) { + return self::$_cache[$_id . '::' . $_full]; } if (!file_exists($_id) || strpos($_id, '/') === false) { $path = self::getPathById($_id); @@ -134,19 +134,19 @@ public static function byId($_id,$_full = false) { 'type' => 'class', ); } - - - $plugin->functionality['interact'] = array('exists' => method_exists($plugin->getId(), 'interact'), 'controlable' => 1); - $plugin->functionality['cron'] = array('exists' => method_exists($plugin->getId(), 'cron'), 'controlable' => 1); - $plugin->functionality['cron5'] = array('exists' => method_exists($plugin->getId(), 'cron5'), 'controlable' => 1); - $plugin->functionality['cron10'] = array('exists' => method_exists($plugin->getId(), 'cron10'), 'controlable' => 1); - $plugin->functionality['cron15'] = array('exists' => method_exists($plugin->getId(), 'cron15'), 'controlable' => 1); - $plugin->functionality['cron30'] = array('exists' => method_exists($plugin->getId(), 'cron30'), 'controlable' => 1); - $plugin->functionality['cronHourly'] = array('exists' => method_exists($plugin->getId(), 'cronHourly'), 'controlable' => 1); - $plugin->functionality['cronDaily'] = array('exists' => method_exists($plugin->getId(), 'cronDaily'), 'controlable' => 1); - $plugin->functionality['deadcmd'] = array('exists' => method_exists($plugin->getId(), 'deadCmd'), 'controlable' => 0); - $plugin->functionality['health'] = array('exists' => method_exists($plugin->getId(), 'health'), 'controlable' => 0); - + + + $plugin->functionality['interact'] = array('exists' => method_exists($plugin->getId(), 'interact'), 'controlable' => 1); + $plugin->functionality['cron'] = array('exists' => method_exists($plugin->getId(), 'cron'), 'controlable' => 1); + $plugin->functionality['cron5'] = array('exists' => method_exists($plugin->getId(), 'cron5'), 'controlable' => 1); + $plugin->functionality['cron10'] = array('exists' => method_exists($plugin->getId(), 'cron10'), 'controlable' => 1); + $plugin->functionality['cron15'] = array('exists' => method_exists($plugin->getId(), 'cron15'), 'controlable' => 1); + $plugin->functionality['cron30'] = array('exists' => method_exists($plugin->getId(), 'cron30'), 'controlable' => 1); + $plugin->functionality['cronHourly'] = array('exists' => method_exists($plugin->getId(), 'cronHourly'), 'controlable' => 1); + $plugin->functionality['cronDaily'] = array('exists' => method_exists($plugin->getId(), 'cronDaily'), 'controlable' => 1); + $plugin->functionality['deadcmd'] = array('exists' => method_exists($plugin->getId(), 'deadCmd'), 'controlable' => 0); + $plugin->functionality['health'] = array('exists' => method_exists($plugin->getId(), 'health'), 'controlable' => 0); + $plugin->functionality['interact'] = array('exists' => method_exists($plugin->getId(), 'interact'), 'controlable' => 1); $plugin->functionality['cron'] = array('exists' => method_exists($plugin->getId(), 'cron'), 'controlable' => 1); $plugin->functionality['cron5'] = array('exists' => method_exists($plugin->getId(), 'cron5'), 'controlable' => 1); @@ -157,11 +157,11 @@ public static function byId($_id,$_full = false) { $plugin->functionality['cronDaily'] = array('exists' => method_exists($plugin->getId(), 'cronDaily'), 'controlable' => 1); $plugin->functionality['deadcmd'] = array('exists' => method_exists($plugin->getId(), 'deadCmd'), 'controlable' => 0); $plugin->functionality['health'] = array('exists' => method_exists($plugin->getId(), 'health'), 'controlable' => 0); - if($_full){ - if($plugin->getCache('usedSpace',-1) == -1){ - $plugin->setCache('usedSpace',getDirectorySize(__DIR__ . '/../../plugins/' . $data['id']),86400); + if ($_full) { + if ($plugin->getCache('usedSpace', -1) == -1) { + $plugin->setCache('usedSpace', getDirectorySize(__DIR__ . '/../../plugins/' . $data['id']), 86400); } - $plugin->usedSpace = $plugin->getCache('usedSpace',-1); + $plugin->usedSpace = $plugin->getCache('usedSpace', -1); } if (!isset($JEEDOM_INTERNAL_CONFIG['plugin']['category'][$plugin->category])) { foreach ($JEEDOM_INTERNAL_CONFIG['plugin']['category'] as $key => $value) { @@ -174,7 +174,7 @@ public static function byId($_id,$_full = false) { } } } - self::$_cache[$plugin->id.'::'.$_full] = $plugin; + self::$_cache[$plugin->id . '::' . $_full] = $plugin; return $plugin; } @@ -619,14 +619,14 @@ public static function checkDeamon() { } } - public static function isInstalled($_pluginId): bool { - try { - plugin::byId($_pluginId); - return true; - } catch (Exception $e) { - return false; - } - } + public static function isInstalled($_pluginId): bool { + try { + plugin::byId($_pluginId); + return true; + } catch (Exception $e) { + return false; + } + } /* * *********************Méthodes d'instance************************* */ @@ -966,9 +966,9 @@ public function setIsEnable($_state, $_force = false, $_foreground = false) { } $osVersion = $this->getRequireOsVersion(); $distrib = system::getDistrib(); - if(isset($osVersion)){ + if (isset($osVersion)) { if ($distrib == 'debian' && version_compare(system::getOsVersion(), $osVersion) == -1 && $_state == 1) { - throw new Exception(__('Votre version Debian n\'est pas assez récente pour activer cette version du plugin, '.$osVersion.' minimum demandé', __FILE__)); + throw new Exception(__('Votre version Debian n\'est pas assez récente pour activer cette version du plugin, ' . $osVersion . ' minimum demandé', __FILE__)); } } $alreadyActive = config::byKey('active', $this->getId(), 0); From a10663d4e7f3c8b6e5f257162e0d16631b4862f7 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Wed, 18 Mar 2026 16:38:33 +0100 Subject: [PATCH 089/128] fix case --- core/class/plugin.class.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/class/plugin.class.php b/core/class/plugin.class.php index db71e29950..8fa4040df8 100644 --- a/core/class/plugin.class.php +++ b/core/class/plugin.class.php @@ -689,7 +689,7 @@ public function callInstallFunction($_function, $_direct = false) { public function dependancy_info($_refresh = false) { $plugin_id = $this->getId(); - $cache = cache::byKey('dependancy' . $this->getID()); + $cache = cache::byKey('dependancy' . $this->getId()); if ($_refresh) { $cache->remove(); } else { @@ -732,7 +732,7 @@ public function dependancy_info($_refresh = false) { } } if ($return['state'] == 'ok') { - cache::set('dependancy' . $this->getID(), $return); + cache::set('dependancy' . $this->getId(), $return); } return $return; } @@ -764,7 +764,7 @@ public function dependancy_info($_refresh = false) { $return['last_launch'] = config::byKey('lastDependancyInstallTime', $this->getId(), __('Inconnue', __FILE__)); $return['auto'] = config::byKey('dependancyAutoMode', $this->getId(), 1); if ($return['state'] == 'ok') { - cache::set('dependancy' . $this->getID(), $return); + cache::set('dependancy' . $this->getId(), $return); } return $return; } @@ -776,7 +776,7 @@ public function dependancy_info($_refresh = false) { public function dependancy_install($_force = false, $_foreground = false) { $plugin_id = $this->getId(); if (!$_force && config::byKey('dontProtectTooFastLaunchDependancy') == 0 && abs(strtotime('now') - strtotime(config::byKey('lastDependancyInstallTime', $plugin_id))) <= 60) { - $cache = cache::byKey('dependancy' . $this->getID()); + $cache = cache::byKey('dependancy' . $this->getId()); $cache->remove(); throw new Exception(__('Vous devez attendre au moins 60 secondes entre deux lancements d\'installation de dépendances', __FILE__)); } @@ -788,7 +788,7 @@ public function dependancy_install($_force = false, $_foreground = false) { $this->deamon_stop(); config::save('lastDependancyInstallTime', date('Y-m-d H:i:s'), $plugin_id); system::checkAndInstall(json_decode(file_get_contents(__DIR__ . '/../../plugins/' . $plugin_id . '/plugin_info/packages.json'), true), true, $_foreground, $plugin_id, $_force); - $cache = cache::byKey('dependancy' . $this->getID()); + $cache = cache::byKey('dependancy' . $this->getId()); $cache->remove(); return; } @@ -831,7 +831,7 @@ public function dependancy_install($_force = false, $_foreground = false) { log::add($plugin_id, 'error', __('Aucun script ne correspond à votre type de Linux :', __FILE__) . ' ' . $cmd['script'] . ' ' . __('avec #stype# :', __FILE__) . ' ' . system::get('type')); } } - $cache = cache::byKey('dependancy' . $this->getID()); + $cache = cache::byKey('dependancy' . $this->getId()); $cache->remove(); return; } From 6c6f33a0bd273c83bc26a7c9f6795d2c330ca27b Mon Sep 17 00:00:00 2001 From: Salvialf Date: Wed, 18 Mar 2026 16:53:31 +0100 Subject: [PATCH 090/128] additionnal dependancy check only when previously ok --- core/class/plugin.class.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/class/plugin.class.php b/core/class/plugin.class.php index 8fa4040df8..a31c88fd3a 100644 --- a/core/class/plugin.class.php +++ b/core/class/plugin.class.php @@ -725,13 +725,15 @@ public function dependancy_info($_refresh = false) { } $return['last_launch'] = config::byKey('lastDependancyInstallTime', $this->getId(), __('Inconnue', __FILE__)); $return['auto'] = config::byKey('dependancyAutoMode', $this->getId(), 1); - if ($return['state'] != 'in_progress' && method_exists($plugin_id, 'additionnalDependancyCheck')) { + + if ($return['state'] === 'ok' && method_exists($plugin_id, 'additionnalDependancyCheck')) { $additionnal = $plugin_id::additionnalDependancyCheck(); if (isset($additionnal['state'])) { $return['state'] = $additionnal['state']; } } - if ($return['state'] == 'ok') { + + if ($return['state'] === 'ok') { cache::set('dependancy' . $this->getId(), $return); } return $return; From b1b3a2e99187966e4aec924b3beb97c32a9346d1 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Thu, 19 Mar 2026 20:12:46 +0100 Subject: [PATCH 091/128] add 4.5.3 details --- docs/fr_FR/changelog.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/fr_FR/changelog.md b/docs/fr_FR/changelog.md index b361a96201..9869a128fd 100644 --- a/docs/fr_FR/changelog.md +++ b/docs/fr_FR/changelog.md @@ -4,9 +4,9 @@ - Diverses corrections de bugs et optimisations - Mise à jour dans les contrôles de version -- Mise à jour du nommage des ports de communication -- [Développeurs] Ajout de la prise en charge de configurations spécifiques au matériel via un fichier `core/config/specific.config.ini` dans le dossier du plugin -- [Développeurs] Ajout de la méthode `config::byValue($_value, string $_key = null)` +- Mise à jour du nommage des ports de communication ([Détails](https://github.com/jeedom/core/issues/3210)) +- [Développeurs] Ajout de la prise en charge de configurations spécifiques au matériel via un fichier `core/config/specific.config.ini` dans le dossier du plugin ([Détails](https://github.com/jeedom/core/issues/3218)) +- [Développeurs] Ajout de la méthode `config::byValue($_value, string $_key = null)`([Détails](https://github.com/jeedom/core/pull/3212/changes/559a010)) # 4.5.2 From 5c923c5689ba76d0acf0241846d69a30f0a50ede Mon Sep 17 00:00:00 2001 From: David <79108364+Phpvarious@users.noreply.github.com> Date: Wed, 25 Mar 2026 21:19:03 +0100 Subject: [PATCH 092/128] Update plugin.ajax.php Add health into CommunityPost --- core/ajax/plugin.ajax.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/core/ajax/plugin.ajax.php b/core/ajax/plugin.ajax.php index a6face2566..397426fcef 100644 --- a/core/ajax/plugin.ajax.php +++ b/core/ajax/plugin.ajax.php @@ -180,6 +180,14 @@ $infoPost .= ' - (' . ($daemon_info['last_launch'] ?? __('Inconnue', __FILE__)) . ')'; } + $healt = '[details="' . __('Santé', __FILE__) . '"]'; + $healt .= '
```
'; + foreach ((jeedom::health()) as $datas) { + $healt .= $datas['name'] . ' : ' . str_replace(["\r","\n"], " ", $datas['result']) . '
'; + } + $healt .= '
```
'; + $healt .= '[/details]
'; + $infoPlugin = ''; if (method_exists($plugin_id, 'getConfigForCommunity')) { $infoPlugin .= '**' . __('Informations complémentaires', __FILE__) . '**
'; @@ -190,7 +198,7 @@ $communitUrl = 'https://community.jeedom.com'; $ressource = '/new-topic?'; - $finalBody = br2nl($header . $infoPost . $footer . $infoPlugin); + $finalBody = br2nl($header . $infoPost . $footer . $healt . $infoPlugin); $data = array( 'category' => 'plugins/' . $plugin->getCategory(), From 12606c3d43d115279693f14634ff5f16e0fd17cf Mon Sep 17 00:00:00 2001 From: David <79108364+Phpvarious@users.noreply.github.com> Date: Wed, 25 Mar 2026 21:52:03 +0100 Subject: [PATCH 093/128] Update plugin.ajax.php --- core/ajax/plugin.ajax.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/ajax/plugin.ajax.php b/core/ajax/plugin.ajax.php index 397426fcef..c5b95894d5 100644 --- a/core/ajax/plugin.ajax.php +++ b/core/ajax/plugin.ajax.php @@ -183,6 +183,13 @@ $healt = '[details="' . __('Santé', __FILE__) . '"]'; $healt .= '
```
'; foreach ((jeedom::health()) as $datas) { + if ($datas['state'] === 2) { + $healt .= "🟠 "; + } else if ($datas['state']) { + $healt .= "🟢 "; + } else { + $healt .= "🔴 "; + } $healt .= $datas['name'] . ' : ' . str_replace(["\r","\n"], " ", $datas['result']) . '
'; } $healt .= '
```
'; From b246629f9cb752caa0904710e86d0400bad6d2d5 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Tue, 31 Mar 2026 10:39:04 +0200 Subject: [PATCH 094/128] update changelog --- docs/fr_FR/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/fr_FR/changelog.md b/docs/fr_FR/changelog.md index 9869a128fd..433b488ab4 100644 --- a/docs/fr_FR/changelog.md +++ b/docs/fr_FR/changelog.md @@ -5,6 +5,7 @@ - Diverses corrections de bugs et optimisations - Mise à jour dans les contrôles de version - Mise à jour du nommage des ports de communication ([Détails](https://github.com/jeedom/core/issues/3210)) +- Ajout du contenu de la page "Santé" aux demandes d'assistance communautaire ([Détails](https://github.com/jeedom/core/pull/3224)) - [Développeurs] Ajout de la prise en charge de configurations spécifiques au matériel via un fichier `core/config/specific.config.ini` dans le dossier du plugin ([Détails](https://github.com/jeedom/core/issues/3218)) - [Développeurs] Ajout de la méthode `config::byValue($_value, string $_key = null)`([Détails](https://github.com/jeedom/core/pull/3212/changes/559a010)) From cf3ab4792356c7ad8e21181d650b89e4d1b62337 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Tue, 31 Mar 2026 10:40:31 +0200 Subject: [PATCH 095/128] typo --- docs/fr_FR/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/fr_FR/changelog.md b/docs/fr_FR/changelog.md index 433b488ab4..b08b2c3485 100644 --- a/docs/fr_FR/changelog.md +++ b/docs/fr_FR/changelog.md @@ -5,7 +5,7 @@ - Diverses corrections de bugs et optimisations - Mise à jour dans les contrôles de version - Mise à jour du nommage des ports de communication ([Détails](https://github.com/jeedom/core/issues/3210)) -- Ajout du contenu de la page "Santé" aux demandes d'assistance communautaire ([Détails](https://github.com/jeedom/core/pull/3224)) +- Ajout du contenu de la page "Santé" aux demandes d'assistance communautaire ([Détails](https://github.com/jeedom/core/pull/3224)) - [Développeurs] Ajout de la prise en charge de configurations spécifiques au matériel via un fichier `core/config/specific.config.ini` dans le dossier du plugin ([Détails](https://github.com/jeedom/core/issues/3218)) - [Développeurs] Ajout de la méthode `config::byValue($_value, string $_key = null)`([Détails](https://github.com/jeedom/core/pull/3212/changes/559a010)) From abd3dbc118d30cec5521019e5701c7d7b81734dc Mon Sep 17 00:00:00 2001 From: Salvialf Date: Tue, 31 Mar 2026 12:11:05 +0200 Subject: [PATCH 096/128] update changelog --- docs/fr_FR/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/fr_FR/changelog.md b/docs/fr_FR/changelog.md index b08b2c3485..367752a20c 100644 --- a/docs/fr_FR/changelog.md +++ b/docs/fr_FR/changelog.md @@ -5,6 +5,7 @@ - Diverses corrections de bugs et optimisations - Mise à jour dans les contrôles de version - Mise à jour du nommage des ports de communication ([Détails](https://github.com/jeedom/core/issues/3210)) +- Limitation de la taille maximale des fichiers de logs ([Détails](https://github.com/jeedom/core/pull/3045)) - Ajout du contenu de la page "Santé" aux demandes d'assistance communautaire ([Détails](https://github.com/jeedom/core/pull/3224)) - [Développeurs] Ajout de la prise en charge de configurations spécifiques au matériel via un fichier `core/config/specific.config.ini` dans le dossier du plugin ([Détails](https://github.com/jeedom/core/issues/3218)) - [Développeurs] Ajout de la méthode `config::byValue($_value, string $_key = null)`([Détails](https://github.com/jeedom/core/pull/3212/changes/559a010)) From 9fdafe639aad7ca8f392a00fba0ccd1683f8179a Mon Sep 17 00:00:00 2001 From: Mips2648 Date: Tue, 31 Mar 2026 15:23:44 +0200 Subject: [PATCH 097/128] fix: correct token handling in curl command for GitHub API requests --- core/repo/github.repo.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/repo/github.repo.php b/core/repo/github.repo.php index dee5e6666f..f76cf5e179 100644 --- a/core/repo/github.repo.php +++ b/core/repo/github.repo.php @@ -143,7 +143,7 @@ public static function downloadObject($_update) { if ($token == '') { exec('curl -s -f -L -o ' . escapeshellarg($tmp) . ' ' . escapeshellarg($url) . ' 2>&1', $output, $return_code); } else { - exec('curl -s -f -L -H "Authorization: token ' . escapeshellarg($token) . '" -o ' . escapeshellarg($tmp) . ' ' . escapeshellarg($url) . ' 2>&1', $output, $return_code); + exec('curl -s -f -L -H ' . escapeshellarg('Authorization: token ' . $token) . ' -o ' . escapeshellarg($tmp) . ' ' . escapeshellarg($url) . ' 2>&1', $output, $return_code); } if ($return_code !== 0 || !file_exists($tmp) || filesize($tmp) == 0) { $error_msg = __('Erreur lors du téléchargement', __FILE__); From 0291729bfdd9a001199acd2787b4a685122fb5a5 Mon Sep 17 00:00:00 2001 From: Mips Date: Wed, 1 Apr 2026 14:40:14 +0200 Subject: [PATCH 098/128] reduce if-else nesting Co-authored-by: kwizer15 --- core/class/scenarioExpression.class.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/class/scenarioExpression.class.php b/core/class/scenarioExpression.class.php index 8948708d39..5e35b18d02 100644 --- a/core/class/scenarioExpression.class.php +++ b/core/class/scenarioExpression.class.php @@ -223,7 +223,9 @@ public static function randText($_sValue) { if (is_array($result)) { $nbr = mt_rand(0, count($result) - 1); return $result[$nbr]; - } else { + } + return $result; + } return $result; } } From fb4ec936411bde3e5dcde3df29b17cca538cde9a Mon Sep 17 00:00:00 2001 From: Mips Date: Wed, 1 Apr 2026 15:56:01 +0200 Subject: [PATCH 099/128] Fix random selection from empty array in scenarioExpression --- core/class/scenarioExpression.class.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/core/class/scenarioExpression.class.php b/core/class/scenarioExpression.class.php index 5e35b18d02..ce0c39a5ea 100644 --- a/core/class/scenarioExpression.class.php +++ b/core/class/scenarioExpression.class.php @@ -221,13 +221,12 @@ public static function randText($_sValue) { $result = $_aValue; } if (is_array($result)) { - $nbr = mt_rand(0, count($result) - 1); - return $result[$nbr]; + if (count($result) === 0) { + return ''; + } + return $result[array_rand($result)]; } return $result; - } - return $result; - } } public static function scenario($_scenario) { From dfe75d094a13c16e0ef56d9fe6950eaf1c9b9cda Mon Sep 17 00:00:00 2001 From: Francois Gallienne Date: Wed, 1 Apr 2026 12:50:04 +0200 Subject: [PATCH 100/128] fix: resolve 3 bugs in proxy configuration in jsonrpcClient - Use strict comparison (== 1) for proxyEnabled to avoid truthy evaluation of unexpected default values - Fix empty() receiving boolean from || operator instead of individual config values - Replace hardcoded 'proxyLogin:proxyPassword' string with actual config::byKey() values - Add proper error handling when proxy address or port is missing - Remove log::add('Connection', 'debug', $ch) which logs a curl resource (not useful) --- core/class/jsonrpcClient.class.php | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/core/class/jsonrpcClient.class.php b/core/class/jsonrpcClient.class.php index 5a00138230..c1df3395a5 100644 --- a/core/class/jsonrpcClient.class.php +++ b/core/class/jsonrpcClient.class.php @@ -138,21 +138,22 @@ private function send($_request, $_timeout = 15, $_file = null, $_maxRetry = 2) curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); } - if(config::byKey('proxyEnabled')) { - if(config::byKey('proxyAddress') == ''){ - // throw new Exception(__('renseigne l\'adresse', __FILE__)); - $this->error = 'Erreur address '; - } else if (config::byKey('proxyPort') == ''){ - // throw new Exception(__('renseigne le port', __FILE__)); - } else { - curl_setopt($ch, CURLOPT_PROXY, config::byKey('proxyAddress')); - curl_setopt($ch, CURLOPT_PROXYPORT, config::byKey('proxyPort')); - if(!empty(config::byKey('proxyLogin') || config::byKey('proxyPassword'))){ - curl_setopt($ch, CURLOPT_PROXYUSERPWD, 'proxyLogin:proxyPassword'); + if (config::byKey('proxyEnabled') == 1) { + $proxyAddress = config::byKey('proxyAddress'); + $proxyPort = config::byKey('proxyPort'); + if ($proxyAddress != '' && $proxyPort != '') { + curl_setopt($ch, CURLOPT_PROXY, $proxyAddress); + curl_setopt($ch, CURLOPT_PROXYPORT, $proxyPort); + $proxyLogin = config::byKey('proxyLogin'); + $proxyPassword = config::byKey('proxyPassword'); + if (!empty($proxyLogin) || !empty($proxyPassword)) { + curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxyLogin . ':' . $proxyPassword); + } + } else { + $this->error = 'Proxy enabled but address or port is missing'; + log::add('connection', 'error', $this->error); } - log::add('Connection', 'debug', $ch); - } - } + } $response = curl_exec($ch); $http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE); $nbRetry++; From edfd09383a11b4508da7ad0eca54f56e8042baee Mon Sep 17 00:00:00 2001 From: Mips2648 Date: Fri, 3 Apr 2026 14:33:03 +0200 Subject: [PATCH 101/128] fix: update GitHub workflows to use 'develop' branch instead of 'alpha' and 'beta' --- .github/workflows/composer-validate.yml | 4 ++-- .github/workflows/docker-test.yml | 8 ++++---- .github/workflows/work.yml | 6 ++---- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/.github/workflows/composer-validate.yml b/.github/workflows/composer-validate.yml index 869b13d00d..07bd51ee4e 100644 --- a/.github/workflows/composer-validate.yml +++ b/.github/workflows/composer-validate.yml @@ -6,8 +6,8 @@ name: Validate Composer dependencies on: pull_request: branches: - - alpha - + - 'develop' + jobs: build-test: runs-on: ubuntu-latest diff --git a/.github/workflows/docker-test.yml b/.github/workflows/docker-test.yml index ac272915bc..62b2613362 100644 --- a/.github/workflows/docker-test.yml +++ b/.github/workflows/docker-test.yml @@ -7,19 +7,18 @@ on: # only trigger CI when pull request on following branches pull_request: branches: - - 'alpha' + - 'develop' # this is to manually trigger the worklow workflow_dispatch: inputs: logLevel: - description: 'Reason' + description: 'Reason' default: 'Manual launch' jobs: buildAndTestDockerImg: runs-on: ubuntu-latest steps: - - name: Check Out Repo # Check out the repo, using current branch # https://github.com/marketplace/actions/checkout @@ -35,6 +34,7 @@ jobs: docker run -p 80:80 -d --rm --name jeedom_bullseye jeedom:bullseye sleep 45 docker exec jeedom_bullseye php sick.php + - name: Build jeedom:bookworm # build current image for bookworm run: | @@ -45,7 +45,7 @@ jobs: docker run -p 81:80 -d --rm --name jeedom_bookworm jeedom:bookworm sleep 45 docker exec jeedom_bookworm php sick.php - + - name: Clean docker image continue-on-error: true run: | diff --git a/.github/workflows/work.yml b/.github/workflows/work.yml index 9db6932f30..960eb4e76c 100644 --- a/.github/workflows/work.yml +++ b/.github/workflows/work.yml @@ -2,12 +2,10 @@ on: # Workflows check plugin Jeedom push: branches: - - alpha - - beta + - develop pull_request: branches: - - alpha - - beta + - develop - master name : 'Test Core Jeedom' From f4ebd4194182c998fe0250b4922f527db634e65b Mon Sep 17 00:00:00 2001 From: Mips2648 Date: Fri, 3 Apr 2026 18:13:15 +0200 Subject: [PATCH 102/128] =?UTF-8?q?typo=20Securit=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- desktop/php/administration.php | 76 +++++++++++++++++----------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/desktop/php/administration.php b/desktop/php/administration.php index 341e592252..f226834255 100644 --- a/desktop/php/administration.php +++ b/desktop/php/administration.php @@ -41,7 +41,7 @@
  • {{Rapports}}
  • {{Liens}}
  • {{Interactions}}
  • -
  • {{Securité}}
  • +
  • {{Sécurité}}
  • {{Mises à jour/Market}}
  • {{Cache}}
  • {{API}}
  • @@ -1134,15 +1134,15 @@
    - +
    @@ -1767,11 +1767,11 @@ foreach ($ban_ips as $ip => $datetime) { $div .= ''; $div .= '' . $ip . ''; - $div .= '' . date('Y-m-d H:i:s',(int) $datetime) . ''; + $div .= '' . date('Y-m-d H:i:s', (int) $datetime) . ''; if (config::byKey('security::bantime') == -1) { $div .= '{{Jamais}}'; } else { - $div .= '' . date('Y-m-d H:i:s',(int) ($datetime + config::byKey('security::bantime'))) . ''; + $div .= '' . date('Y-m-d H:i:s', (int) ($datetime + config::byKey('security::bantime'))) . ''; } $div .= ''; } @@ -1820,52 +1820,52 @@ - getValue(array()); - if(!isset($lists['branchs']) || !is_array($lists['branchs'])){ + if (!isset($lists['branchs']) || !is_array($lists['branchs'])) { $request_http = new com_http('https://api.github.com/repos/jeedom/core/branches'); $request_http->setHeader(array('User-agent: jeedom')); try { $lists['branchs'] = json_decode($request_http->exec(10, 1), true); } catch (\Exception $e) { } - cache::set('core::branch::default::list',$lists,86400); + cache::set('core::branch::default::list', $lists, 86400); } - if(!isset($lists['tags']) || !is_array($lists['tags'])){ + if (!isset($lists['tags']) || !is_array($lists['tags'])) { $request_http = new com_http('https://api.github.com/repos/jeedom/core/tags'); $request_http->setHeader(array('User-agent: jeedom')); try { $lists['tags'] = json_decode($request_http->exec(10, 1), true); } catch (\Exception $e) { } - cache::set('core::branch::default::list',$lists,86400); - } - if(isset($lists['branchs']) && is_array($lists['branchs'])){ - echo ''; - foreach ($lists['branchs'] as $branch) { - if(!is_array($branch) || !isset($branch['name'])){ - continue; - } - if(in_array($branch['name'],array('V4-stable','master'))){ - continue; + cache::set('core::branch::default::list', $lists, 86400); + } + if (isset($lists['branchs']) && is_array($lists['branchs'])) { + echo ''; + foreach ($lists['branchs'] as $branch) { + if (!is_array($branch) || !isset($branch['name'])) { + continue; + } + if (in_array($branch['name'], array('V4-stable', 'master'))) { + continue; + } + echo ''; } - echo ''; + echo ''; } - echo ''; - } - if(isset($lists['tags']) && is_array($lists['tags'])){ - echo ''; - foreach ($lists['tags'] as $tag) { - if(!is_array($tag) || !isset($tag['name'])){ - continue; + if (isset($lists['tags']) && is_array($lists['tags'])) { + echo ''; + foreach ($lists['tags'] as $tag) { + if (!is_array($tag) || !isset($tag['name'])) { + continue; + } + echo ''; } - echo ''; + echo ''; } - echo ''; } - } - ?> + ?> @@ -2337,4 +2337,4 @@
    - + \ No newline at end of file From 8cd07194a66a6294d2d824b44d08c0d46eb76fc3 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Sat, 4 Apr 2026 17:36:30 +0200 Subject: [PATCH 103/128] implode groupingType if is array --- core/ajax/cmd.ajax.php | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/core/ajax/cmd.ajax.php b/core/ajax/cmd.ajax.php index 9b858a2dbc..c579a7163a 100644 --- a/core/ajax/cmd.ajax.php +++ b/core/ajax/cmd.ajax.php @@ -246,12 +246,12 @@ } if (init('action') == 'byEqLogic') { - if (init('typeCmd')) { - ajax::success(utils::o2a(cmd::byEqLogicId(init('eqLogic_id'), init('typeCmd')))); - } else { - ajax::success(utils::o2a(cmd::byEqLogicId(init('eqLogic_id')))); - } - } + if (init('typeCmd')) { + ajax::success(utils::o2a(cmd::byEqLogicId(init('eqLogic_id'), init('typeCmd')))); + } else { + ajax::success(utils::o2a(cmd::byEqLogicId(init('eqLogic_id')))); + } + } if (init('action') == 'getCmd') { $cmd = cmd::byId(init('id')); @@ -422,6 +422,9 @@ } $return['derive'] = $derive; $groupingType = init('groupingType'); + if (is_array($groupingType)) { + $groupingType = implode('::', array_values($groupingType)); + } if ($groupingType == '') { $groupingType = $cmd->getDisplay('groupingType'); } @@ -497,7 +500,7 @@ if (init('action') == 'getLastHistory') { $cmd = cmd::byId(init('id')); $_time = date('Y-m-d H:i:s', strtotime(init('time'))); - if(is_object($cmd)){ + if (is_object($cmd)) { ajax::success($cmd->getLastHistory($_time)); } else { throw new Exception(__('Nombre maximum de niveaux d’éléments affichés dans les graphiques de liens', __FILE__) . ' ' . init('id')); From 67bda288add1c9db1198035d4fc22e62fb45b9e4 Mon Sep 17 00:00:00 2001 From: Salvialf Date: Sat, 4 Apr 2026 21:32:26 +0200 Subject: [PATCH 104/128] Explicitly handle groupingType arrays with function/time keys --- core/ajax/cmd.ajax.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/ajax/cmd.ajax.php b/core/ajax/cmd.ajax.php index c579a7163a..0acbe7c6b3 100644 --- a/core/ajax/cmd.ajax.php +++ b/core/ajax/cmd.ajax.php @@ -423,7 +423,11 @@ $return['derive'] = $derive; $groupingType = init('groupingType'); if (is_array($groupingType)) { - $groupingType = implode('::', array_values($groupingType)); + if (isset($groupingType['function']) && isset($groupingType['time'])) { + $groupingType = $groupingType['function'] . '::' . $groupingType['time']; + } else { + $groupingType = implode('::', array_values($groupingType)); + } } if ($groupingType == '') { $groupingType = $cmd->getDisplay('groupingType'); From af9daa2a3c4dd262e7b77c979ca6016cea84c250 Mon Sep 17 00:00:00 2001 From: Olivier <16240457+TiTidom-RC@users.noreply.github.com> Date: Mon, 6 Apr 2026 18:50:53 +0200 Subject: [PATCH 105/128] feat: add logProtect() support in log::remove() Plugins can now declare a static logProtect(): array method to prevent specific log files from being deleted by log::remove(). Protected logs are cleared (log::clear) instead of removed. Pattern identical to backupExclude() in install/backup.php. Fully backward-compatible: plugins without logProtect() are unaffected. --- core/class/log.class.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/class/log.class.php b/core/class/log.class.php index 5c06f73494..ccffa633da 100644 --- a/core/class/log.class.php +++ b/core/class/log.class.php @@ -223,6 +223,15 @@ public static function remove($_log) { self::clear($_log); return; } + foreach (plugin::listPlugin(true) as $plugin) { + $plugin_id = $plugin->getId(); + if (method_exists($plugin_id, 'logProtect')) { + if (in_array($_log, $plugin_id::logProtect(), true)) { + self::clear($_log); + return; + } + } + } if (self::authorizeClearLog($_log)) { $path = self::getPathToLog($_log); com_shell::execute(system::getCmdSudo() . 'chmod 664 ' . $path . ' > /dev/null 2>&1;' . system::getCmdSudo() . 'chown -R ' . system::get('www-uid') . ':' . system::get('www-gid') . ' ' . $path . ' > /dev/null 2>&1;' . system::getCmdSudo() . ' cat /dev/null > ' . $path . ';rm ' . $path . ' 2>&1 > /dev/null'); From 304b1703888a8ef925a67056abbbe7fceaa2a058 Mon Sep 17 00:00:00 2001 From: Michel F <80367602+MrWaloo@users.noreply.github.com> Date: Tue, 7 Apr 2026 09:04:41 +0200 Subject: [PATCH 106/128] Correction *aucun* script --- desktop/php/update.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/php/update.php b/desktop/php/update.php index 5d7295083a..ea18cb41bd 100644 --- a/desktop/php/update.php +++ b/desktop/php/update.php @@ -185,7 +185,7 @@
    From 8504e00d7c20883e39f4333d310f07896a37aaf0 Mon Sep 17 00:00:00 2001 From: Emmanuel Bernaszuk Date: Fri, 17 Apr 2026 10:24:21 +0200 Subject: [PATCH 122/128] =?UTF-8?q?Corriger=20l'=C3=A9chec=20du=20step=20d?= =?UTF-8?q?e=20suppression=20de=20branche=20dans=20le=20workflow=20PHPStan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git ls-remote --exit-code retourne 2 quand la branche n'existe pas, ce qui, combiné à bash -e, faisait échouer tout le step. Remplacement par une condition if/grep pour ne pas propager le code de sortie. --- .github/workflows/phpstan.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/phpstan.yaml b/.github/workflows/phpstan.yaml index 1954106f8c..be829bf51c 100644 --- a/.github/workflows/phpstan.yaml +++ b/.github/workflows/phpstan.yaml @@ -48,7 +48,9 @@ jobs: - name: Delete existing branch if exists run: | - git ls-remote --exit-code --heads origin update-phpstan-baseline && git push origin --delete update-phpstan-baseline + if git ls-remote --heads origin update-phpstan-baseline | grep -q update-phpstan-baseline; then + git push origin --delete update-phpstan-baseline + fi - name: Setup PHP 7.4 for dependencies uses: shivammathur/setup-php@v2 From e4e176779eddc51b4751ebd29e21004410897f8d Mon Sep 17 00:00:00 2001 From: Mips2648 Date: Fri, 17 Apr 2026 12:03:28 +0000 Subject: [PATCH 123/128] Update PHPStan baseline --- phpstan-baseline.neon | 114 ------------------------------------------ 1 file changed, 114 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index fec11fd743..678a40ab7c 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,95 +1,35 @@ parameters: ignoreErrors: - - - message: '#^Static method cache\:\:set\(\) invoked with 4 parameters, 2\-3 required\.$#' - identifier: arguments.count - count: 1 - path: core/ajax/cache.ajax.php - - - - message: '#^Static call to instance method cmd\:\:dropInfluxDatabase\(\)\.$#' - identifier: method.staticCall - count: 1 - path: core/ajax/cmd.ajax.php - - message: '#^Static call to instance method cmd\:\:historyInfluxAll\(\)\.$#' identifier: method.staticCall count: 1 path: core/ajax/cmd.ajax.php - - - message: '#^Static method history\:\:getHistoryFromCalcul\(\) invoked with 6 parameters, 1\-5 required\.$#' - identifier: arguments.count - count: 1 - path: core/ajax/cmd.ajax.php - - message: '#^Variable \$eqLogic might not be defined\.$#' identifier: variable.undefined count: 1 path: core/ajax/eqLogic.ajax.php - - - message: '#^Static method interactQuery\:\:byInteractDefId\(\) invoked with 2 parameters, 1 required\.$#' - identifier: arguments.count - count: 2 - path: core/ajax/interact.ajax.php - - - - message: '#^Static method listener\:\:all\(\) invoked with 1 parameter, 0 required\.$#' - identifier: arguments.count - count: 1 - path: core/ajax/listener.ajax.php - - - - message: '#^Static method jeeObject\:\:getUISelectList\(\) invoked with 2 parameters, 0\-1 required\.$#' - identifier: arguments.count - count: 1 - path: core/ajax/object.ajax.php - - - - message: '#^Static method queue\:\:all\(\) invoked with 1 parameter, 0 required\.$#' - identifier: arguments.count - count: 1 - path: core/ajax/queue.ajax.php - - message: '#^Variable \$market might not be defined\.$#' identifier: variable.undefined count: 1 path: core/ajax/repo.ajax.php - - - message: '#^Variable \$return might not be defined\.$#' - identifier: variable.undefined - count: 1 - path: core/ajax/report.ajax.php - - message: '#^Variable \$converts might not be defined\.$#' identifier: variable.undefined count: 2 path: core/ajax/scenario.ajax.php - - - message: '#^Result of static method user\:\:removeBanIp\(\) \(void\) is used\.$#' - identifier: staticMethod.void - count: 1 - path: core/ajax/user.ajax.php - - message: '#^Variable \$configs might not be defined\.$#' identifier: variable.undefined count: 1 path: core/ajax/user.ajax.php - - - message: '#^Static method jeedom\:\:update\(\) invoked with 2 parameters, 0\-1 required\.$#' - identifier: arguments.count - count: 2 - path: core/api/jeeApi.php - - message: '#^Variable \$cmd might not be defined\.$#' identifier: variable.undefined @@ -126,42 +66,6 @@ parameters: count: 2 path: core/api/jeeApi.php - - - message: '#^Call to static method byId\(\) on an unknown class jeeNetwork\.$#' - identifier: class.notFound - count: 1 - path: core/api/proApi.php - - - - message: '#^Call to static method byId\(\) on an unknown class market\.$#' - identifier: class.notFound - count: 2 - path: core/api/proApi.php - - - - message: '#^Call to static method byLogicalId\(\) on an unknown class market\.$#' - identifier: class.notFound - count: 1 - path: core/api/proApi.php - - - - message: '#^Call to static method testMaster\(\) on an unknown class jeeNetwork\.$#' - identifier: class.notFound - count: 1 - path: core/api/proApi.php - - - - message: '#^Static method jeedom\:\:update\(\) invoked with 2 parameters, 0\-1 required\.$#' - identifier: arguments.count - count: 2 - path: core/api/proApi.php - - - - message: '#^Static method repo_market\:\:backup_restore\(\) invoked with 2 parameters, 1 required\.$#' - identifier: arguments.count - count: 1 - path: core/api/proApi.php - - message: '#^Instantiated class InfluxDB\\Client not found\.$#' identifier: class.notFound @@ -180,12 +84,6 @@ parameters: count: 3 path: core/class/history.class.php - - - message: '#^Static call to instance method interactQuery\:\:replaceForContextual\(\)\.$#' - identifier: method.staticCall - count: 3 - path: core/class/interactQuery.class.php - - message: '#^Variable \$closest might not be defined\.$#' identifier: variable.undefined @@ -258,18 +156,6 @@ parameters: count: 1 path: core/class/scenario.class.php - - - message: '#^Instantiated class SolarData\\SolarData not found\.$#' - identifier: class.notFound - count: 1 - path: core/class/scenarioExpression.class.php - - - - message: '#^Static method update\:\:getLastAvailableVersion\(\) invoked with 1 parameter, 0 required\.$#' - identifier: arguments.count - count: 1 - path: core/class/update.class.php - - message: '#^Variable \$url might not be defined\.$#' identifier: variable.undefined From 9fc569479e60a57d0e61db04848ecaac30454d25 Mon Sep 17 00:00:00 2001 From: Emmanuel Bernaszuk Date: Sat, 18 Apr 2026 14:37:49 +0200 Subject: [PATCH 124/128] =?UTF-8?q?Corriger=20l'injection=20SQL=20dans=20s?= =?UTF-8?q?etComponentOrder=20et=20ajouter=20des=20tests=20d'int=C3=A9grat?= =?UTF-8?q?ion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le champ `type` envoyé par le client dans `setComponentOrder` était concaténé directement dans la requête SQL, sans aucune validation, contrairement aux autres champs contrôlés par is_numeric(). Un admin pouvait injecter du SQL arbitraire (testé : `DROP TABLE viewData` s'exécute réellement). - Whitelist stricte sur le champ `type` (cmd, eqLogic, scenario) - Remplacement de la concaténation SQL multi-statements par un prepared statement exécuté à chaque itération - Tests d'intégration couvrant l'endpoint via un sous-processus PHP authentifié comme admin (payload DROP, UNION, type inconnu, type manquant) --- core/ajax/view.ajax.php | 15 +- .../support/AjaxIntegrationTestCase.php | 180 ++++++++++++++++++ tests/integration/support/ajax_runner.php | 56 ++++++ .../integration/viewAjaxSqlInjectionTest.php | 100 ++++++++++ 4 files changed, 346 insertions(+), 5 deletions(-) create mode 100644 tests/integration/support/AjaxIntegrationTestCase.php create mode 100644 tests/integration/support/ajax_runner.php create mode 100644 tests/integration/viewAjaxSqlInjectionTest.php diff --git a/core/ajax/view.ajax.php b/core/ajax/view.ajax.php index 033976e1de..23eff4530d 100644 --- a/core/ajax/view.ajax.php +++ b/core/ajax/view.ajax.php @@ -164,12 +164,20 @@ } unautorizedInDemo(); $components = json_decode(init('components'), true); - $sql = ''; + $allowedTypes = array('cmd', 'eqLogic', 'scenario'); foreach ($components as $component) { if (!isset($component['viewZone_id']) || !is_numeric($component['viewZone_id']) || !is_numeric($component['id']) || !is_numeric($component['viewOrder']) || (isset($component['object_id']) && !is_numeric($component['object_id']))) { continue; } - $sql .= 'UPDATE viewData SET `order`= ' . $component['viewOrder'] . ' WHERE link_id=' . $component['id'] . ' AND type="' . $component['type'] . '" AND viewZone_id=' . $component['viewZone_id'] . ';'; + if (!isset($component['type']) || !in_array($component['type'], $allowedTypes)) { + continue; + } + DB::Prepare('UPDATE viewData SET `order`= :viewOrder WHERE link_id= :id AND type= :type AND viewZone_id= :viewZone_id', array( + 'viewOrder' => $component['viewOrder'], + 'id' => $component['id'], + 'type' => $component['type'], + 'viewZone_id' => $component['viewZone_id'], + ), DB::FETCH_TYPE_ROW); if ($component['type'] == 'eqLogic') { unset($component['type']); $eqLogic = eqLogic::byId($component['id']); @@ -188,9 +196,6 @@ $scenario->save(true); } } - if ($sql != '') { - DB::Prepare($sql, array(), DB::FETCH_TYPE_ROW); - } ajax::success(); } diff --git a/tests/integration/support/AjaxIntegrationTestCase.php b/tests/integration/support/AjaxIntegrationTestCase.php new file mode 100644 index 0000000000..5e8d75a32e --- /dev/null +++ b/tests/integration/support/AjaxIntegrationTestCase.php @@ -0,0 +1,180 @@ +. +*/ + +use PHPUnit\Framework\TestCase; + +abstract class AjaxIntegrationTestCase extends TestCase { + + /** @var callable[] */ + private $cleanupStack = array(); + + /** @var int|null */ + private $authenticatedUserId = null; + + protected function tearDown(): void { + foreach (array_reverse($this->cleanupStack) as $callback) { + try { + $callback(); + } catch (\Throwable $e) { + fwrite(STDERR, "Cleanup failed: " . $e->getMessage() . "\n"); + } + } + $this->cleanupStack = []; + $this->authenticatedUserId = null; + } + + protected function givenAdminIsLoggedIn(): void { + $admin = new user(); + $admin->setLogin('test_admin_' . bin2hex(random_bytes(4))); + $admin->setPassword(sha512('test_password_' . bin2hex(random_bytes(4)))); + $admin->setProfils('admin'); + $admin->setEnable(1); + $admin->save(); + + $this->registerCleanup(function () use ($admin) { + $admin->remove(); + }); + $this->authenticatedUserId = (int)$admin->getId(); + } + + /** + * Creates a view + zone + viewData ready to be targeted by setComponentOrder. + * The link_id intentionally points to a non-existent id so that the + * eqLogic::byId() branch of the handler is short-circuited: we only test + * the order UPDATE query, not the side-effects. + */ + protected function givenAViewDataWith(string $type, int $initialOrder = 5): object { + $view = new view(); + $view->setName('test_view_' . bin2hex(random_bytes(4))); + $view->save(); + $this->registerCleanup(function () use ($view) { + $view->remove(); + }); + + $viewZone = new viewZone(); + $viewZone->setView_id($view->getId()); + $viewZone->setName('test_zone'); + $viewZone->save(); + + $fakeLinkId = 987654321; + $viewData = new viewData(); + $viewData->setviewZone_id($viewZone->getId()); + $viewData->setType($type); + $viewData->setLink_id($fakeLinkId); + $viewData->setOrder($initialOrder); + $viewData->save(); + + return (object)[ + 'viewId' => (int)$view->getId(), + 'viewZoneId' => (int)$viewZone->getId(), + 'viewDataId' => (int)$viewData->getId(), + 'linkId' => $fakeLinkId, + 'initialOrder' => $initialOrder, + ]; + } + + protected function whenSetComponentOrderIsCalledWith(array $components): array { + return $this->callAjaxEndpoint('view.ajax.php', [ + 'action' => 'setComponentOrder', + 'components' => json_encode($components), + ]); + } + + protected function thenResponseIsJsonSuccess(array $response): void { + $this->assertSame( + 0, + $response['exitCode'], + "Subprocess exited non-zero.\nStdout: {$response['body']}\nStderr: {$response['stderr']}" + ); + $this->assertNotNull( + $response['json'], + "Expected JSON response.\nStdout: {$response['body']}\nStderr: {$response['stderr']}" + ); + $this->assertSame( + 'ok', + $response['json']['state'] ?? null, + "Expected state=ok.\nBody: {$response['body']}" + ); + } + + protected function thenViewDataOrderIs(int $viewDataId, int $expectedOrder): void { + $row = DB::Prepare( + 'SELECT `order` FROM viewData WHERE id = :id', + ['id' => $viewDataId], + DB::FETCH_TYPE_ROW + ); + $this->assertIsArray($row, "viewData id={$viewDataId} not found"); + $this->assertSame( + $expectedOrder, + (int)$row['order'], + "viewData.order was modified unexpectedly" + ); + } + + protected function thenTableStillExists(string $table): void { + $row = DB::Prepare( + 'SHOW TABLES LIKE :table', + ['table' => $table], + DB::FETCH_TYPE_ROW + ); + $this->assertNotEmpty($row, "Table '{$table}' no longer exists"); + } + + private function registerCleanup(callable $callback): void { + $this->cleanupStack[] = $callback; + } + + private function callAjaxEndpoint(string $ajaxFile, array $postData): array { + $this->assertNotNull( + $this->authenticatedUserId, + 'Call givenAdminIsLoggedIn() before calling any when...()' + ); + + $payload = json_encode([ + 'file' => $ajaxFile, + 'post' => $postData, + 'user_id' => $this->authenticatedUserId, + ]); + + $runner = __DIR__ . '/ajax_runner.php'; + $descriptors = [ + 0 => ['pipe', 'r'], + 1 => ['pipe', 'w'], + 2 => ['pipe', 'w'], + ]; + $process = proc_open('php ' . escapeshellarg($runner), $descriptors, $pipes); + if (!is_resource($process)) { + $this->fail('Failed to spawn ajax_runner.php subprocess'); + } + + fwrite($pipes[0], $payload); + fclose($pipes[0]); + $stdout = stream_get_contents($pipes[1]); + $stderr = stream_get_contents($pipes[2]); + fclose($pipes[1]); + fclose($pipes[2]); + $exitCode = proc_close($process); + + return [ + 'body' => $stdout, + 'stderr' => $stderr, + 'exitCode' => $exitCode, + 'json' => json_decode($stdout, true), + ]; + } +} diff --git a/tests/integration/support/ajax_runner.php b/tests/integration/support/ajax_runner.php new file mode 100644 index 0000000000..bfd3286565 --- /dev/null +++ b/tests/integration/support/ajax_runner.php @@ -0,0 +1,56 @@ +. +*/ + +$payload = json_decode(file_get_contents('php://stdin'), true); +if (!is_array($payload)) { + fwrite(STDERR, "Invalid JSON payload on stdin\n"); + exit(1); +} + +require_once __DIR__ . '/../../../core/php/core.inc.php'; + +$_SERVER['REQUEST_METHOD'] = 'POST'; +$_SERVER['HTTP_HOST'] = 'localhost'; +$_SERVER['SERVER_NAME'] = 'localhost'; +$_SERVER['REMOTE_ADDR'] = '127.0.0.1'; +$_POST = $payload['post'] ?? []; +$_GET = $payload['get'] ?? []; +$_REQUEST = array_merge($_GET, $_POST); + +if (isset($payload['user_id'])) { + $user = user::byId($payload['user_id']); + if (!is_object($user)) { + fwrite(STDERR, "User {$payload['user_id']} not found\n"); + exit(1); + } + $sessionId = bin2hex(random_bytes(32)); + session_id($sessionId); + @session_start(); + $_SESSION['user'] = $user; + $_SESSION['ip'] = '127.0.0.1'; + @session_write_close(); + $_COOKIE[session_name()] = $sessionId; +} + +$ajaxFile = __DIR__ . '/../../../core/ajax/' . basename($payload['file']); +if (!is_file($ajaxFile)) { + fwrite(STDERR, "Ajax file not found: {$ajaxFile}\n"); + exit(1); +} + +include $ajaxFile; diff --git a/tests/integration/viewAjaxSqlInjectionTest.php b/tests/integration/viewAjaxSqlInjectionTest.php new file mode 100644 index 0000000000..a740e68ee3 --- /dev/null +++ b/tests/integration/viewAjaxSqlInjectionTest.php @@ -0,0 +1,100 @@ +. +*/ + +require_once __DIR__ . '/support/AjaxIntegrationTestCase.php'; + +class viewAjaxSqlInjectionTest extends AjaxIntegrationTestCase { + + public function testValidComponentTypeUpdatesTheOrder(): void { + $this->givenAdminIsLoggedIn(); + $component = $this->givenAViewDataWith('eqLogic', 5); + + $response = $this->whenSetComponentOrderIsCalledWith([[ + 'viewZone_id' => $component->viewZoneId, + 'id' => $component->linkId, + 'viewOrder' => 42, + 'type' => 'eqLogic', + ]]); + + $this->thenResponseIsJsonSuccess($response); + $this->thenViewDataOrderIs($component->viewDataId, 42); + } + + public function testSqlInjectionPayloadInTypeFieldIsRejected(): void { + $this->givenAdminIsLoggedIn(); + $component = $this->givenAViewDataWith('eqLogic', 5); + + $response = $this->whenSetComponentOrderIsCalledWith([[ + 'viewZone_id' => $component->viewZoneId, + 'id' => $component->linkId, + 'viewOrder' => 999, + 'type' => 'eqLogic"; DROP TABLE viewData; --', + ]]); + + $this->thenResponseIsJsonSuccess($response); + $this->thenTableStillExists('viewData'); + $this->thenViewDataOrderIs($component->viewDataId, $component->initialOrder); + } + + public function testUnionBasedSqlInjectionIsRejected(): void { + $this->givenAdminIsLoggedIn(); + $component = $this->givenAViewDataWith('eqLogic', 5); + + $response = $this->whenSetComponentOrderIsCalledWith([[ + 'viewZone_id' => $component->viewZoneId, + 'id' => $component->linkId, + 'viewOrder' => 999, + 'type' => 'eqLogic" UNION SELECT password FROM user --', + ]]); + + $this->thenResponseIsJsonSuccess($response); + $this->thenViewDataOrderIs($component->viewDataId, $component->initialOrder); + } + + public function testUnknownComponentTypeIsRejected(): void { + $this->givenAdminIsLoggedIn(); + // Fixture type matches the injected type so the vulnerable WHERE would hit this row. + $component = $this->givenAViewDataWith('arbitrary_string', 5); + + $response = $this->whenSetComponentOrderIsCalledWith([[ + 'viewZone_id' => $component->viewZoneId, + 'id' => $component->linkId, + 'viewOrder' => 999, + 'type' => 'arbitrary_string', + ]]); + + $this->thenResponseIsJsonSuccess($response); + $this->thenViewDataOrderIs($component->viewDataId, $component->initialOrder); + } + + public function testMissingComponentTypeIsRejected(): void { + $this->givenAdminIsLoggedIn(); + // Fixture type is empty so the vulnerable code — which concatenates a + // null into type="" — would match this row and bump its order. + $component = $this->givenAViewDataWith('', 5); + + $response = $this->whenSetComponentOrderIsCalledWith([[ + 'viewZone_id' => $component->viewZoneId, + 'id' => $component->linkId, + 'viewOrder' => 999, + ]]); + + $this->thenResponseIsJsonSuccess($response); + $this->thenViewDataOrderIs($component->viewDataId, $component->initialOrder); + } +} From 92881e749b6a62dfbd99756ef0f33e5a3d0a6fde Mon Sep 17 00:00:00 2001 From: TonioBDS <49921859+TonioBDS@users.noreply.github.com> Date: Mon, 15 Dec 2025 10:39:34 +0100 Subject: [PATCH 125/128] Update #trigger_name# tag explanation in scenario.md Clarified the usage of the #trigger_name# tag and its implications for object, equipment, or command name changes. --- docs/fr_FR/scenario.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/fr_FR/scenario.md b/docs/fr_FR/scenario.md index 779d802487..256d54852b 100644 --- a/docs/fr_FR/scenario.md +++ b/docs/fr_FR/scenario.md @@ -297,7 +297,7 @@ Un tag est remplacé lors de l’exécution du scénario par sa valeur. Vous pou - ``user`` s'il a été lancé manuellement, - ``start`` pour un lancement au démarrage de Jeedom. - ``#trigger_id#`` : Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur de l'id de la commande qui l'a déclenché. Exemple : ``#trigger_id# == 19`` -- ``#trigger_name#`` : Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur du nom de la commande (sous forme [objet][equipement][commande]). Exemple : ``#trigger_name# == '[cuisine][lumiere][etat]'`` +- ``#trigger_name#`` : Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur du nom de la commande (sous forme [objet][equipement][commande]). Exemple : ``#trigger_name# == '[cuisine][lumiere][etat]'``. Notez qu'en utilisant la syntaxe : ``#trigger_name# == '[cuisine][lumiere][etat]'``, en cas de modification de nom de votre objet, équipement ou commande, ce ne sera pas mis à jour automatiquement dans votre code. - ``#trigger_value#`` : Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur de la commande ayant déclenché le scénario. Astuce si vous voulez la valeur courante de la commande qui a déclencher le scénario (et non sa valeur au déclenchement) vous pouvez utiliser : ``##trigger_id##`` (double #) - ``#latitude#`` : Permet de récuperer l'information de latitude mise dans la configuration de jeedom - ``#longitude#`` : Permet de récuperer l'information de longitude mise dans la configuration de jeedom From e80046bd3a1bf2a5071bbb3e6c4211d1dabf1415 Mon Sep 17 00:00:00 2001 From: Jenkins Date: Wed, 17 Dec 2025 09:43:28 +0000 Subject: [PATCH 126/128] [Jenkins] Updated translation --- docs/de_DE/scenario.md | 2 +- docs/en_US/scenario.md | 2 +- docs/es_ES/scenario.md | 2 +- docs/i18n/de_DE.json | 2 ++ docs/i18n/en_US.json | 2 ++ docs/i18n/es_ES.json | 2 ++ docs/i18n/fr_FR.json | 2 ++ docs/i18n/pt_PT.json | 2 ++ docs/pt_PT/scenario.md | 2 +- 9 files changed, 14 insertions(+), 4 deletions(-) diff --git a/docs/de_DE/scenario.md b/docs/de_DE/scenario.md index 4885475e70..ed1105b668 100644 --- a/docs/de_DE/scenario.md +++ b/docs/de_DE/scenario.md @@ -297,7 +297,7 @@ Ein Tag wird während der Ausführung des Szenarios durch seinen Wert ersetzt. S - ``user`` wenn es manuell gestartet wurde, - ``start`` für einen Start beim Start von Jeedom. - ``#trigger_id#`` : Wenn es sich um einen Befehl handelt, der das Szenario ausgelöst hat, hat dieses Tag den Wert der ID des Befehls, der es ausgelöst hat. Beispiel : ``#trigger_id# == 19`` -- ``#trigger_name#`` : Wenn es sich um einen Befehl handelt, der das Szenario ausgelöst hat, hat dieses Tag den Wert des Namens des Befehls (in der Form [Objekt][Ausrüstung][Befehl]). Beispiel : ``#trigger_name# == '[cuisine][lumiere][etat]'`` +- ``#trigger_name#`` : Wenn es sich um einen Befehl handelt, der das Szenario ausgelöst hat, hat dieses Tag den Wert des Namens des Befehls (in der Form [Objekt][Ausrüstung][Befehl]). Beispiel : ``#trigger_name# == '[cuisine][lumiere][etat]'``. : ``#trigger_name# == '[cuisine][lumiere][etat]'``, . - ``#trigger_value#`` : Wenn es sich um einen Befehl handelt, der das Szenario ausgelöst hat, hat dieses Tag den Wert des Befehls, der das Szenario ausgelöst hat. Tipp: Wenn Sie den aktuellen Wert des Befehls möchten, der das Szenario ausgelöst hat (und nicht seinen Wert zum Zeitpunkt der Auslösung), können Sie diesen verwenden : ``##trigger_id##`` (doppelt #) - ``#latitude#`` : Ermöglicht Ihnen, die in der Jeedom-Konfiguration eingegebenen Breitengradinformationen abzurufen - ``#longitude#`` : Ermöglicht Ihnen, die in der Jeedom-Konfiguration eingegebenen Längengradinformationen abzurufen diff --git a/docs/en_US/scenario.md b/docs/en_US/scenario.md index c2aa5556a5..45a83cb9e8 100644 --- a/docs/en_US/scenario.md +++ b/docs/en_US/scenario.md @@ -297,7 +297,7 @@ A tag is replaced during the execution of the scenario by its value. You can use - ``user`` if it was started manually, - ``start`` for a launch at startup of Jeedom. - ``#trigger_id#`` : If it is a command which triggered the scenario then this tag has the value of the id of the command which triggered it. Example : ``#trigger_id# == 19`` -- ``#trigger_name#`` : If it is a command which triggered the scenario then this tag has the value of the name of the command (in the form [object][equipment][command]). Example : ``#trigger_name# == '[cuisine][lumiere][etat]'`` +- ``#trigger_name#`` : If it is a command which triggered the scenario then this tag has the value of the name of the command (in the form [object][equipment][command]). Example : ``#trigger_name# == '[cuisine][lumiere][etat]'``. : ``#trigger_name# == '[cuisine][lumiere][etat]'``, . - ``#trigger_value#`` : If it is a command which triggered the scenario then this tag has the value of the command which triggered the scenario. Tip if you want the current value of the command which triggered the scenario (and not its value at triggering) you can use : ``##trigger_id##`` (double #) - ``#latitude#`` : Allows you to retrieve the latitude information put in the jeedom configuration - ``#longitude#`` : Allows you to retrieve the longitude information put in the jeedom configuration diff --git a/docs/es_ES/scenario.md b/docs/es_ES/scenario.md index c86c36f31b..2cfc582e9b 100644 --- a/docs/es_ES/scenario.md +++ b/docs/es_ES/scenario.md @@ -297,7 +297,7 @@ Una etiqueta se reemplaza durante la ejecución del escenario por su valor. Pued - ``user`` si se inició manualmente, - ``start`` para un lanzamiento al inicio de Jeedom. - ``#trigger_id#`` : Si es un comando el que desencadenó el escenario, entonces esta etiqueta tiene el valor de la identificación del comando que lo desencadenó. Ejemplo : ``#trigger_id# == 19`` -- ``#trigger_name#`` : Si es un comando el que desencadenó el escenario, entonces esta etiqueta tiene el valor del nombre del comando (en el formato [objeto][equipo][comando]). Ejemplo : ``#trigger_name# == '[cuisine][lumiere][etat]'`` +- ``#trigger_name#`` : Si es un comando el que desencadenó el escenario, entonces esta etiqueta tiene el valor del nombre del comando (en el formato [objeto][equipo][comando]). Ejemplo : ``#trigger_name# == '[cuisine][lumiere][etat]'``. : ``#trigger_name# == '[cuisine][lumiere][etat]'``, . - ``#trigger_value#`` : Si es un comando que desencadenó el escenario, entonces esta etiqueta tiene el valor del comando que desencadenó el escenario. Consejo: si desea conocer el valor actual del comando que desencadenó el escenario (y no su valor en el momento de la activación), puede utilizar : ``##trigger_id##`` (doble #) - ``#latitude#`` : Le permite recuperar la información de latitud ingresada en la configuración de jeedom - ``#longitude#`` : Le permite recuperar la información de longitud ingresada en la configuración de jeedom diff --git a/docs/i18n/de_DE.json b/docs/i18n/de_DE.json index 798e06ef2a..5b36d17e2b 100644 --- a/docs/i18n/de_DE.json +++ b/docs/i18n/de_DE.json @@ -6884,6 +6884,8 @@ "pour un lancement au démarrage de Jeedom": "für einen Start beim Start von Jeedom", "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur de l'id de la commande qui l'a déclenché": "Wenn es sich um einen Befehl handelt, der das Szenario ausgelöst hat, hat dieses Tag den Wert der ID des Befehls, der es ausgelöst hat", "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur du nom de la commande (sous forme [objet][equipement][commande": "Wenn es sich um einen Befehl handelt, der das Szenario ausgelöst hat, hat dieses Tag den Wert des Namens des Befehls (in der Form [Objekt][Ausrüstung][Befehl", + "Notez qu'en utilisant la syntaxe": "", + "en cas de modification de nom de votre objet, équipement ou commande, ce ne sera pas mis à jour automatiquement dans votre code": "", "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur de la commande ayant déclenché le scénario": "Wenn es sich um einen Befehl handelt, der das Szenario ausgelöst hat, hat dieses Tag den Wert des Befehls, der das Szenario ausgelöst hat", "Astuce si vous voulez la valeur courante de la commande qui a déclencher le scénario (et non sa valeur au déclenchement) vous pouvez utiliser": "Tipp: Wenn Sie den aktuellen Wert des Befehls möchten, der das Szenario ausgelöst hat (und nicht seinen Wert zum Zeitpunkt der Auslösung), können Sie diesen verwenden", "Permet de récuperer l'information de latitude mise dans la configuration de jeedom": "Ermöglicht Ihnen, die in der Jeedom-Konfiguration eingegebenen Breitengradinformationen abzurufen", diff --git a/docs/i18n/en_US.json b/docs/i18n/en_US.json index f94a0eb74c..4cdf40a5c2 100644 --- a/docs/i18n/en_US.json +++ b/docs/i18n/en_US.json @@ -6884,6 +6884,8 @@ "pour un lancement au démarrage de Jeedom": "for a launch at startup of Jeedom", "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur de l'id de la commande qui l'a déclenché": "If it is a command which triggered the scenario then this tag has the value of the id of the command which triggered it", "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur du nom de la commande (sous forme [objet][equipement][commande": "If it is a command which triggered the scenario then this tag has the value of the name of the command (in the form [object][equipment][command", + "Notez qu'en utilisant la syntaxe": "", + "en cas de modification de nom de votre objet, équipement ou commande, ce ne sera pas mis à jour automatiquement dans votre code": "", "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur de la commande ayant déclenché le scénario": "If it is a command which triggered the scenario then this tag has the value of the command which triggered the scenario", "Astuce si vous voulez la valeur courante de la commande qui a déclencher le scénario (et non sa valeur au déclenchement) vous pouvez utiliser": "Tip if you want the current value of the command which triggered the scenario (and not its value at triggering) you can use", "Permet de récuperer l'information de latitude mise dans la configuration de jeedom": "Allows you to retrieve the latitude information put in the jeedom configuration", diff --git a/docs/i18n/es_ES.json b/docs/i18n/es_ES.json index 34245f33b3..ceb1f77a95 100644 --- a/docs/i18n/es_ES.json +++ b/docs/i18n/es_ES.json @@ -6884,6 +6884,8 @@ "pour un lancement au démarrage de Jeedom": "para un lanzamiento al inicio de Jeedom", "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur de l'id de la commande qui l'a déclenché": "Si es un comando el que desencadenó el escenario, entonces esta etiqueta tiene el valor de la identificación del comando que lo desencadenó", "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur du nom de la commande (sous forme [objet][equipement][commande": "Si es un comando el que desencadenó el escenario, entonces esta etiqueta tiene el valor del nombre del comando (en el formato [objeto][equipo][comando", + "Notez qu'en utilisant la syntaxe": "", + "en cas de modification de nom de votre objet, équipement ou commande, ce ne sera pas mis à jour automatiquement dans votre code": "", "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur de la commande ayant déclenché le scénario": "Si es un comando que desencadenó el escenario, entonces esta etiqueta tiene el valor del comando que desencadenó el escenario", "Astuce si vous voulez la valeur courante de la commande qui a déclencher le scénario (et non sa valeur au déclenchement) vous pouvez utiliser": "Consejo: si desea conocer el valor actual del comando que desencadenó el escenario (y no su valor en el momento de la activación), puede utilizar", "Permet de récuperer l'information de latitude mise dans la configuration de jeedom": "Le permite recuperar la información de latitud ingresada en la configuración de jeedom", diff --git a/docs/i18n/fr_FR.json b/docs/i18n/fr_FR.json index 5ebef45b8d..93c4972a5a 100644 --- a/docs/i18n/fr_FR.json +++ b/docs/i18n/fr_FR.json @@ -3845,6 +3845,8 @@ "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur de l'id de la commande qui l'a déclenché": "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur de l'id de la commande qui l'a déclenché", "Exemple": "Exemple", "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur du nom de la commande (sous forme [objet][equipement][commande": "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur du nom de la commande (sous forme [objet][equipement][commande", + "Notez qu'en utilisant la syntaxe": "Notez qu'en utilisant la syntaxe", + "en cas de modification de nom de votre objet, équipement ou commande, ce ne sera pas mis à jour automatiquement dans votre code": "en cas de modification de nom de votre objet, équipement ou commande, ce ne sera pas mis à jour automatiquement dans votre code", "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur de la commande ayant déclenché le scénario": "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur de la commande ayant déclenché le scénario", "Astuce si vous voulez la valeur courante de la commande qui a déclencher le scénario (et non sa valeur au déclenchement) vous pouvez utiliser": "Astuce si vous voulez la valeur courante de la commande qui a déclencher le scénario (et non sa valeur au déclenchement) vous pouvez utiliser", "double": "double", diff --git a/docs/i18n/pt_PT.json b/docs/i18n/pt_PT.json index 77445125fd..9f28170049 100644 --- a/docs/i18n/pt_PT.json +++ b/docs/i18n/pt_PT.json @@ -6884,6 +6884,8 @@ "pour un lancement au démarrage de Jeedom": "para um lançamento na inicialização do Jeedom", "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur de l'id de la commande qui l'a déclenché": "Se for um comando que desencadeou o cenário então esta tag tem o valor do id do comando que o desencadeou", "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur du nom de la commande (sous forme [objet][equipement][commande": "Se for um comando que disparou o cenário então esta tag terá o valor do nome do comando (na forma [objeto][equipamento][comando", + "Notez qu'en utilisant la syntaxe": "", + "en cas de modification de nom de votre objet, équipement ou commande, ce ne sera pas mis à jour automatiquement dans votre code": "", "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur de la commande ayant déclenché le scénario": "Se for um comando que acionou o cenário então esta tag terá o valor do comando que acionou o cenário", "Astuce si vous voulez la valeur courante de la commande qui a déclencher le scénario (et non sa valeur au déclenchement) vous pouvez utiliser": "Dica se você quiser o valor atual do comando que disparou o cenário (e não seu valor no disparo), você pode usar", "Permet de récuperer l'information de latitude mise dans la configuration de jeedom": "Permite recuperar as informações de latitude colocadas na configuração do jeedom", diff --git a/docs/pt_PT/scenario.md b/docs/pt_PT/scenario.md index 4482cad03c..4dd16d09db 100644 --- a/docs/pt_PT/scenario.md +++ b/docs/pt_PT/scenario.md @@ -297,7 +297,7 @@ Uma tag é substituída durante a execução do cenário por seu valor. Você po - ``user`` se foi iniciado manualmente, - ``start`` para um lançamento na inicialização do Jeedom. - ``#trigger_id#`` : Se for um comando que desencadeou o cenário então esta tag tem o valor do id do comando que o desencadeou. Exemplo : ``#trigger_id# == 19`` -- ``#trigger_name#`` : Se for um comando que disparou o cenário então esta tag terá o valor do nome do comando (na forma [objeto][equipamento][comando]). Exemplo : ``#trigger_name# == '[cuisine][lumiere][etat]'`` +- ``#trigger_name#`` : Se for um comando que disparou o cenário então esta tag terá o valor do nome do comando (na forma [objeto][equipamento][comando]). Exemplo : ``#trigger_name# == '[cuisine][lumiere][etat]'``. : ``#trigger_name# == '[cuisine][lumiere][etat]'``, . - ``#trigger_value#`` : Se for um comando que acionou o cenário então esta tag terá o valor do comando que acionou o cenário. Dica se você quiser o valor atual do comando que disparou o cenário (e não seu valor no disparo), você pode usar : ``##trigger_id##`` (dobro #) - ``#latitude#`` : Permite recuperar as informações de latitude colocadas na configuração do jeedom - ``#longitude#`` : Permite recuperar as informações de longitude colocadas na configuração do jeedom From 851e23a1e750b2c5a2fcbda7f3f29a20f1e222ae Mon Sep 17 00:00:00 2001 From: Jenkins Date: Wed, 17 Dec 2025 11:42:08 +0000 Subject: [PATCH 127/128] [Jenkins] Updated translation --- docs/de_DE/scenario.md | 2 +- docs/en_US/scenario.md | 2 +- docs/es_ES/scenario.md | 2 +- docs/i18n/de_DE.json | 4 ++-- docs/i18n/en_US.json | 4 ++-- docs/i18n/es_ES.json | 4 ++-- docs/i18n/pt_PT.json | 4 ++-- docs/pt_PT/scenario.md | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/de_DE/scenario.md b/docs/de_DE/scenario.md index ed1105b668..a8e59feadf 100644 --- a/docs/de_DE/scenario.md +++ b/docs/de_DE/scenario.md @@ -297,7 +297,7 @@ Ein Tag wird während der Ausführung des Szenarios durch seinen Wert ersetzt. S - ``user`` wenn es manuell gestartet wurde, - ``start`` für einen Start beim Start von Jeedom. - ``#trigger_id#`` : Wenn es sich um einen Befehl handelt, der das Szenario ausgelöst hat, hat dieses Tag den Wert der ID des Befehls, der es ausgelöst hat. Beispiel : ``#trigger_id# == 19`` -- ``#trigger_name#`` : Wenn es sich um einen Befehl handelt, der das Szenario ausgelöst hat, hat dieses Tag den Wert des Namens des Befehls (in der Form [Objekt][Ausrüstung][Befehl]). Beispiel : ``#trigger_name# == '[cuisine][lumiere][etat]'``. : ``#trigger_name# == '[cuisine][lumiere][etat]'``, . +- ``#trigger_name#`` : Wenn es sich um einen Befehl handelt, der das Szenario ausgelöst hat, hat dieses Tag den Wert des Namens des Befehls (in der Form [Objekt][Ausrüstung][Befehl]). Beispiel : ``#trigger_name# == '[cuisine][lumiere][etat]'``. Notez qu'en utilisant la syntaxe : ``#trigger_name# == '[cuisine][lumiere][etat]'``, en cas de modification de nom de votre objet, équipement ou commande, ce ne sera pas mis à jour automatiquement dans votre code. - ``#trigger_value#`` : Wenn es sich um einen Befehl handelt, der das Szenario ausgelöst hat, hat dieses Tag den Wert des Befehls, der das Szenario ausgelöst hat. Tipp: Wenn Sie den aktuellen Wert des Befehls möchten, der das Szenario ausgelöst hat (und nicht seinen Wert zum Zeitpunkt der Auslösung), können Sie diesen verwenden : ``##trigger_id##`` (doppelt #) - ``#latitude#`` : Ermöglicht Ihnen, die in der Jeedom-Konfiguration eingegebenen Breitengradinformationen abzurufen - ``#longitude#`` : Ermöglicht Ihnen, die in der Jeedom-Konfiguration eingegebenen Längengradinformationen abzurufen diff --git a/docs/en_US/scenario.md b/docs/en_US/scenario.md index 45a83cb9e8..11f6d787cc 100644 --- a/docs/en_US/scenario.md +++ b/docs/en_US/scenario.md @@ -297,7 +297,7 @@ A tag is replaced during the execution of the scenario by its value. You can use - ``user`` if it was started manually, - ``start`` for a launch at startup of Jeedom. - ``#trigger_id#`` : If it is a command which triggered the scenario then this tag has the value of the id of the command which triggered it. Example : ``#trigger_id# == 19`` -- ``#trigger_name#`` : If it is a command which triggered the scenario then this tag has the value of the name of the command (in the form [object][equipment][command]). Example : ``#trigger_name# == '[cuisine][lumiere][etat]'``. : ``#trigger_name# == '[cuisine][lumiere][etat]'``, . +- ``#trigger_name#`` : If it is a command which triggered the scenario then this tag has the value of the name of the command (in the form [object][equipment][command]). Example : ``#trigger_name# == '[cuisine][lumiere][etat]'``. Notez qu'en utilisant la syntaxe : ``#trigger_name# == '[cuisine][lumiere][etat]'``, en cas de modification de nom de votre objet, équipement ou commande, ce ne sera pas mis à jour automatiquement dans votre code. - ``#trigger_value#`` : If it is a command which triggered the scenario then this tag has the value of the command which triggered the scenario. Tip if you want the current value of the command which triggered the scenario (and not its value at triggering) you can use : ``##trigger_id##`` (double #) - ``#latitude#`` : Allows you to retrieve the latitude information put in the jeedom configuration - ``#longitude#`` : Allows you to retrieve the longitude information put in the jeedom configuration diff --git a/docs/es_ES/scenario.md b/docs/es_ES/scenario.md index 2cfc582e9b..da7fefb251 100644 --- a/docs/es_ES/scenario.md +++ b/docs/es_ES/scenario.md @@ -297,7 +297,7 @@ Una etiqueta se reemplaza durante la ejecución del escenario por su valor. Pued - ``user`` si se inició manualmente, - ``start`` para un lanzamiento al inicio de Jeedom. - ``#trigger_id#`` : Si es un comando el que desencadenó el escenario, entonces esta etiqueta tiene el valor de la identificación del comando que lo desencadenó. Ejemplo : ``#trigger_id# == 19`` -- ``#trigger_name#`` : Si es un comando el que desencadenó el escenario, entonces esta etiqueta tiene el valor del nombre del comando (en el formato [objeto][equipo][comando]). Ejemplo : ``#trigger_name# == '[cuisine][lumiere][etat]'``. : ``#trigger_name# == '[cuisine][lumiere][etat]'``, . +- ``#trigger_name#`` : Si es un comando el que desencadenó el escenario, entonces esta etiqueta tiene el valor del nombre del comando (en el formato [objeto][equipo][comando]). Ejemplo : ``#trigger_name# == '[cuisine][lumiere][etat]'``. Notez qu'en utilisant la syntaxe : ``#trigger_name# == '[cuisine][lumiere][etat]'``, en cas de modification de nom de votre objet, équipement ou commande, ce ne sera pas mis à jour automatiquement dans votre code. - ``#trigger_value#`` : Si es un comando que desencadenó el escenario, entonces esta etiqueta tiene el valor del comando que desencadenó el escenario. Consejo: si desea conocer el valor actual del comando que desencadenó el escenario (y no su valor en el momento de la activación), puede utilizar : ``##trigger_id##`` (doble #) - ``#latitude#`` : Le permite recuperar la información de latitud ingresada en la configuración de jeedom - ``#longitude#`` : Le permite recuperar la información de longitud ingresada en la configuración de jeedom diff --git a/docs/i18n/de_DE.json b/docs/i18n/de_DE.json index 5b36d17e2b..dd56ced7de 100644 --- a/docs/i18n/de_DE.json +++ b/docs/i18n/de_DE.json @@ -6884,8 +6884,8 @@ "pour un lancement au démarrage de Jeedom": "für einen Start beim Start von Jeedom", "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur de l'id de la commande qui l'a déclenché": "Wenn es sich um einen Befehl handelt, der das Szenario ausgelöst hat, hat dieses Tag den Wert der ID des Befehls, der es ausgelöst hat", "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur du nom de la commande (sous forme [objet][equipement][commande": "Wenn es sich um einen Befehl handelt, der das Szenario ausgelöst hat, hat dieses Tag den Wert des Namens des Befehls (in der Form [Objekt][Ausrüstung][Befehl", - "Notez qu'en utilisant la syntaxe": "", - "en cas de modification de nom de votre objet, équipement ou commande, ce ne sera pas mis à jour automatiquement dans votre code": "", + "Notez qu'en utilisant la syntaxe": "Notez qu'en utilisant la syntaxe", + "en cas de modification de nom de votre objet, équipement ou commande, ce ne sera pas mis à jour automatiquement dans votre code": "en cas de modification de nom de votre objet, équipement ou commande, ce ne sera pas mis à jour automatiquement dans votre code", "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur de la commande ayant déclenché le scénario": "Wenn es sich um einen Befehl handelt, der das Szenario ausgelöst hat, hat dieses Tag den Wert des Befehls, der das Szenario ausgelöst hat", "Astuce si vous voulez la valeur courante de la commande qui a déclencher le scénario (et non sa valeur au déclenchement) vous pouvez utiliser": "Tipp: Wenn Sie den aktuellen Wert des Befehls möchten, der das Szenario ausgelöst hat (und nicht seinen Wert zum Zeitpunkt der Auslösung), können Sie diesen verwenden", "Permet de récuperer l'information de latitude mise dans la configuration de jeedom": "Ermöglicht Ihnen, die in der Jeedom-Konfiguration eingegebenen Breitengradinformationen abzurufen", diff --git a/docs/i18n/en_US.json b/docs/i18n/en_US.json index 4cdf40a5c2..fcac739e4f 100644 --- a/docs/i18n/en_US.json +++ b/docs/i18n/en_US.json @@ -6884,8 +6884,8 @@ "pour un lancement au démarrage de Jeedom": "for a launch at startup of Jeedom", "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur de l'id de la commande qui l'a déclenché": "If it is a command which triggered the scenario then this tag has the value of the id of the command which triggered it", "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur du nom de la commande (sous forme [objet][equipement][commande": "If it is a command which triggered the scenario then this tag has the value of the name of the command (in the form [object][equipment][command", - "Notez qu'en utilisant la syntaxe": "", - "en cas de modification de nom de votre objet, équipement ou commande, ce ne sera pas mis à jour automatiquement dans votre code": "", + "Notez qu'en utilisant la syntaxe": "Notez qu'en utilisant la syntaxe", + "en cas de modification de nom de votre objet, équipement ou commande, ce ne sera pas mis à jour automatiquement dans votre code": "en cas de modification de nom de votre objet, équipement ou commande, ce ne sera pas mis à jour automatiquement dans votre code", "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur de la commande ayant déclenché le scénario": "If it is a command which triggered the scenario then this tag has the value of the command which triggered the scenario", "Astuce si vous voulez la valeur courante de la commande qui a déclencher le scénario (et non sa valeur au déclenchement) vous pouvez utiliser": "Tip if you want the current value of the command which triggered the scenario (and not its value at triggering) you can use", "Permet de récuperer l'information de latitude mise dans la configuration de jeedom": "Allows you to retrieve the latitude information put in the jeedom configuration", diff --git a/docs/i18n/es_ES.json b/docs/i18n/es_ES.json index ceb1f77a95..92094c55a1 100644 --- a/docs/i18n/es_ES.json +++ b/docs/i18n/es_ES.json @@ -6884,8 +6884,8 @@ "pour un lancement au démarrage de Jeedom": "para un lanzamiento al inicio de Jeedom", "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur de l'id de la commande qui l'a déclenché": "Si es un comando el que desencadenó el escenario, entonces esta etiqueta tiene el valor de la identificación del comando que lo desencadenó", "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur du nom de la commande (sous forme [objet][equipement][commande": "Si es un comando el que desencadenó el escenario, entonces esta etiqueta tiene el valor del nombre del comando (en el formato [objeto][equipo][comando", - "Notez qu'en utilisant la syntaxe": "", - "en cas de modification de nom de votre objet, équipement ou commande, ce ne sera pas mis à jour automatiquement dans votre code": "", + "Notez qu'en utilisant la syntaxe": "Notez qu'en utilisant la syntaxe", + "en cas de modification de nom de votre objet, équipement ou commande, ce ne sera pas mis à jour automatiquement dans votre code": "en cas de modification de nom de votre objet, équipement ou commande, ce ne sera pas mis à jour automatiquement dans votre code", "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur de la commande ayant déclenché le scénario": "Si es un comando que desencadenó el escenario, entonces esta etiqueta tiene el valor del comando que desencadenó el escenario", "Astuce si vous voulez la valeur courante de la commande qui a déclencher le scénario (et non sa valeur au déclenchement) vous pouvez utiliser": "Consejo: si desea conocer el valor actual del comando que desencadenó el escenario (y no su valor en el momento de la activación), puede utilizar", "Permet de récuperer l'information de latitude mise dans la configuration de jeedom": "Le permite recuperar la información de latitud ingresada en la configuración de jeedom", diff --git a/docs/i18n/pt_PT.json b/docs/i18n/pt_PT.json index 9f28170049..75dffc74d3 100644 --- a/docs/i18n/pt_PT.json +++ b/docs/i18n/pt_PT.json @@ -6884,8 +6884,8 @@ "pour un lancement au démarrage de Jeedom": "para um lançamento na inicialização do Jeedom", "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur de l'id de la commande qui l'a déclenché": "Se for um comando que desencadeou o cenário então esta tag tem o valor do id do comando que o desencadeou", "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur du nom de la commande (sous forme [objet][equipement][commande": "Se for um comando que disparou o cenário então esta tag terá o valor do nome do comando (na forma [objeto][equipamento][comando", - "Notez qu'en utilisant la syntaxe": "", - "en cas de modification de nom de votre objet, équipement ou commande, ce ne sera pas mis à jour automatiquement dans votre code": "", + "Notez qu'en utilisant la syntaxe": "Notez qu'en utilisant la syntaxe", + "en cas de modification de nom de votre objet, équipement ou commande, ce ne sera pas mis à jour automatiquement dans votre code": "en cas de modification de nom de votre objet, équipement ou commande, ce ne sera pas mis à jour automatiquement dans votre code", "Si c'est une commande qui a déclenché le scénario alors ce tag à la valeur de la commande ayant déclenché le scénario": "Se for um comando que acionou o cenário então esta tag terá o valor do comando que acionou o cenário", "Astuce si vous voulez la valeur courante de la commande qui a déclencher le scénario (et non sa valeur au déclenchement) vous pouvez utiliser": "Dica se você quiser o valor atual do comando que disparou o cenário (e não seu valor no disparo), você pode usar", "Permet de récuperer l'information de latitude mise dans la configuration de jeedom": "Permite recuperar as informações de latitude colocadas na configuração do jeedom", diff --git a/docs/pt_PT/scenario.md b/docs/pt_PT/scenario.md index 4dd16d09db..c3583ad1be 100644 --- a/docs/pt_PT/scenario.md +++ b/docs/pt_PT/scenario.md @@ -297,7 +297,7 @@ Uma tag é substituída durante a execução do cenário por seu valor. Você po - ``user`` se foi iniciado manualmente, - ``start`` para um lançamento na inicialização do Jeedom. - ``#trigger_id#`` : Se for um comando que desencadeou o cenário então esta tag tem o valor do id do comando que o desencadeou. Exemplo : ``#trigger_id# == 19`` -- ``#trigger_name#`` : Se for um comando que disparou o cenário então esta tag terá o valor do nome do comando (na forma [objeto][equipamento][comando]). Exemplo : ``#trigger_name# == '[cuisine][lumiere][etat]'``. : ``#trigger_name# == '[cuisine][lumiere][etat]'``, . +- ``#trigger_name#`` : Se for um comando que disparou o cenário então esta tag terá o valor do nome do comando (na forma [objeto][equipamento][comando]). Exemplo : ``#trigger_name# == '[cuisine][lumiere][etat]'``. Notez qu'en utilisant la syntaxe : ``#trigger_name# == '[cuisine][lumiere][etat]'``, en cas de modification de nom de votre objet, équipement ou commande, ce ne sera pas mis à jour automatiquement dans votre code. - ``#trigger_value#`` : Se for um comando que acionou o cenário então esta tag terá o valor do comando que acionou o cenário. Dica se você quiser o valor atual do comando que disparou o cenário (e não seu valor no disparo), você pode usar : ``##trigger_id##`` (dobro #) - ``#latitude#`` : Permite recuperar as informações de latitude colocadas na configuração do jeedom - ``#longitude#`` : Permite recuperar as informações de longitude colocadas na configuração do jeedom From eab64465a49d1ed242294172422b58f34a2d1fe4 Mon Sep 17 00:00:00 2001 From: pifou25 Date: Tue, 17 Dec 2024 22:39:19 +0100 Subject: [PATCH 128/128] improve Dockerfile build sequence --- .dockerignore | 3 +++ Dockerfile | 22 +++++++++++----------- install/OS_specific/Docker/init.sh | 13 +++++++++---- install/install.sh | 6 +++--- 4 files changed, 26 insertions(+), 18 deletions(-) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..f4cab784eb --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +.git +/vendor +/docs \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 37a75bffed..4628ef8761 100644 --- a/Dockerfile +++ b/Dockerfile @@ -44,18 +44,18 @@ RUN apt -o Dpkg::Options::="--force-confdef" -y install software-properties-comm php libapache2-mod-php php-json php-mysql php-curl php-gd php-imap php-xml php-opcache php-soap php-xmlrpc \ php-common php-dev php-zip php-ssh2 php-mbstring php-ldap php-yaml php-snmp && apt -y remove brltty -COPY install/install.sh /tmp/ -RUN sh /tmp/install.sh -s 1 -v ${VERSION} -w ${WEBSERVER_HOME} -d ${DATABASE} -i docker -RUN sh /tmp/install.sh -s 2 -v ${VERSION} -w ${WEBSERVER_HOME} -d ${DATABASE} -i docker -RUN sh /tmp/install.sh -s 3 -v ${VERSION} -w ${WEBSERVER_HOME} -d ${DATABASE} -i docker -RUN sh /tmp/install.sh -s 4 -v ${VERSION} -w ${WEBSERVER_HOME} -d ${DATABASE} -i docker -RUN sh /tmp/install.sh -s 5 -v ${VERSION} -w ${WEBSERVER_HOME} -d ${DATABASE} -i docker +COPY --chown=root:root --chmod=550 install/install.sh /root/ +RUN sh /root/install.sh -s 1 -v ${VERSION} -w ${WEBSERVER_HOME} -d ${DATABASE} -i docker +RUN sh /root/install.sh -s 2 -v ${VERSION} -w ${WEBSERVER_HOME} -d ${DATABASE} -i docker +RUN sh /root/install.sh -s 3 -v ${VERSION} -w ${WEBSERVER_HOME} -d ${DATABASE} -i docker +RUN sh /root/install.sh -s 4 -v ${VERSION} -w ${WEBSERVER_HOME} -d ${DATABASE} -i docker +RUN sh /root/install.sh -s 5 -v ${VERSION} -w ${WEBSERVER_HOME} -d ${DATABASE} -i docker COPY . ${WEBSERVER_HOME} -RUN sh /tmp/install.sh -s 7 -v ${VERSION} -w ${WEBSERVER_HOME} -d ${DATABASE} -i docker -RUN sh /tmp/install.sh -s 8 -v ${VERSION} -w ${WEBSERVER_HOME} -d ${DATABASE} -i docker -RUN sh /tmp/install.sh -s 9 -v ${VERSION} -w ${WEBSERVER_HOME} -d ${DATABASE} -i docker -RUN sh /tmp/install.sh -s 10 -v ${VERSION} -w ${WEBSERVER_HOME} -d ${DATABASE} -i docker -RUN sh /tmp/install.sh -s 11 -v ${VERSION} -w ${WEBSERVER_HOME} -d ${DATABASE} -i docker +RUN sh /root/install.sh -s 7 -v ${VERSION} -w ${WEBSERVER_HOME} -d ${DATABASE} -i docker +RUN sh /root/install.sh -s 8 -v ${VERSION} -w ${WEBSERVER_HOME} -d ${DATABASE} -i docker +RUN sh /root/install.sh -s 9 -v ${VERSION} -w ${WEBSERVER_HOME} -d ${DATABASE} -i docker +RUN sh /root/install.sh -s 10 -v ${VERSION} -w ${WEBSERVER_HOME} -d ${DATABASE} -i docker +RUN sh /root/install.sh -s 11 -v ${VERSION} -w ${WEBSERVER_HOME} -d ${DATABASE} -i docker RUN apt-get clean && rm -rf /var/lib/apt/lists/* RUN echo >${WEBSERVER_HOME}/initialisation diff --git a/install/OS_specific/Docker/init.sh b/install/OS_specific/Docker/init.sh index 1bb9c50f77..8bfe14f32b 100644 --- a/install/OS_specific/Docker/init.sh +++ b/install/OS_specific/Docker/init.sh @@ -81,10 +81,15 @@ if [ -f ${WEBSERVER_HOME}/core/config/common.config.php ]; then else echo 'Start jeedom installation' JEEDOM_INSTALL=0 - rm -rf /root/install.sh - wget https://raw.githubusercontent.com/jeedom/core/${VERSION}/install/install.sh -O /root/install.sh - chmod +x /root/install.sh - /root/install.sh -s 6 -v ${VERSION} -w ${WEBSERVER_HOME} + + # do not re-install jeedom + if [ ! -f ${WEBSERVER_HOME}/core/config/common.config.sample.php ]; then + echo 'download again Jeedom' + /root/install.sh -s 6 -v ${VERSION} -w ${WEBSERVER_HOME} + # jeedom installation : install composer + /root/install.sh -s 10 -v ${VERSION} -w ${WEBSERVER_HOME} -i docker + fi + if [ $(which mysqld | wc -l) -ne 0 ]; then chown -R mysql:mysql /var/lib/mysql mysql_install_db --user=mysql --basedir=/usr/ --ldata=/var/lib/mysql/ diff --git a/install/install.sh b/install/install.sh index bb55dafea1..f6db7fa612 100644 --- a/install/install.sh +++ b/install/install.sh @@ -331,9 +331,6 @@ step_10_jeedom_installation() { export COMPOSER_ALLOW_SUPERUSER=1 cd ${WEBSERVER_HOME} composer install --no-ansi --no-dev --no-interaction --no-plugins --no-progress --no-scripts --optimize-autoloader - mkdir -p /tmp/jeedom - chmod 777 -R /tmp/jeedom - chown www-data:www-data -R /tmp/jeedom if [ "${INSTALLATION_TYPE}" != "docker" ];then php ${WEBSERVER_HOME}/install/install.php mode=force @@ -349,6 +346,9 @@ step_10_jeedom_installation() { step_11_jeedom_post() { echo "---------------------------------------------------------------------" echo "${YELLOW}Starting step 11 - Jeedom post-install${NORMAL}" + mkdir -p /tmp/jeedom + chmod 777 -R /tmp/jeedom + chown www-data:www-data -R /tmp/jeedom if [ $(crontab -l | grep jeedom | wc -l) -ne 0 ];then (echo crontab -l | grep -v "jeedom") | crontab -