From f7c595182505ce5d8b7d9bd0a5226b2c70eebc0a Mon Sep 17 00:00:00 2001 From: Jens Maus Date: Wed, 14 Oct 2026 17:32:25 +0000 Subject: [PATCH 1/3] webui: add a recovery mode restart to the maintenance page The maintenance page gets a "Recovery Modus" row next to the safe mode restart. The new RecoveryMode.enter API method (admin only) creates /usr/local/.recoveryMode and reboots, which makes the boot loader start the recovery system. The row is hidden on the oci and lxc platforms, which have no recovery system; the get_platform helper is added to eq3_old/common.tcl for this. The confirmation for deleting a program now includes the program name, which the program list already passes in. Applies OpenCCU rootfs patch 0012-WebUI-RecoveryButton. --- src/webui/www/api/methods.conf | 7 ++++ .../www/api/methods/recoverymode/enter.tcl | 23 +++++++++++++ src/webui/www/config/cp_maintenance.cgi | 33 +++++++++++++++++++ src/webui/www/tcl/eq3_old/common.tcl | 4 +++ .../www/webui/js/lang/de/translate.lang.js | 9 ++++- .../www/webui/js/lang/en/translate.lang.js | 9 ++++- 6 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 src/webui/www/api/methods/recoverymode/enter.tcl diff --git a/src/webui/www/api/methods.conf b/src/webui/www/api/methods.conf index b5bffa717..5e1d4c408 100644 --- a/src/webui/www/api/methods.conf +++ b/src/webui/www/api/methods.conf @@ -991,6 +991,13 @@ CCU.setSecurityLevel { ARGUMENTS {_session_id_ level} } +RecoveryMode.enter { + LEVEL ADMIN + SCRIPT_FILE recoverymode/enter.tcl + INFO {Startet die HomeMatic Zentrale im Recovery Modus} + ARGUMENTS {_session_id_} +} + CCU.getSerial { LEVEL ADMIN SCRIPT_FILE ccu/getserial.tcl diff --git a/src/webui/www/api/methods/recoverymode/enter.tcl b/src/webui/www/api/methods/recoverymode/enter.tcl new file mode 100644 index 000000000..c78199325 --- /dev/null +++ b/src/webui/www/api/methods/recoverymode/enter.tcl @@ -0,0 +1,23 @@ +## +# RecoveryMode.enter +# Startet die HomeMatic Zentrale im Recovery Modus +# +# Parameter: +# paasword: [string] Passwort für den Recovery Modus +# +# Rückgabewert: [bool] +# true +## + +set FLAG_FILE "/usr/local/.recoveryMode" + +# Flag-Datei erzeugen +set fd [open $FLAG_FILE w] +puts $fd 1 +close $fd + +# Neustart +rega system.Save() +exec /sbin/reboot -d 2 2>/dev/null >/dev/null & + +jsonrpc_response true diff --git a/src/webui/www/config/cp_maintenance.cgi b/src/webui/www/config/cp_maintenance.cgi index f38b744ee..2c9ce297d 100644 --- a/src/webui/www/config/cp_maintenance.cgi +++ b/src/webui/www/config/cp_maintenance.cgi @@ -539,6 +539,27 @@ proc action_put_page {} { } } + # Recovery Modus + if {[get_platform] != "oci" && [get_platform] != "lxc"} { + table_row {class="CLASS20902 j_noForcedUpdate j_fwUpdateOnly"} { + table_data {class="CLASS20903"} $styleMaxWidth { + #puts "Recovery
" + #puts "Modus" + puts "\${dialogSettingsCMTDCCURecoveryMode}" + } + table_data {class="CLASS20904"} { + division {class="popupControls CLASS20905"} { + division {class="CLASS20910 colorGradient50px"} {onClick="OnEnterRecoveryMode();"} { + puts "\${dialogSettingsCMBtnCCURestartRecovery}" + } + } + } + table_data {align="left"} {class="CLASS20904"} { + puts "\${dialogSettingsCMHintRestartRecoveryMode}" + } + } + } + table_row {class="CLASS20902 j_noForcedUpdate j_fwUpdateOnly"} { table_data {class="CLASS20903"} $styleMaxWidth { #puts "Fehler-
" @@ -717,6 +738,18 @@ proc action_put_page {} { } }); } + + OnEnterRecoveryMode = function() { + new YesNoDialog(translateKey("dialogRecoveryCheck"), translateKey("dialogQuestionRestartRecoveryMode"), function(result) { + if (result == YesNoDialog.RESULT_YES) + { + MessageBox.show(translateKey("dialogRestartRecoveryModeTitle"), translateKey("dialogRestartRecoveryModeContent"), function() { + window.location.href = "/"; + }); + homematic("RecoveryMode.enter"); + } + }); + } } diff --git a/src/webui/www/tcl/eq3_old/common.tcl b/src/webui/www/tcl/eq3_old/common.tcl index e98a92faa..1051671f6 100644 --- a/src/webui/www/tcl/eq3_old/common.tcl +++ b/src/webui/www/tcl/eq3_old/common.tcl @@ -173,6 +173,10 @@ proc get_version { } { return [read_var /VERSION VERSION] } +proc get_platform { } { + return [read_var /VERSION PLATFORM] +} + proc get_CoProVersion {} { set cfgFile "/var/hm_mode" set unknownVersion "0.0.0" diff --git a/src/webui/www/webui/js/lang/de/translate.lang.js b/src/webui/www/webui/js/lang/de/translate.lang.js index ec441a09b..feba52afe 100644 --- a/src/webui/www/webui/js/lang/de/translate.lang.js +++ b/src/webui/www/webui/js/lang/de/translate.lang.js @@ -400,9 +400,10 @@ jQuery.extend(true,langJSON, { "dialogNo" : "Nein", "dialogBack" : "Zur%FCck", "dialogSafetyCheck" : "Sicherheitsabfrage", + "dialogRecoveryCheck" : "Sicherheitsabfrage", "dialogInfo" : "Info", "dialogHint" : "Hinweis", - "dialogQuestionRemoveProgram" : "M%F6chten Sie das Programm wirklich l%F6schen?", + "dialogQuestionRemoveProgram" : "M%F6chten Sie das Programm '%s' wirklich l%F6schen?", "dialogQuestionRemoveLink" : "M%F6chten Sie die Verkn%FCpfung wirklich l%F6schen?", "dialogQuestionRemoveUser" : "M%F6chten Sie den Benutzer wirklich l%F6schen?", "dialogQuestionRemoveSysVar" : "M%F6chten Sie die Systemvariable wirklich l%F6schen?", @@ -412,6 +413,7 @@ jQuery.extend(true,langJSON, { "dialogQuestionRestart" : "Best%E4tigen Sie hier, um den Neustart durchzuf%FChren.", "dialogQuestionShutdown" : "Best%E4tigen Sie hier, um das Herunterfahren durchzuf%FChren.", "dialogQuestionRestartSafeMode" : "M%F6chten Sie die " + HMIdentifier.de.CCUFullNameText + " wirklich im abgesicherten Modus starten?", + "dialogQuestionRestartRecoveryMode" : "M%F6chten Sie die " + HMIdentifier.de.CCUFullNameText + " wirklich im Recovery Modus starten?", "dialogUserAccountTitle" : "Benutzerkonto - Konfiguration", "dialogProgChoseSysVar" : "Programme - Systemvariablenauswahl", "dialogEditRoom" : "Raum bearbeiten", @@ -434,7 +436,9 @@ jQuery.extend(true,langJSON, { "dialogChooseBidCosInterface" : "Auswahl BidCos-Interface", "dialogChooseChannel" : "Kanalauswahl", "dialogRestartSafeModeTitle" : "Neustart", + "dialogRestartRecoveryModeTitle" : "Neustart", "dialogRestartSafeModeContent" : "Die Zentrale startet nun im abgesicherten Modus. Klicken Sie auf \"OK\", um sich neu anzumelden.", + "dialogRestartRecoveryModeContent" : "Die Zentrale startet nun im Recovery Modus. Klicken Sie auf \"OK\", um sich neu anzumelden.", "dialogBtnPerformRestart" : "Neustart ", "dialogBtnPerformShutdown" : "Herunterfahren", "dialogEditRoomBtnClose" : "Schlie%DFen", @@ -469,6 +473,7 @@ jQuery.extend(true,langJSON, { "dialogSettingsCMTDCCURestart" : HMIdentifier.de.CCUShortName + "
Neustart", "dialogSettingsCMTDCCUShutdown" : "Herunterfahren", "dialogSettingsCMTDCCUSafeMode" : "Abgesicherter
Modus", + "dialogSettingsCMTDCCURecoveryMode" : "Recovery
Modus", "dialogSettingsCMTDErrorProtocol" : "Fehler-
protokoll", "dialogSettingsCMLblActualSoftwareVersion" : "Aktuelle Software-Version:", "dialogSettingsCMLblAvailableSoftwareVersion" : "Verf%FCgbare Software-Version:", @@ -487,6 +492,7 @@ jQuery.extend(true,langJSON, { "dialogSettingsCMBtnCCURestart" : "Neustart", "dialogSettingsCMBtnCCUShutdown" : "Herunterfahren", "dialogSettingsCMBtnCCURestartSafe" : "Neustart im abgesicherten Modus", + "dialogSettingsCMBtnCCURestartRecovery" : "Neustart im Recovery Modus", "dialogSettingsCMBtnLogSysLogServerAddress" : "Einstellungen %FCbernehmen", "dialogSettingsCMBtnLogLoadLogFile" : "Logdatei herunterladen", "dialogSettingsCMErrorTitle" : "Softwareupdate - Fehler", @@ -499,6 +505,7 @@ jQuery.extend(true,langJSON, { "dialogSettingsCMHintRestart" : "Zentrale neu starten. Ver%E4nderte Einstellungen werden vorher gespeichert.", "dialogSettingsCMHintShutdown" : "Zentrale herunterfahren. Ver%E4nderte Einstellungen werden vorher gespeichert.", "dialogSettingsCMHintRestartSafeMode" : "Homematic Zentrale einmalig im abgesicherten Modus starten. Im abgesicherten Modus wird die installierte Zusatzsoftware nicht gestartet. Ver%E4nderte Einstellungen werden vorher gespeichert.", + "dialogSettingsCMHintRestartRecoveryMode" : "HomeMatic Zentrale einmalig im Recovery Modus starten. Im Recovery Modus werden administrative Werkzeuge zur Verf%FCgung gestellt. Ver%E4nderte Einstellungen werden vorher gespeichert.", "dialogSettingsCMHintErrorLog" : "Stellen Sie die Anzahl der Logmeldungen ein, die von der Zentrale generiert werden sollen.
Sie k%F6nnen zus%E4tzlich einen Rechner angeben, dem die Zentrale ihre Logmeldungen per Syslog schickt. Auf diesem Rechner mu%DF entsprechende Software installiert sein, die die Meldungen entgegennimmt.
Zu Diagnosezwecken k%F6nnen die aktuellen Logmeldungen der Zentrale in einer Textdatei heruntergeladen werden.", "dialogSettingsCMLogLevel0" : "Alles loggen", "dialogSettingsCMLogLevel1" : "Information", diff --git a/src/webui/www/webui/js/lang/en/translate.lang.js b/src/webui/www/webui/js/lang/en/translate.lang.js index c1e3cc3d6..2b7d1e1ea 100644 --- a/src/webui/www/webui/js/lang/en/translate.lang.js +++ b/src/webui/www/webui/js/lang/en/translate.lang.js @@ -399,9 +399,10 @@ jQuery.extend(true,langJSON, { "dialogNo" : "No", "dialogBack" : "Back", "dialogSafetyCheck" : "Confirmation prompt", + "dialogRecoveryCheck" : "Confirmation prompt", "dialogInfo" : "Info", "dialogHint" : "Please note", - "dialogQuestionRemoveProgram" : "Do you really want to delete the program?", + "dialogQuestionRemoveProgram" : "Do you really want to delete the program '%s'?", "dialogQuestionRemoveLink" : "Do you really want to delete the connection?", "dialogQuestionRemoveUser" : "Do you really want to delete the user?", "dialogQuestionRemoveSysVar" : "Do you really want to delete the system variable?", @@ -411,6 +412,7 @@ jQuery.extend(true,langJSON, { "dialogQuestionRestart" : "Please click here to perform the restart?", "dialogQuestionShutdown" : "Please click here to perform the shutdown?", "dialogQuestionRestartSafeMode" : "Do you really want to start the " + HMIdentifier.en.CCUFullNameText + " in the safe mode?", + "dialogQuestionRestartRecoveryMode" : "Do you really want to start the " + HMIdentifier.en.CCUFullNameText + " in the recovery mode?", "dialogUserAccountTitle" : "User account configuration", "dialogProgChoseSysVar" : "Programs - system variable selection", "dialogEditRoom" : "Edit room", @@ -433,7 +435,9 @@ jQuery.extend(true,langJSON, { "dialogChooseBidCosInterface" : "Selection BidCos-Interface", "dialogChooseChannel" : "Channel selection", "dialogRestartSafeModeTitle" : "Restart", + "dialogRestartRecoveryModeTitle" : "Restart", "dialogRestartSafeModeContent" : "The Central Control Unit will now start in safe mode. Please click \"OK\" to log in again.", + "dialogRestartRecoveryModeContent" : "The Central Control Unit will now start in recovery mode. Please click \"OK\" to log in again.", "dialogBtnPerformRestart" : "Restart ", "dialogBtnPerformShutdown" : "Shutdown", "dialogEditRoomBtnClose" : "Close", @@ -468,6 +472,7 @@ jQuery.extend(true,langJSON, { "dialogSettingsCMTDCCURestart" : HMIdentifier.en.CCUShortName + "
restart", "dialogSettingsCMTDCCUShutdown" : "Shutdown", "dialogSettingsCMTDCCUSafeMode" : "Safe
mode", + "dialogSettingsCMTDCCURecoveryMode" : "Recovery
mode", "dialogSettingsCMTDErrorProtocol" : "Error
log", "dialogSettingsCMLblActualSoftwareVersion" : "Current software version:", "dialogSettingsCMLblAvailableSoftwareVersion" : "Available software version:", @@ -486,6 +491,7 @@ jQuery.extend(true,langJSON, { "dialogSettingsCMBtnCCURestart" : "Restart", "dialogSettingsCMBtnCCUShutdown" : "Shutdown", "dialogSettingsCMBtnCCURestartSafe" : "Restart in safe mode", + "dialogSettingsCMBtnCCURestartRecovery" : "Restart in recovery mode", "dialogSettingsCMBtnLogSysLogServerAddress" : "Apply settings", "dialogSettingsCMBtnLogLoadLogFile" : "Download log file", "dialogSettingsCMErrorTitle" : "Software update - error", @@ -498,6 +504,7 @@ jQuery.extend(true,langJSON, { "dialogSettingsCMHintRestart" : "Restart CCU. The changed settings will be saved before.", "dialogSettingsCMHintShutdown" : "Shutdown CCU. The changed settings will be saved before.", "dialogSettingsCMHintRestartSafeMode" : "Start Homematic Central Control Unit in safe mode once. In safe mode, the installed additional software will not be started. The changed settings will be saved before.", + "dialogSettingsCMHintRestartRecoveryMode" : "Start HomeMatic Central Control Unit in recovery mode once. In recovery mode, maintenance tasks can be performed. The changed settings will be saved before.", "dialogSettingsCMHintErrorLog" : "Please set the number of log messages to be generated by the CCU.
You can also specify a computer that will receive the log messages from the CCU via syslog. An additional software that will be able to receive this messages has to be installed on the computer.
To analyse the results, the current log messages of the CCU can be downloaded in a text file.", "dialogSettingsCMLogLevel0" : "Log all", "dialogSettingsCMLogLevel1" : "Information", From dd9616cf5865f110563ee8079bd1531cc9e32351 Mon Sep 17 00:00:00 2001 From: Jens Maus Date: Wed, 14 Oct 2026 21:15:59 +0000 Subject: [PATCH 2/3] webui: harden the recovery mode API RecoveryMode.enter now rejects the call on the oci and lxc platforms, which have no recovery system, saves the ReGa settings before creating /usr/local/.recoveryMode, and removes the flag again if it cannot be written or the restart cannot be started. Applies the fix to OpenCCU rootfs patch 0012 from OpenCCU/OpenCCU#4434. --- .../www/api/methods/recoverymode/enter.tcl | 40 +++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/src/webui/www/api/methods/recoverymode/enter.tcl b/src/webui/www/api/methods/recoverymode/enter.tcl index c78199325..e64c0bfb5 100644 --- a/src/webui/www/api/methods/recoverymode/enter.tcl +++ b/src/webui/www/api/methods/recoverymode/enter.tcl @@ -2,8 +2,7 @@ # RecoveryMode.enter # Startet die HomeMatic Zentrale im Recovery Modus # -# Parameter: -# paasword: [string] Passwort für den Recovery Modus +# Parameter: kein # # Rückgabewert: [bool] # true @@ -11,13 +10,38 @@ set FLAG_FILE "/usr/local/.recoveryMode" +# Plattform ermitteln: oci und lxc haben kein Recovery-System +set platform "" +catch { + set fd [open /VERSION r] + while { [gets $fd line] >= 0 } { + if { [regexp {^ *PLATFORM *= *(.*)$} $line dummy platform] } { break } + } + close $fd +} +if { $platform == "oci" || $platform == "lxc" } { + jsonrpc_error 501 "recovery mode is not supported on platform $platform" +} + +# Einstellungen speichern, bevor die Flag-Datei erzeugt wird +if { [catch {rega system.Save()} err] } { + jsonrpc_error 500 "could not save the settings: $err" +} + # Flag-Datei erzeugen -set fd [open $FLAG_FILE w] -puts $fd 1 -close $fd +if { [catch { + set fd [open $FLAG_FILE w] + puts $fd 1 + close $fd +} err] } { + file delete -force $FLAG_FILE + jsonrpc_error 500 "could not create the recovery mode flag: $err" +} -# Neustart -rega system.Save() -exec /sbin/reboot -d 2 2>/dev/null >/dev/null & +# Neustart, bei einem Fehler die Flag-Datei wieder entfernen +if { [catch {exec /sbin/reboot -d 2 2>/dev/null >/dev/null &} err] } { + file delete -force $FLAG_FILE + jsonrpc_error 500 "could not restart: $err" +} jsonrpc_response true From 0b7e897c62980714db6a57ec2aa14665374952dc Mon Sep 17 00:00:00 2001 From: Jens Maus Date: Wed, 14 Oct 2026 21:54:22 +0000 Subject: [PATCH 3/3] webui: reject recovery mode when the platform is unknown RecoveryMode.enter now returns an error if /VERSION cannot be read or has no PLATFORM entry, before saving, creating the flag or restarting. Applies the fix to OpenCCU rootfs patch 0012 from OpenCCU/OpenCCU#4435. --- src/webui/www/api/methods/recoverymode/enter.tcl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/webui/www/api/methods/recoverymode/enter.tcl b/src/webui/www/api/methods/recoverymode/enter.tcl index e64c0bfb5..a933bb2bd 100644 --- a/src/webui/www/api/methods/recoverymode/enter.tcl +++ b/src/webui/www/api/methods/recoverymode/enter.tcl @@ -19,6 +19,9 @@ catch { } close $fd } +if { $platform == "" } { + jsonrpc_error 500 "could not determine the platform from /VERSION" +} if { $platform == "oci" || $platform == "lxc" } { jsonrpc_error 501 "recovery mode is not supported on platform $platform" }