diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3cc657130..6a78213ff 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -480,6 +480,7 @@ set (VENUS_QML_MODULE_SOURCES
pages/settings/PageSettingsCGwacs.qml
pages/settings/PageSettingsCGwacsOverview.qml
pages/settings/PageSettingsCanbus.qml
+ pages/settings/PageSettingsCustomizationChecks.qml
pages/settings/PageSettingsConnectivity.qml
pages/settings/PageSettingsDisplayAndAppearance.qml
pages/settings/PageSettingsDisplayBrief.qml
@@ -526,6 +527,7 @@ set (VENUS_QML_MODULE_SOURCES
pages/settings/PageSettingsTailscale.qml
pages/settings/PageSettingsTankPump.qml
pages/settings/PageSettingsTcpIp.qml
+ pages/settings/PageSettingsUsefulLinks.qml
pages/settings/PageSettingsVrm.qml
pages/settings/PageSettingsVecanDevice.qml
pages/settings/PageSettingsVecanDevices.qml
@@ -1261,4 +1263,3 @@ add_custom_target(
DEPENDS "${PROJECT_SOURCE_DIR}/src/themeobjects.h"
)
add_dependencies(${PROJECT_NAME} theme_parser)
-
diff --git a/pages/settings/PageSettingsCustomizationChecks.qml b/pages/settings/PageSettingsCustomizationChecks.qml
new file mode 100644
index 000000000..0ca138514
--- /dev/null
+++ b/pages/settings/PageSettingsCustomizationChecks.qml
@@ -0,0 +1,439 @@
+/*
+** Copyright (C) 2025 Victron Energy B.V.
+** See LICENSE.txt for license information.
+*/
+
+import QtQuick
+import Victron.VenusOS
+import QtQuick.Templates as T
+
+Page {
+ id: root
+
+ readonly property int fsModifiedState: fsModifiedStateItem.isValid ? fsModifiedStateItem.value : -1
+ readonly property int systemHooksState: systemHooksStateItem.isValid ? systemHooksStateItem.value : -1
+
+ property bool restoreFirmwareIntegrityPressed: false
+
+ function getSystemState() {
+ if (fsModifiedState === 0 && systemHooksState === 0 && modelItem.value.indexOf("Raspberry") === -1) {
+ //% "Supported"
+ return qsTrId("pagesettingscustomizationchecks_supported")
+ } else {
+ //% "No Victron Energy support"
+ return qsTrId("pagesettingscustomizationchecks_unsupported")
+ }
+ }
+
+ function scaleBytes(bytes) {
+ if (bytes < 1024) {
+ return bytes + " B"
+ } else if (bytes < 1024 * 1024) {
+ return (bytes / 1024).toFixed(1) + " KB"
+ } else if (bytes < 1024 * 1024 * 1024) {
+ return (bytes / 1024 / 1024).toFixed(1) + " MB"
+ } else {
+ return (bytes / 1024 / 1024 / 1024).toFixed(1) + " GB"
+ }
+ }
+
+ function getFsModifiedState() {
+ if (fsModifiedState === 0) {
+ //% "Ok"
+ return qsTrId("common_words_ok")
+ } else if (fsModifiedState === 1) {
+ //% "Modified"
+ return qsTrId("pagesettingscustomizationchecks_modified")
+ } else {
+ //% "Unknown: %1"
+ return qsTrId("pagesettingscustomizationchecks_unknown").arg(fsModifiedState)
+ }
+ }
+
+ function getSystemHooksState() {
+ if (systemHooksState === 0) {
+ //% "No"
+ return qsTrId("common_words_no")
+ } else if (systemHooksState === 1) {
+ //% "No, but enable at next boot (rc.local)"
+ return qsTrId("pagesettingscustomizationchecks_no_but_next_boot_rc_local")
+ } else if (systemHooksState === 2) {
+ //% "No, but enable at next boot (rcS.local)"
+ return qsTrId("pagesettingscustomizationchecks_no_but_next_boot_rcS_local")
+ } else if (systemHooksState === 3) {
+ //% "No, but enable at next boot (rc.local and rcS.local)"
+ return qsTrId("pagesettingscustomizationchecks_no_but_next_boot_rc_local_rcS_local")
+ } else if (systemHooksState === 4) {
+ //% "Yes, but disable at next boot"
+ return qsTrId("pagesettingscustomizationchecks_yes_but_disable_next_boot")
+ } else if (systemHooksState === 5) {
+ //% "Yes (rc.local)"
+ return qsTrId("pagesettingscustomizationchecks_yes_rc_local")
+ } else if (systemHooksState === 6) {
+ //% "Yes (rcS.local)"
+ return qsTrId("pagesettingscustomizationchecks_yes_rcS_local")
+ } else if (systemHooksState === 7) {
+ //% "Yes (rc.local and rcS.local)"
+ return qsTrId("pagesettingscustomizationchecks_yes_rc_local_rcS_local")
+ } else {
+ //% "Unknown: %1"
+ return qsTrId("pagesettingscustomizationchecks_unknown").arg(systemHooksState)
+ }
+ }
+
+ function getIsFirmwareUpToDate() {
+ if (firmwareOnlineAvailableBuildItem.isValid && firmwareOnlineAvailableBuildItem.value == "" && firmwareOnlineAvailableVersionItem.isValid && firmwareOnlineAvailableVersionItem.value == "") {
+ //% "Yes"
+ return qsTrId("common_words_yes")
+ } else if (Global.firmwareUpdate.checkingForUpdate) {
+ //% "Checking..."
+ return qsTrId("pagesettingscustomizationchecks_firmware_checking")
+ } else if ((firmwareStateItem.isValid ? firmwareStateItem.value : FirmwareUpdater.Idle) == FirmwareUpdater.ErrorDuringChecking) {
+ //% "Online check failed"
+ return qsTrId("pagesettingscustomizationchecks_firmware_online_check_failed")
+ } else if (Global.firmwareUpdate.onlineAvailableVersion == "") {
+ //% "Go to \"Firmware: Online update\" and check for updates"
+ return qsTrId("pagesettingscustomizationchecks_firmware_go_to_online_update")
+ } else {
+ //: %1 = firmware version
+ //% "No, %1 is available"
+ return qsTrId("pagesettingscustomizationchecks_firmware_no_available").arg(Global.firmwareUpdate.onlineAvailableVersion)
+ }
+ }
+
+ function getIsFirmwareUpToDateBool() {
+ if (firmwareOnlineAvailableBuildItem.isValid && firmwareOnlineAvailableBuildItem.value == "" && firmwareOnlineAvailableVersionItem.isValid && firmwareOnlineAvailableVersionItem.value == "") {
+ return true
+ } else {
+ return false
+ }
+ }
+
+ VeQuickItem {
+ id: allModificationsDisabledItem
+ uid: Global.systemSettings.serviceUid + "/Settings/System/CustomizationChecks/AllModificationsDisabled"
+ }
+ VeQuickItem {
+ id: hqSerialNumberItem
+ uid: Global.venusPlatform.serviceUid + "/Device/HQSerialNumber"
+ }
+ VeQuickItem {
+ id: modelItem
+ uid: Global.venusPlatform.serviceUid + "/Device/Model"
+ }
+ VeQuickItem {
+ id: signalKItem
+ uid: Global.venusPlatform.serviceUid + "/Services/SignalK/Enabled"
+ }
+ VeQuickItem {
+ id: nodeRedItem
+ uid: Global.venusPlatform.serviceUid + "/Services/NodeRed/Mode"
+ }
+ VeQuickItem {
+ id: firmwareOnlineAvailableBuildItem
+ uid: Global.venusPlatform.serviceUid + "/Firmware/Online/AvailableVersion"
+ }
+ VeQuickItem {
+ id: firmwareOnlineAvailableVersionItem
+ uid: Global.venusPlatform.serviceUid + "/Firmware/Online/AvailableBuild"
+ }
+ VeQuickItem {
+ id: firmwareOnlineCheckItem
+ uid: Global.venusPlatform.serviceUid + "/Firmware/Online/Check"
+ }
+ VeQuickItem {
+ id: firmwareProgressItem
+ uid: Global.venusPlatform.serviceUid + "/Firmware/Progress"
+ }
+ VeQuickItem {
+ id: firmwareStateItem
+ uid: Global.venusPlatform.serviceUid + "/Firmware/State"
+ }
+ VeQuickItem {
+ id: dataPartitionFreeSpaceItem
+ uid: Global.venusPlatform.serviceUid + "/CustomizationChecks/DataPartitionFreeSpace"
+ }
+ VeQuickItem {
+ id: forceFirmwareReinstallItem
+ uid: Global.venusPlatform.serviceUid + "/CustomizationChecks/ForceFirmwareReinstall"
+ }
+ VeQuickItem {
+ id: sshKeyForRootPresentItem
+ uid: Global.venusPlatform.serviceUid + "/CustomizationChecks/SshKeyForRootPresent"
+ }
+ VeQuickItem {
+ id: startCustomizationCheckItem
+ uid: Global.venusPlatform.serviceUid + "/CustomizationChecks/StartCheck"
+ }
+ VeQuickItem {
+ id: fsModifiedStateItem
+ uid: Global.venusPlatform.serviceUid + "/CustomizationChecks/FsModifiedState"
+ }
+ VeQuickItem {
+ id: systemHooksStateItem
+ uid: Global.venusPlatform.serviceUid + "/CustomizationChecks/SystemHooksState"
+ }
+
+
+ GradientListView {
+ id: supportAndTroubleshootListView
+
+ model: ObjectModel {
+
+ PrimaryListLabel {
+ //% "Customization Checks"
+ text: qsTrId("pagesettingscustomizationchecks_customization_checks")
+ }
+
+ ListText {
+ //% "System state"
+ text: qsTrId("pagesettingscustomizationchecks_system_state")
+ secondaryText: getSystemState()
+ secondaryLabel.color: fsModifiedState === 0 && systemHooksState === 0 && modelItem.value.indexOf("Raspberry") === -1 ? Theme.color_green : Theme.color_red
+ }
+
+ ListText {
+ //% "Device model"
+ text: qsTrId("pagesettingscustomizationchecks_device_model")
+ secondaryText: modelItem.value || ""
+ secondaryLabel.color: modelItem.isValid && modelItem.value.indexOf("Raspberry") === -1 ? Theme.color_green : Theme.color_red
+ }
+
+ ListText {
+ // Value is missing on older devices, therefore do not use colors on that
+ //% "HQ serial number"
+ text: qsTrId("pagesettingscustomizationchecks_hq_serial_number")
+ secondaryText: hqSerialNumberItem.value
+ allowed: defaultAllowed && hqSerialNumberItem.value != ""
+ }
+
+ ListText {
+ //% "Data partition free space"
+ text: qsTrId("pagesettingscustomizationchecks_data_free_space")
+ secondaryText: scaleBytes(dataPartitionFreeSpaceItem.value)
+ secondaryLabel.color: {
+ if (dataPartitionFreeSpaceItem.value < 1024 * 1024 * 10) {
+ return Theme.color_red;
+ } else {
+ return Theme.color_green;
+ }
+ }
+ }
+
+ ListText {
+ //% "Modifications loaded at boot"
+ text: qsTrId("pagesettingscustomizationchecks_modifications_at_boot")
+ secondaryText: getSystemHooksState()
+ secondaryLabel.color: systemHooksState === 0 ? Theme.color_green : systemHooksState < 4 ? Theme.color_orange : Theme.color_red
+ }
+
+ ListText {
+ //% "Firmware integrity"
+ text: qsTrId("pagesettingscustomizationchecks_firmware_integrity")
+ secondaryText: getFsModifiedState()
+ secondaryLabel.color: fsModifiedState === 0 ? Theme.color_green : Theme.color_red
+ }
+
+ ListText {
+ //% "Latest firmware version installed?"
+ text: qsTrId("pagesettingscustomizationchecks_latest_firmware_installed")
+ secondaryText: getIsFirmwareUpToDate()
+ secondaryLabel.color: getIsFirmwareUpToDateBool() ? Theme.color_green : Theme.color_red
+ }
+
+ ListText {
+ //% "Installed firmware version"
+ text: qsTrId("pagesettingscustomizationchecks_installed_firmware_version")
+ secondaryText: FirmwareVersion.versionText(dataItem.value, "venus")
+ dataItem.uid: Global.venusPlatform.serviceUid + "/Firmware/Installed/Version"
+ }
+
+ ListText {
+ //% "Installed build date/time"
+ text: qsTrId("pagesettingscustomizationchecks_build_date_time")
+ dataItem.uid: Global.venusPlatform.serviceUid + "/Firmware/Installed/Build"
+ }
+
+ ListText {
+ //% "Installed image type"
+ text: qsTrId("pagesettingscustomizationchecks_installed_image_type")
+ secondaryText: signalKItem.isValid || nodeRedItem.isValid ? qsTrId("settings_firmware_large") : qsTrId("settings_firmware_normal")
+ }
+
+ ListText {
+ //% "User SSH key present"
+ text: qsTrId("pagesettingscustomizationchecks_user_ssh_key_present")
+ secondaryText: sshKeyForRootPresentItem.value === 1 ? qsTrId("common_words_yes") : qsTrId("common_words_no")
+ }
+
+ PrimaryListLabel {
+ //% "Tools to normalize the system"
+ text: qsTrId("pagesettingscustomizationchecks_tools_to_normalize_system")
+ }
+
+ ListSwitch {
+ id: disableAllModifications
+ //% "Disable all modifications"
+ text: qsTrId("pagesettingscustomizationchecks_disable_all_modifications")
+ /*
+ Venus Platform
+ - Save the current state of Signal K and Node-RED
+ - Disable (service) and lock (GUI buttons) Signal K
+ - Disable (service) and lock (GUI buttons) Node-RED
+ - Disable rc.local by renaming it to rc.local.disabled
+ - Disable rcS.local by renaming it to rcS.local.disabled
+ */
+ dataItem.uid: Global.systemSettings.serviceUid + "/Settings/System/CustomizationChecks/AllModificationsDisabled"
+
+ bottomContentChildren: [
+ PrimaryListLabel {
+ width: Math.min(implicitWidth, disableAllModifications.maximumContentWidth)
+ topPadding: 0
+ bottomPadding: 0
+ color: Theme.color_font_secondary
+ //% "This disables all modifications, including SignalK and Node-RED."
+ text: qsTrId("pagesettingscustomizationchecks_disable_all_modifications_description")
+ }
+ ]
+
+ onCheckedChanged: {
+ // Show the dialog only if
+ // - restore integrity button was not pressed
+ // - it doesn't match the current state
+ if (!restoreFirmwareIntegrityPressed &&
+ ((!disableAllModifications.checked && systemHooksState < 4) || (disableAllModifications.checked && systemHooksState >= 4))) {
+ Global.dialogLayer.open(askForRebootDialogComponent)
+ } else {
+ startCustomizationCheckItem.setValue(1)
+ }
+ }
+
+ Component {
+ id: askForRebootDialogComponent
+
+ ModalWarningDialog {
+ //% "To apply changes a reboot is needed.
Press 'OK' to reboot now."
+ title: qsTrId("pagesettingscustomizationchecks_reboot_needed")
+ dialogDoneOptions: VenusOS.ModalDialog_DoneOptions_OkAndCancel
+ onClosed: {
+ if (result === T.Dialog.Accepted) {
+ Global.venusPlatform.reboot()
+ Qt.callLater(Global.dialogLayer.open, rebootingDialogComponent)
+ } else {
+ startCustomizationCheckItem.setValue(1)
+ }
+ }
+ }
+ }
+ }
+
+ ListButton {
+ //% "Reboot now to apply changes"
+ text: qsTrId("pagesettingscustomizationchecks_reboot_now_to_apply_changes")
+ //% "Reboot now"
+ button.text: qsTrId("settings_reboot_now")
+ writeAccessLevel: VenusOS.User_AccessType_User
+ allowed: defaultAllowed && systemHooksState >= 1 && systemHooksState <= 4
+ onClicked: Global.dialogLayer.open(confirmRebootDialogComponent)
+
+ Component {
+ id: confirmRebootDialogComponent
+
+ ModalWarningDialog {
+ //% "Press 'OK' to reboot"
+ title: qsTrId("press_ok_to_reboot")
+ dialogDoneOptions: VenusOS.ModalDialog_DoneOptions_OkAndCancel
+ onClosed: {
+ if (result === T.Dialog.Accepted) {
+ Global.venusPlatform.reboot()
+ Qt.callLater(Global.dialogLayer.open, rebootingDialogComponent)
+ }
+ }
+ }
+ }
+
+ Component {
+ id: rebootingDialogComponent
+
+ ModalWarningDialog {
+ title: BackendConnection.type === BackendConnection.DBusSource
+ //% "Rebooting..."
+ ? qsTrId("dialoglayer_rebooting")
+ //% "Device has been rebooted."
+ : qsTrId("dialoglayer_rebooted")
+
+ // On device, dialog cannot be dismissed; just wait until device is rebooted.
+ dialogDoneOptions: VenusOS.ModalDialog_DoneOptions_OkOnly
+ footer.enabled: BackendConnection.type !== BackendConnection.DBusSource
+ footer.opacity: footer.enabled ? 1 : 0
+ }
+ }
+ }
+
+ ListButton {
+ //% "Firmware: Restore clean state"
+ text: qsTrId("pagesettingscustomizationchecks_firmware_restore_clean_state")
+ button.text: {
+ if (Global.firmwareUpdate.state === FirmwareUpdater.DownloadingAndInstalling) {
+ if (firmwareProgressItem.value) {
+ //: Firmware update firmwareProgressItem. %1 = firmware version, %2 = current update progress
+ //% "Installing %1 %2%"
+ qsTrId("settings_firmware_online_installing_progress").arg(Global.firmwareUpdate.onlineAvailableVersion).arg(firmwareProgressItem.value)
+ }
+ //: %1 = firmware version
+ //% "Installing %1..."
+ qsTrId("settings_firmware_online_installing").arg(Global.firmwareUpdate.onlineAvailableVersion)
+ } else {
+ //% "Press to restore"
+ qsTrId("pagesettingscustomizationchecks_press_to_restore")
+ }
+ }
+ writeAccessLevel: VenusOS.User_AccessType_User
+ onClicked: Global.dialogLayer.open(confirmReinstallDialogComponent)
+
+ Component {
+ id: confirmReinstallDialogComponent
+
+ ModalWarningDialog {
+ //% "This will disable all modifications, download and reinstall the latest available firmware.
Internet connectivity is required.
Press 'OK' to continue."
+ title: qsTrId("pagesettingscustomizationchecks_firmware_restore_clean_state_description")
+ dialogDoneOptions: VenusOS.ModalDialog_DoneOptions_OkAndCancel
+ onClosed: {
+ if (result === T.Dialog.Accepted) {
+ restoreFirmwareIntegrityPressed = true
+ allModificationsDisabledItem.setValue(1)
+ forceFirmwareReinstallItem.setValue(1)
+ }
+ }
+ }
+ }
+ }
+
+ ListNavigation {
+ //% "Firmware: Online update"
+ text: qsTrId("pagesettingscustomizationchecks_firmware_online_update")
+ onClicked: {
+ Global.pageManager.pushPage("/pages/settings/PageSettingsFirmwareOnline.qml", { title: text })
+ }
+ }
+
+ ListNavigation {
+ //% "Firmware: Install from SD/USB"
+ text: qsTrId("pagesettingscustomizationchecks_firmware_install_from_sd_usb")
+ onClicked: {
+ Global.pageManager.pushPage("/pages/settings/PageSettingsFirmwareOffline.qml", { title: text })
+ }
+ }
+ }
+ }
+
+ Component.onCompleted: {
+ // Check for updates
+ if (firmwareOnlineCheckItem.isValid && firmwareOnlineCheckItem.value === 0) {
+ Global.firmwareUpdate.checkForUpdate(VenusOS.Firmware_UpdateType_Online)
+ }
+
+ // Run system integrity check
+ startCustomizationCheckItem.setValue(1)
+ }
+}
diff --git a/pages/settings/PageSettingsGeneral.qml b/pages/settings/PageSettingsGeneral.qml
index af18b73cb..73511c590 100644
--- a/pages/settings/PageSettingsGeneral.qml
+++ b/pages/settings/PageSettingsGeneral.qml
@@ -183,6 +183,50 @@ Page {
}
}
}
+
+ SettingsListHeader { }
+
+ ListNavigation {
+ //% "Useful Links"
+ text: qsTrId("pagesettingsgeneral_useful_links")
+ onClicked: Global.pageManager.pushPage("/pages/settings/PageSettingsUsefulLinks.qml", {"title": text})
+ }
+
+ ListNavigation {
+ //% "Customization Checks"
+ text: qsTrId("pagesettingsgeneral_customization_checks")
+ secondaryText: getSystemState()
+ secondaryLabel.color: fsModifiedState === 0 && systemHooksState === 0 && modelItem.value.indexOf("Raspberry") === -1 ? Theme.color_font_primary : Theme.color_red
+ onClicked: Global.pageManager.pushPage("/pages/settings/PageSettingsCustomizationChecks.qml", {"title": text})
+
+ readonly property int fsModifiedState: fsModifiedStateItem.isValid ? fsModifiedStateItem.value : -1
+ readonly property int systemHooksState: systemHooksStateItem.isValid ? systemHooksStateItem.value : -1
+
+ property bool restoreFirmwareIntegrityPressed: false
+
+ function getSystemState() {
+ if (fsModifiedState === 0 && systemHooksState === 0 && modelItem.value.indexOf("Raspberry") === -1) {
+ //% "Supported"
+ return qsTrId("pagesettingscustomizationchecks_supported")
+ } else {
+ //% "No Victron Energy support"
+ return qsTrId("pagesettingscustomizationchecks_unsupported")
+ }
+ }
+
+ VeQuickItem {
+ id: fsModifiedStateItem
+ uid: Global.venusPlatform.serviceUid + "/CustomizationChecks/FsModifiedState"
+ }
+ VeQuickItem {
+ id: systemHooksStateItem
+ uid: Global.venusPlatform.serviceUid + "/CustomizationChecks/SystemHooksState"
+ }
+ VeQuickItem {
+ id: modelItem
+ uid: Global.venusPlatform.serviceUid + "/Device/Model"
+ }
+ }
}
}
}
diff --git a/pages/settings/PageSettingsIntegrations.qml b/pages/settings/PageSettingsIntegrations.qml
index 2f6311d69..d3cd1078b 100644
--- a/pages/settings/PageSettingsIntegrations.qml
+++ b/pages/settings/PageSettingsIntegrations.qml
@@ -9,6 +9,13 @@ import Victron.VenusOS
Page {
id: root
+ readonly property bool allModificationsDisabled: allModificationsDisabledItem.isValid && allModificationsDisabledItem.value === 1
+
+ VeQuickItem {
+ id: allModificationsDisabledItem
+ uid: Global.systemSettings.serviceUid + "/Settings/System/CustomizationChecks/AllModificationsDisabled"
+ }
+
GradientListView {
id: settingsListView
@@ -168,6 +175,12 @@ Page {
text: qsTrId("settings_large_features_not_offically_supported")
}
+ PrimaryListLabel {
+ //% "Venus OS Large features are disabled, since \"Disable all modifications\" under \"Settings -> General -> Customization checks\" is enabled."
+ text: qsTrId("settings_large_features_currently_disabled")
+ allowed: root.allModificationsDisabled
+ }
+
ListSwitch {
id: signalk
@@ -175,6 +188,7 @@ Page {
text: qsTrId("settings_large_signal_k")
dataItem.uid: Global.venusPlatform.serviceUid + "/Services/SignalK/Enabled"
allowed: dataItem.isValid
+ enabled: userHasWriteAccess && !root.allModificationsDisabled
}
PrimaryListLabel {
diff --git a/pages/settings/PageSettingsNodeRed.qml b/pages/settings/PageSettingsNodeRed.qml
index 4a4e98b41..95f4b40ab 100644
--- a/pages/settings/PageSettingsNodeRed.qml
+++ b/pages/settings/PageSettingsNodeRed.qml
@@ -9,14 +9,28 @@ import Victron.VenusOS
Page {
id: root
+ readonly property bool allModificationsDisabled: allModificationsDisabledItem.isValid && allModificationsDisabledItem.value === 1
+
+ VeQuickItem {
+ id: allModificationsDisabledItem
+ uid: Global.systemSettings.serviceUid + "/Settings/System/CustomizationChecks/AllModificationsDisabled"
+ }
+
GradientListView {
model: ObjectModel {
+ PrimaryListLabel {
+ //% "Venus OS Large features are disabled, since \"Disable all modifications\" under \"Settings -> Support & Troubleshoot -> Customization checks\" is enabled."
+ text: qsTrId("settings_large_features_currently_disabled")
+ allowed: root.allModificationsDisabled
+ }
+
ListRadioButtonGroup {
id: nodered
text: qsTrId("settings_large_node_red")
dataItem.uid: Global.venusPlatform.serviceUid + "/Services/NodeRed/Mode"
allowed: dataItem.isValid
+ enabled: userHasWriteAccess && !root.allModificationsDisabled
optionModel: [
{ display: CommonWords.disabled, value: VenusOS.NodeRed_Mode_Disabled },
{ display: CommonWords.enabled, value: VenusOS.NodeRed_Mode_Enabled },
diff --git a/pages/settings/PageSettingsUsefulLinks.qml b/pages/settings/PageSettingsUsefulLinks.qml
new file mode 100644
index 000000000..6c2c2c545
--- /dev/null
+++ b/pages/settings/PageSettingsUsefulLinks.qml
@@ -0,0 +1,45 @@
+/*
+** Copyright (C) 2025 Victron Energy B.V.
+** See LICENSE.txt for license information.
+*/
+
+import QtQuick
+import Victron.VenusOS
+
+Page {
+ id: root
+
+ GradientListView {
+ model: ObjectModel {
+ ListLink {
+ //% "Warranty check"
+ text: qsTrId("settings_support_links_warranty")
+ url: "https://www.victronenergy.com/support"
+ }
+
+ ListLink {
+ //% "FAQ"
+ text: qsTrId("settings_support_links_faq")
+ url: "https://www.victronenergy.com/media/pg/Energy_Storage_System/en/faq.html"
+ }
+
+ ListLink {
+ //% "How to troubleshoot"
+ text: qsTrId("settings_support_links_troubleshoot")
+ url: "https://www.victronenergy.com/media/pg/Venus_GX/en/troubleshooting.html"
+ }
+
+ ListLink {
+ //% "Forum"
+ text: qsTrId("settings_support_links_forum")
+ url: "https://community.victronenergy.com/"
+ }
+
+ ListLink {
+ //% "Find a local distributor"
+ text: qsTrId("settings_support_links_distributor")
+ url: "https://www.victronenergy.com/where-to-buy"
+ }
+ }
+ }
+}