From ad91f6a61602fc63a375782388b9f655234106dd Mon Sep 17 00:00:00 2001
From: kwindrem <58538395+kwindrem@users.noreply.github.com>
Date: Tue, 25 Feb 2025 09:49:40 -0800
Subject: [PATCH] add support for v3.54, v3.60~25
---
FileSets/PatchSource/HubData.qml | 6 +-
FileSets/PatchSource/HubData.qml.patch | 22 +-
.../PatchSource/MbItemDigitalInput.qml-v3.40 | 2 -
.../MbItemDigitalInput.qml-v3.40.patch | 10 +-
FileSets/PatchSource/ObjectAcConnection.qml | 4 -
.../PatchSource/ObjectAcConnection.qml.patch | 11 +-
FileSets/PatchSource/PageDigitalInput.qml | 2 -
.../PatchSource/PageDigitalInput.qml.patch | 16 +-
FileSets/PatchSource/PageMain.qml-v3.22 | 2 -
FileSets/PatchSource/PageMain.qml-v3.22.patch | 10 +-
FileSets/PatchSource/PageMain.qml-v3.34 | 2 -
FileSets/PatchSource/PageMain.qml-v3.34.patch | 8 +-
FileSets/PatchSource/PageMain.qml-v3.40 | 2 -
FileSets/PatchSource/PageMain.qml-v3.40.patch | 10 +-
FileSets/PatchSource/PageMain.qml-v3.60~21 | 294 ++++
.../PatchSource/PageMain.qml-v3.60~21.orig | 254 +++
.../PatchSource/PageMain.qml-v3.60~21.patch | 73 +
.../PatchSource/PageSettingsDisplay.qml-v3.14 | 2 -
.../PageSettingsDisplay.qml-v3.14.patch | 14 +-
.../PatchSource/PageSettingsDisplay.qml-v3.22 | 2 -
.../PageSettingsDisplay.qml-v3.22.patch | 14 +-
.../PatchSource/PageSettingsDisplay.qml-v3.34 | 2 -
.../PageSettingsDisplay.qml-v3.34.patch | 10 +-
.../PatchSource/PageSettingsDisplay.qml-v3.40 | 2 -
.../PageSettingsDisplay.qml-v3.40.patch | 10 +-
.../PatchSource/PageSettingsGenerator.qml | 3 -
.../PageSettingsGenerator.qml.patch | 11 +-
.../PatchSource/PageSettingsRelay.qml-v3.22 | 5 -
.../PageSettingsRelay.qml-v3.22.patch | 23 +-
.../PageSettingsRelay.qml-v3.60~16 | 5 -
.../PageSettingsRelay.qml-v3.60~16.patch | 23 +-
FileSets/PatchSource/main.qml-v3.22 | 3 -
FileSets/PatchSource/main.qml-v3.22.patch | 27 +-
FileSets/PatchSource/main.qml-v3.34 | 3 -
FileSets/PatchSource/main.qml-v3.34.patch | 27 +-
FileSets/PatchSource/main.qml-v3.40 | 3 -
FileSets/PatchSource/main.qml-v3.40.patch | 32 +-
FileSets/PatchSource/main.qml-v3.50 | 3 -
FileSets/PatchSource/main.qml-v3.50.patch | 23 +-
FileSets/PatchSource/main.qml-v3.51 | 3 -
FileSets/PatchSource/main.qml-v3.51.patch | 27 +-
FileSets/PatchSource/startstop.py-v3.60~25 | 1552 +++++++++++++++++
.../PatchSource/startstop.py-v3.60~25.orig | 1357 ++++++++++++++
.../PatchSource/startstop.py-v3.60~25.patch | 539 ++++++
.../PageSettingsGuiModsGauges.qml | 2 +-
FileSets/v3.10/Battery.qml | 1 -
FileSets/v3.10/MbEditBox.qml | 1 -
FileSets/v3.10/MbEditBoxDateTime.qml | 1 -
FileSets/v3.10/MbItem.qml | 1 -
FileSets/v3.10/MbItemDigitalInput.qml | 1 -
FileSets/v3.10/MbSpinBox.qml | 1 -
FileSets/v3.10/MbStyle.qml | 1 -
FileSets/v3.10/MbSubMenu.qml | 1 -
FileSets/v3.10/Multi.qml | 1 -
FileSets/v3.10/OverviewBox.qml | 1 -
FileSets/v3.10/OverviewConnection.qml | 1 -
FileSets/v3.10/OverviewConnectionEnd.qml | 1 -
FileSets/v3.10/OverviewSolarCharger.qml | 1 -
FileSets/v3.10/OverviewSolarInverter.qml | 1 -
FileSets/v3.10/OverviewTankDelegate.qml | 1 -
FileSets/v3.10/OverviewTanks.qml | 1 -
FileSets/v3.10/PageDigitalInput.qml | 1 -
FileSets/v3.10/PageMain.qml | 1 -
FileSets/v3.10/PageSettingsDisplay.qml | 1 -
FileSets/v3.10/Tile.qml | 1 -
FileSets/v3.10/TileText.qml | 1 -
FileSets/v3.10/attributes.csv | 1 -
FileSets/v3.10/dbus_generator.py | 1 -
FileSets/v3.10/styles.css | 1 -
FileSets/v3.11/Battery.qml | 1 -
FileSets/v3.11/MbEditBox.qml | 1 -
FileSets/v3.11/MbEditBoxDateTime.qml | 1 -
FileSets/v3.11/MbItem.qml | 1 -
FileSets/v3.11/MbItemDigitalInput.qml | 1 -
FileSets/v3.11/MbSpinBox.qml | 1 -
FileSets/v3.11/MbStyle.qml | 1 -
FileSets/v3.11/MbSubMenu.qml | 1 -
FileSets/v3.11/Multi.qml | 1 -
FileSets/v3.11/OverviewBox.qml | 1 -
FileSets/v3.11/OverviewConnection.qml | 1 -
FileSets/v3.11/OverviewConnectionEnd.qml | 1 -
FileSets/v3.11/OverviewSolarCharger.qml | 1 -
FileSets/v3.11/OverviewSolarInverter.qml | 1 -
FileSets/v3.11/OverviewTankDelegate.qml | 1 -
FileSets/v3.11/OverviewTanks.qml | 1 -
FileSets/v3.11/PageDigitalInput.qml | 1 -
FileSets/v3.11/PageMain.qml | 1 -
FileSets/v3.11/PageSettingsDisplay.qml | 1 -
FileSets/v3.11/Tile.qml | 1 -
FileSets/v3.11/TileText.qml | 1 -
FileSets/v3.11/attributes.csv | 1 -
FileSets/v3.11/dbus_generator.py | 1 -
FileSets/v3.11/dbus_systemcalc.py | 1 -
FileSets/v3.11/main.qml | 1 -
FileSets/v3.11/styles.css | 1 -
FileSets/v3.12/Battery.qml | 1 -
FileSets/v3.12/MbEditBox.qml | 1 -
FileSets/v3.12/MbEditBoxDateTime.qml | 1 -
FileSets/v3.12/MbItem.qml | 1 -
FileSets/v3.12/MbItemDigitalInput.qml | 1 -
FileSets/v3.12/MbSpinBox.qml | 1 -
FileSets/v3.12/MbStyle.qml | 1 -
FileSets/v3.12/MbSubMenu.qml | 1 -
FileSets/v3.12/Multi.qml | 1 -
FileSets/v3.12/OverviewBox.qml | 1 -
FileSets/v3.12/OverviewConnection.qml | 1 -
FileSets/v3.12/OverviewConnectionEnd.qml | 1 -
FileSets/v3.12/OverviewSolarCharger.qml | 1 -
FileSets/v3.12/OverviewSolarInverter.qml | 1 -
FileSets/v3.12/OverviewTankDelegate.qml | 1 -
FileSets/v3.12/OverviewTanks.qml | 1 -
FileSets/v3.12/PageDigitalInput.qml | 1 -
FileSets/v3.12/PageMain.qml | 1 -
FileSets/v3.12/PageSettingsDisplay.qml | 1 -
FileSets/v3.12/Tile.qml | 1 -
FileSets/v3.12/TileText.qml | 1 -
FileSets/v3.12/attributes.csv | 1 -
FileSets/v3.12/dbus_generator.py | 1 -
FileSets/v3.12/main.qml | 1 -
FileSets/v3.12/styles.css | 1 -
FileSets/v3.13/Battery.qml | 1 -
FileSets/v3.13/MbEditBox.qml | 1 -
FileSets/v3.13/MbEditBoxDateTime.qml | 1 -
FileSets/v3.13/MbItem.qml | 1 -
FileSets/v3.13/MbItemDigitalInput.qml | 1 -
FileSets/v3.13/MbSpinBox.qml | 1 -
FileSets/v3.13/MbStyle.qml | 1 -
FileSets/v3.13/MbSubMenu.qml | 1 -
FileSets/v3.13/Multi.qml | 1 -
FileSets/v3.13/OverviewBox.qml | 1 -
FileSets/v3.13/OverviewConnection.qml | 1 -
FileSets/v3.13/OverviewConnectionEnd.qml | 1 -
FileSets/v3.13/OverviewSolarCharger.qml | 1 -
FileSets/v3.13/OverviewSolarInverter.qml | 1 -
FileSets/v3.13/OverviewTankDelegate.qml | 1 -
FileSets/v3.13/OverviewTanks.qml | 1 -
FileSets/v3.13/PageDigitalInput.qml | 1 -
FileSets/v3.13/PageMain.qml | 1 -
FileSets/v3.13/PageSettingsDisplay.qml | 1 -
FileSets/v3.13/Tile.qml | 1 -
FileSets/v3.13/TileText.qml | 1 -
FileSets/v3.13/attributes.csv | 1 -
FileSets/v3.13/dbus_generator.py | 1 -
FileSets/v3.13/dbus_systemcalc.py | 1 -
FileSets/v3.13/main.qml | 1 -
FileSets/v3.13/styles.css | 1 -
FileSets/v3.14/Battery.qml | 1 -
FileSets/v3.14/MbEditBox.qml | 1 -
FileSets/v3.14/MbEditBoxDateTime.qml | 1 -
FileSets/v3.14/MbItem.qml | 1 -
FileSets/v3.14/MbItemDigitalInput.qml | 1 -
FileSets/v3.14/MbSpinBox.qml | 1 -
FileSets/v3.14/MbStyle.qml | 1 -
FileSets/v3.14/MbSubMenu.qml | 1 -
FileSets/v3.14/Multi.qml | 1 -
FileSets/v3.14/OverviewBox.qml | 1 -
FileSets/v3.14/OverviewConnection.qml | 1 -
FileSets/v3.14/OverviewConnectionEnd.qml | 1 -
FileSets/v3.14/OverviewSolarCharger.qml | 1 -
FileSets/v3.14/OverviewSolarInverter.qml | 1 -
FileSets/v3.14/OverviewTankDelegate.qml | 1 -
FileSets/v3.14/OverviewTanks.qml | 1 -
FileSets/v3.14/PageDigitalInput.qml | 1 -
FileSets/v3.14/Tile.qml | 1 -
FileSets/v3.14/TileText.qml | 1 -
FileSets/v3.14/main.qml | 1 -
FileSets/v3.14/styles.css | 1 -
FileSets/v3.20/Battery.qml | 1 -
FileSets/v3.20/MbEditBox.qml | 1 -
FileSets/v3.20/MbEditBoxDateTime.qml | 1 -
FileSets/v3.20/MbItem.qml | 1 -
FileSets/v3.20/MbItemDigitalInput.qml | 1 -
FileSets/v3.20/MbSpinBox.qml | 1 -
FileSets/v3.20/MbStyle.qml | 1 -
FileSets/v3.20/MbSubMenu.qml | 1 -
FileSets/v3.20/Multi.qml | 1 -
FileSets/v3.20/OverviewBox.qml | 1 -
FileSets/v3.20/OverviewConnection.qml | 1 -
FileSets/v3.20/OverviewConnectionEnd.qml | 1 -
FileSets/v3.20/OverviewSolarCharger.qml | 1 -
FileSets/v3.20/OverviewSolarInverter.qml | 1 -
FileSets/v3.20/OverviewTankDelegate.qml | 1 -
FileSets/v3.20/OverviewTanks.qml | 1 -
FileSets/v3.20/PageDigitalInput.qml | 1 -
FileSets/v3.20/PageMain.qml | 1 -
FileSets/v3.20/PageSettingsDisplay.qml | 1 -
FileSets/v3.20/Tile.qml | 1 -
FileSets/v3.20/TileText.qml | 1 -
FileSets/v3.20/attributes.csv | 1 -
FileSets/v3.20/dbus_generator.py | 1 -
FileSets/v3.20/dbus_systemcalc.py | 1 -
FileSets/v3.20/main.qml | 1 -
FileSets/v3.20/styles.css | 1 -
FileSets/v3.21/Battery.qml | 1 -
FileSets/v3.21/MbEditBox.qml | 1 -
FileSets/v3.21/MbEditBoxDateTime.qml | 1 -
FileSets/v3.21/MbItem.qml | 1 -
FileSets/v3.21/MbItemDigitalInput.qml | 1 -
FileSets/v3.21/MbSpinBox.qml | 1 -
FileSets/v3.21/MbStyle.qml | 1 -
FileSets/v3.21/MbSubMenu.qml | 1 -
FileSets/v3.21/Multi.qml | 1 -
FileSets/v3.21/OverviewBox.qml | 1 -
FileSets/v3.21/OverviewConnection.qml | 1 -
FileSets/v3.21/OverviewConnectionEnd.qml | 1 -
FileSets/v3.21/OverviewSolarCharger.qml | 1 -
FileSets/v3.21/OverviewSolarInverter.qml | 1 -
FileSets/v3.21/OverviewTankDelegate.qml | 1 -
FileSets/v3.21/OverviewTanks.qml | 1 -
FileSets/v3.21/PageDigitalInput.qml | 1 -
FileSets/v3.21/PageMain.qml | 1 -
FileSets/v3.21/PageSettingsDisplay.qml | 1 -
FileSets/v3.21/Tile.qml | 1 -
FileSets/v3.21/TileText.qml | 1 -
FileSets/v3.21/attributes.csv | 1 -
FileSets/v3.21/dbus_generator.py | 1 -
FileSets/v3.21/dbus_systemcalc.py | 1 -
FileSets/v3.21/main.qml | 1 -
FileSets/v3.21/styles.css | 1 -
FileSets/v3.22/Battery.qml | 1 -
FileSets/v3.22/MbEditBox.qml | 1 -
FileSets/v3.22/MbEditBoxDateTime.qml | 1 -
FileSets/v3.22/MbItem.qml | 1 -
FileSets/v3.22/MbItemDigitalInput.qml | 1 -
FileSets/v3.22/MbSpinBox.qml | 1 -
FileSets/v3.22/MbStyle.qml | 1 -
FileSets/v3.22/MbSubMenu.qml | 1 -
FileSets/v3.22/Multi.qml | 1 -
FileSets/v3.22/OverviewBox.qml | 1 -
FileSets/v3.22/OverviewConnection.qml | 1 -
FileSets/v3.22/OverviewConnectionEnd.qml | 1 -
FileSets/v3.22/OverviewSolarCharger.qml | 1 -
FileSets/v3.22/OverviewSolarInverter.qml | 1 -
FileSets/v3.22/OverviewTankDelegate.qml | 1 -
FileSets/v3.22/OverviewTanks.qml | 1 -
FileSets/v3.22/PageDigitalInput.qml | 1 -
FileSets/v3.22/Tile.qml | 1 -
FileSets/v3.22/TileText.qml | 1 -
FileSets/v3.22/dbus_generator.py | 1 -
FileSets/v3.22/styles.css | 1 -
FileSets/v3.30/Battery.qml | 1 -
FileSets/v3.30/MbEditBox.qml | 1 -
FileSets/v3.30/MbEditBoxDateTime.qml | 1 -
FileSets/v3.30/MbItem.qml | 1 -
FileSets/v3.30/MbItemDigitalInput.qml | 1 -
FileSets/v3.30/MbSpinBox.qml | 1 -
FileSets/v3.30/MbStyle.qml | 1 -
FileSets/v3.30/MbSubMenu.qml | 1 -
FileSets/v3.30/Multi.qml | 1 -
FileSets/v3.30/OverviewBox.qml | 1 -
FileSets/v3.30/OverviewConnection.qml | 1 -
FileSets/v3.30/OverviewConnectionEnd.qml | 1 -
FileSets/v3.30/OverviewSolarCharger.qml | 1 -
FileSets/v3.30/OverviewSolarInverter.qml | 1 -
FileSets/v3.30/OverviewTankDelegate.qml | 1 -
FileSets/v3.30/OverviewTanks.qml | 1 -
FileSets/v3.30/PageDigitalInput.qml | 1 -
FileSets/v3.30/PageMain.qml | 1 -
FileSets/v3.30/PageSettingsDisplay.qml | 1 -
FileSets/v3.30/Tile.qml | 1 -
FileSets/v3.30/TileText.qml | 1 -
FileSets/v3.30/attributes.csv | 1 -
FileSets/v3.30/dbus_generator.py | 1 -
FileSets/v3.30/dbus_systemcalc.py | 1 -
FileSets/v3.30/main.qml | 1 -
FileSets/v3.30/styles.css | 1 -
FileSets/v3.31/Battery.qml | 1 -
FileSets/v3.31/MbEditBox.qml | 1 -
FileSets/v3.31/MbEditBoxDateTime.qml | 1 -
FileSets/v3.31/MbItem.qml | 1 -
FileSets/v3.31/MbItemDigitalInput.qml | 1 -
FileSets/v3.31/MbSpinBox.qml | 1 -
FileSets/v3.31/MbStyle.qml | 1 -
FileSets/v3.31/MbSubMenu.qml | 1 -
FileSets/v3.31/Multi.qml | 1 -
FileSets/v3.31/OverviewBox.qml | 1 -
FileSets/v3.31/OverviewConnection.qml | 1 -
FileSets/v3.31/OverviewConnectionEnd.qml | 1 -
FileSets/v3.31/OverviewSolarCharger.qml | 1 -
FileSets/v3.31/OverviewSolarInverter.qml | 1 -
FileSets/v3.31/OverviewTankDelegate.qml | 1 -
FileSets/v3.31/OverviewTanks.qml | 1 -
FileSets/v3.31/PageDigitalInput.qml | 1 -
FileSets/v3.31/PageMain.qml | 1 -
FileSets/v3.31/PageSettingsDisplay.qml | 1 -
FileSets/v3.31/Tile.qml | 1 -
FileSets/v3.31/TileText.qml | 1 -
FileSets/v3.31/attributes.csv | 1 -
FileSets/v3.31/dbus_generator.py | 1 -
FileSets/v3.31/dbus_systemcalc.py | 1 -
FileSets/v3.31/main.qml | 1 -
FileSets/v3.31/styles.css | 1 -
FileSets/v3.33/Battery.qml | 1 -
FileSets/v3.33/MbEditBox.qml | 1 -
FileSets/v3.33/MbEditBoxDateTime.qml | 1 -
FileSets/v3.33/MbItem.qml | 1 -
FileSets/v3.33/MbItemDigitalInput.qml | 1 -
FileSets/v3.33/MbSpinBox.qml | 1 -
FileSets/v3.33/MbStyle.qml | 1 -
FileSets/v3.33/MbSubMenu.qml | 1 -
FileSets/v3.33/Multi.qml | 1 -
FileSets/v3.33/OverviewBox.qml | 1 -
FileSets/v3.33/OverviewConnection.qml | 1 -
FileSets/v3.33/OverviewConnectionEnd.qml | 1 -
FileSets/v3.33/OverviewSolarCharger.qml | 1 -
FileSets/v3.33/OverviewSolarInverter.qml | 1 -
FileSets/v3.33/OverviewTankDelegate.qml | 1 -
FileSets/v3.33/OverviewTanks.qml | 1 -
FileSets/v3.33/PageDigitalInput.qml | 1 -
FileSets/v3.33/PageMain.qml | 1 -
FileSets/v3.33/PageSettingsDisplay.qml | 1 -
FileSets/v3.33/Tile.qml | 1 -
FileSets/v3.33/TileText.qml | 1 -
FileSets/v3.33/attributes.csv | 1 -
FileSets/v3.33/dbus_generator.py | 1 -
FileSets/v3.33/dbus_systemcalc.py | 1 -
FileSets/v3.33/main.qml | 1 -
FileSets/v3.33/styles.css | 1 -
FileSets/v3.34/Battery.qml | 1 -
FileSets/v3.34/MbEditBox.qml | 1 -
FileSets/v3.34/MbEditBoxDateTime.qml | 1 -
FileSets/v3.34/MbItem.qml | 1 -
FileSets/v3.34/MbItemDigitalInput.qml | 1 -
FileSets/v3.34/MbSpinBox.qml | 1 -
FileSets/v3.34/MbStyle.qml | 1 -
FileSets/v3.34/MbSubMenu.qml | 1 -
FileSets/v3.34/Multi.qml | 1 -
FileSets/v3.34/OverviewBox.qml | 1 -
FileSets/v3.34/OverviewConnection.qml | 1 -
FileSets/v3.34/OverviewConnectionEnd.qml | 1 -
FileSets/v3.34/OverviewSolarCharger.qml | 1 -
FileSets/v3.34/OverviewSolarInverter.qml | 1 -
FileSets/v3.34/OverviewTankDelegate.qml | 1 -
FileSets/v3.34/OverviewTanks.qml | 1 -
FileSets/v3.34/PageDigitalInput.qml | 1 -
FileSets/v3.34/PageMain.qml | 1 -
FileSets/v3.34/Tile.qml | 1 -
FileSets/v3.34/TileText.qml | 1 -
FileSets/v3.34/dbus_generator.py | 1 -
FileSets/v3.34/dbus_systemcalc.py | 1 -
FileSets/v3.34/main.qml | 1 -
FileSets/v3.34/styles.css | 1 -
FileSets/v3.50/TileDigIn.qml | 2 +-
FileSets/v3.51/TileDigIn.qml | 2 +-
FileSets/v3.52/TileDigIn.qml | 2 +-
FileSets/v3.53/TileDigIn.qml | 2 +-
FileSets/v3.54/COMPLETE | 0
FileSets/v3.54/DetailAcInput.qml | 1 +
FileSets/v3.54/DetailInverter.qml | 1 +
FileSets/v3.54/DetailLoadsCombined.qml | 1 +
FileSets/v3.54/DetailLoadsOnInput.qml | 1 +
FileSets/v3.54/DetailLoadsOnOutput.qml | 1 +
FileSets/v3.54/LINKS_ONLY | 0
FileSets/v3.54/OverviewAcValuesEnhanced.qml | 1 +
FileSets/v3.54/OverviewFlowComplex.qml | 1 +
FileSets/v3.54/OverviewGeneratorEnhanced.qml | 1 +
.../v3.54/OverviewGeneratorRelayEnhanced.qml | 1 +
FileSets/v3.54/OverviewHubEnhanced.qml | 1 +
FileSets/v3.54/OverviewMobileEnhanced.qml | 1 +
.../v3.54/OverviewTanksTempsDigInputs.qml | 1 +
FileSets/v3.54/PageGenerator.qml.USE_ORIGINAL | 0
FileSets/v3.54/PageSettingsGuiMods.qml | 1 +
FileSets/v3.54/PowerGauge.qml | 1 +
FileSets/v3.54/TileDigIn.qml | 1 +
FileSets/v3.54/TileRelay.qml | 1 +
FileSets/v3.54~1/COMPLETE | 0
FileSets/v3.54~1/DetailAcInput.qml | 1 +
FileSets/v3.54~1/DetailInverter.qml | 1 +
FileSets/v3.54~1/DetailLoadsCombined.qml | 1 +
FileSets/v3.54~1/DetailLoadsOnInput.qml | 1 +
FileSets/v3.54~1/DetailLoadsOnOutput.qml | 1 +
FileSets/v3.54~1/LINKS_ONLY | 0
FileSets/v3.54~1/OverviewAcValuesEnhanced.qml | 1 +
FileSets/v3.54~1/OverviewFlowComplex.qml | 1 +
.../v3.54~1/OverviewGeneratorEnhanced.qml | 1 +
.../OverviewGeneratorRelayEnhanced.qml | 1 +
FileSets/v3.54~1/OverviewHubEnhanced.qml | 1 +
FileSets/v3.54~1/OverviewMobileEnhanced.qml | 1 +
.../v3.54~1/OverviewTanksTempsDigInputs.qml | 1 +
.../v3.54~1/PageGenerator.qml.USE_ORIGINAL | 0
FileSets/v3.54~1/PageSettingsGuiMods.qml | 1 +
FileSets/v3.54~1/PowerGauge.qml | 1 +
FileSets/v3.54~1/TileDigIn.qml | 1 +
FileSets/v3.54~1/TileRelay.qml | 1 +
FileSets/v3.60~15/COMPLETE | 0
FileSets/v3.60~15/DetailAcInput.qml | 1 +
FileSets/v3.60~15/DetailInverter.qml | 1 +
FileSets/v3.60~15/DetailLoadsCombined.qml | 1 +
FileSets/v3.60~15/DetailLoadsOnInput.qml | 1 +
FileSets/v3.60~15/DetailLoadsOnOutput.qml | 1 +
FileSets/v3.60~15/LINKS_ONLY | 0
.../v3.60~15/OverviewAcValuesEnhanced.qml | 1 +
FileSets/v3.60~15/OverviewFlowComplex.qml | 1 +
.../v3.60~15/OverviewGeneratorEnhanced.qml | 1 +
.../OverviewGeneratorRelayEnhanced.qml | 1 +
FileSets/v3.60~15/OverviewHubEnhanced.qml | 1 +
FileSets/v3.60~15/OverviewMobileEnhanced.qml | 1 +
.../v3.60~15/OverviewTanksTempsDigInputs.qml | 1 +
.../v3.60~15/PageGenerator.qml.USE_ORIGINAL | 0
FileSets/v3.60~15/PageSettingsGuiMods.qml | 1 +
FileSets/v3.60~15/PowerGauge.qml | 1 +
FileSets/v3.60~15/TileDigIn.qml | 1 +
FileSets/v3.60~15/TileRelay.qml | 1 +
FileSets/v3.60~16/COMPLETE | 0
FileSets/v3.60~16/DetailAcInput.qml | 1 +
FileSets/v3.60~16/DetailInverter.qml | 1 +
FileSets/v3.60~16/DetailLoadsCombined.qml | 1 +
FileSets/v3.60~16/DetailLoadsOnInput.qml | 1 +
FileSets/v3.60~16/DetailLoadsOnOutput.qml | 1 +
FileSets/v3.60~16/LINKS_ONLY | 0
.../v3.60~16/OverviewAcValuesEnhanced.qml | 1 +
FileSets/v3.60~16/OverviewFlowComplex.qml | 1 +
.../v3.60~16/OverviewGeneratorEnhanced.qml | 1 +
.../OverviewGeneratorRelayEnhanced.qml | 1 +
FileSets/v3.60~16/OverviewHubEnhanced.qml | 1 +
FileSets/v3.60~16/OverviewMobileEnhanced.qml | 1 +
.../v3.60~16/OverviewTanksTempsDigInputs.qml | 1 +
.../v3.60~16/PageGenerator.qml.USE_ORIGINAL | 0
FileSets/v3.60~16/PageSettingsGuiMods.qml | 1 +
FileSets/v3.60~16/PowerGauge.qml | 1 +
FileSets/v3.60~16/TileDigIn.qml | 1 +
FileSets/v3.60~16/TileRelay.qml | 1 +
FileSets/v3.60~17/TileDigIn.qml | 134 +-
FileSets/v3.60~21/COMPLETE | 0
FileSets/v3.60~21/DetailAcInput.qml | 1 +
FileSets/v3.60~21/DetailInverter.qml | 1 +
FileSets/v3.60~21/DetailLoadsCombined.qml | 1 +
FileSets/v3.60~21/DetailLoadsOnInput.qml | 1 +
FileSets/v3.60~21/DetailLoadsOnOutput.qml | 1 +
FileSets/v3.60~21/LINKS_ONLY | 0
.../v3.60~21/OverviewAcValuesEnhanced.qml | 1 +
FileSets/v3.60~21/OverviewFlowComplex.qml | 1 +
.../v3.60~21/OverviewGeneratorEnhanced.qml | 1 +
.../OverviewGeneratorRelayEnhanced.qml | 1 +
FileSets/v3.60~21/OverviewHubEnhanced.qml | 1 +
FileSets/v3.60~21/OverviewMobileEnhanced.qml | 1 +
.../v3.60~21/OverviewTanksTempsDigInputs.qml | 1 +
.../v3.60~21/PageGenerator.qml.USE_ORIGINAL | 0
FileSets/v3.60~21/PageGenerator.qml.orig | 96 +
FileSets/v3.60~21/PageSettingsGuiMods.qml | 1 +
FileSets/v3.60~21/PowerGauge.qml | 1 +
FileSets/v3.60~21/TileDigIn.qml | 1 +
FileSets/v3.60~21/TileRelay.orig | 170 ++
FileSets/v3.60~21/TileRelay.qml | 1 +
FileSets/v3.60~25/COMPLETE | 0
FileSets/v3.60~25/DetailAcInput.qml | 606 +++++++
FileSets/v3.60~25/DetailAcInput.qml.orig | 170 ++
FileSets/v3.60~25/DetailInverter.qml | 650 +++++++
FileSets/v3.60~25/DetailInverter.qml.orig | 170 ++
FileSets/v3.60~25/DetailLoadsCombined.qml | 146 ++
.../v3.60~25/DetailLoadsCombined.qml.orig | 170 ++
FileSets/v3.60~25/DetailLoadsOnInput.qml | 146 ++
FileSets/v3.60~25/DetailLoadsOnInput.qml.orig | 170 ++
FileSets/v3.60~25/DetailLoadsOnOutput.qml | 151 ++
.../v3.60~25/DetailLoadsOnOutput.qml.orig | 170 ++
.../v3.60~25/OverviewAcValuesEnhanced.qml | 94 +
.../OverviewAcValuesEnhanced.qml.orig | 42 +
FileSets/v3.60~25/OverviewFlowComplex.qml | 1549 ++++++++++++++++
.../v3.60~25/OverviewFlowComplex.qml.orig | 484 +++++
.../v3.60~25/OverviewGeneratorEnhanced.qml | 549 ++++++
.../OverviewGeneratorEnhanced.qml.orig | 96 +
.../OverviewGeneratorRelayEnhanced.qml | 8 +
.../OverviewGeneratorRelayEnhanced.qml.orig | 8 +
FileSets/v3.60~25/OverviewHubEnhanced.qml | 1504 ++++++++++++++++
.../v3.60~25/OverviewHubEnhanced.qml.orig | 311 ++++
FileSets/v3.60~25/OverviewMobileEnhanced.qml | 989 +++++++++++
.../v3.60~25/OverviewMobileEnhanced.qml.orig | 642 +++++++
.../v3.60~25/OverviewTanksTempsDigInputs.qml | 207 +++
.../OverviewTanksTempsDigInputs.qml.orig | 642 +++++++
.../v3.60~25/PageGenerator.qml.USE_ORIGINAL | 0
FileSets/v3.60~25/PageGenerator.qml.orig | 96 +
FileSets/v3.60~25/PageSettingsGuiMods.qml | 290 +++
.../v3.60~25/PageSettingsGuiMods.qml.orig | 373 ++++
FileSets/v3.60~25/PowerGauge.qml | 320 ++++
FileSets/v3.60~25/PowerGauge.qml.orig | 170 ++
FileSets/v3.60~25/TileDigIn.qml | 133 ++
.../{v3.60~17 => v3.60~25}/TileDigIn.qml.orig | 0
FileSets/v3.60~25/TileRelay.qml | 498 ++++++
FileSets/v3.60~25/TileRelay.qml.orig | 23 +
changes | 4 +
firstCompatibleVersion | 2 +-
version | 2 +-
482 files changed, 16113 insertions(+), 712 deletions(-)
create mode 100644 FileSets/PatchSource/PageMain.qml-v3.60~21
create mode 100644 FileSets/PatchSource/PageMain.qml-v3.60~21.orig
create mode 100644 FileSets/PatchSource/PageMain.qml-v3.60~21.patch
create mode 100644 FileSets/PatchSource/startstop.py-v3.60~25
create mode 100644 FileSets/PatchSource/startstop.py-v3.60~25.orig
create mode 100644 FileSets/PatchSource/startstop.py-v3.60~25.patch
delete mode 120000 FileSets/v3.10/Battery.qml
delete mode 120000 FileSets/v3.10/MbEditBox.qml
delete mode 120000 FileSets/v3.10/MbEditBoxDateTime.qml
delete mode 120000 FileSets/v3.10/MbItem.qml
delete mode 120000 FileSets/v3.10/MbItemDigitalInput.qml
delete mode 120000 FileSets/v3.10/MbSpinBox.qml
delete mode 120000 FileSets/v3.10/MbStyle.qml
delete mode 120000 FileSets/v3.10/MbSubMenu.qml
delete mode 120000 FileSets/v3.10/Multi.qml
delete mode 120000 FileSets/v3.10/OverviewBox.qml
delete mode 120000 FileSets/v3.10/OverviewConnection.qml
delete mode 120000 FileSets/v3.10/OverviewConnectionEnd.qml
delete mode 120000 FileSets/v3.10/OverviewSolarCharger.qml
delete mode 120000 FileSets/v3.10/OverviewSolarInverter.qml
delete mode 120000 FileSets/v3.10/OverviewTankDelegate.qml
delete mode 120000 FileSets/v3.10/OverviewTanks.qml
delete mode 120000 FileSets/v3.10/PageDigitalInput.qml
delete mode 120000 FileSets/v3.10/PageMain.qml
delete mode 120000 FileSets/v3.10/PageSettingsDisplay.qml
delete mode 120000 FileSets/v3.10/Tile.qml
delete mode 120000 FileSets/v3.10/TileText.qml
delete mode 120000 FileSets/v3.10/attributes.csv
delete mode 120000 FileSets/v3.10/dbus_generator.py
delete mode 120000 FileSets/v3.10/styles.css
delete mode 120000 FileSets/v3.11/Battery.qml
delete mode 120000 FileSets/v3.11/MbEditBox.qml
delete mode 120000 FileSets/v3.11/MbEditBoxDateTime.qml
delete mode 120000 FileSets/v3.11/MbItem.qml
delete mode 120000 FileSets/v3.11/MbItemDigitalInput.qml
delete mode 120000 FileSets/v3.11/MbSpinBox.qml
delete mode 120000 FileSets/v3.11/MbStyle.qml
delete mode 120000 FileSets/v3.11/MbSubMenu.qml
delete mode 120000 FileSets/v3.11/Multi.qml
delete mode 120000 FileSets/v3.11/OverviewBox.qml
delete mode 120000 FileSets/v3.11/OverviewConnection.qml
delete mode 120000 FileSets/v3.11/OverviewConnectionEnd.qml
delete mode 120000 FileSets/v3.11/OverviewSolarCharger.qml
delete mode 120000 FileSets/v3.11/OverviewSolarInverter.qml
delete mode 120000 FileSets/v3.11/OverviewTankDelegate.qml
delete mode 120000 FileSets/v3.11/OverviewTanks.qml
delete mode 120000 FileSets/v3.11/PageDigitalInput.qml
delete mode 120000 FileSets/v3.11/PageMain.qml
delete mode 120000 FileSets/v3.11/PageSettingsDisplay.qml
delete mode 120000 FileSets/v3.11/Tile.qml
delete mode 120000 FileSets/v3.11/TileText.qml
delete mode 120000 FileSets/v3.11/attributes.csv
delete mode 120000 FileSets/v3.11/dbus_generator.py
delete mode 120000 FileSets/v3.11/dbus_systemcalc.py
delete mode 120000 FileSets/v3.11/main.qml
delete mode 120000 FileSets/v3.11/styles.css
delete mode 120000 FileSets/v3.12/Battery.qml
delete mode 120000 FileSets/v3.12/MbEditBox.qml
delete mode 120000 FileSets/v3.12/MbEditBoxDateTime.qml
delete mode 120000 FileSets/v3.12/MbItem.qml
delete mode 120000 FileSets/v3.12/MbItemDigitalInput.qml
delete mode 120000 FileSets/v3.12/MbSpinBox.qml
delete mode 120000 FileSets/v3.12/MbStyle.qml
delete mode 120000 FileSets/v3.12/MbSubMenu.qml
delete mode 120000 FileSets/v3.12/Multi.qml
delete mode 120000 FileSets/v3.12/OverviewBox.qml
delete mode 120000 FileSets/v3.12/OverviewConnection.qml
delete mode 120000 FileSets/v3.12/OverviewConnectionEnd.qml
delete mode 120000 FileSets/v3.12/OverviewSolarCharger.qml
delete mode 120000 FileSets/v3.12/OverviewSolarInverter.qml
delete mode 120000 FileSets/v3.12/OverviewTankDelegate.qml
delete mode 120000 FileSets/v3.12/OverviewTanks.qml
delete mode 120000 FileSets/v3.12/PageDigitalInput.qml
delete mode 120000 FileSets/v3.12/PageMain.qml
delete mode 120000 FileSets/v3.12/PageSettingsDisplay.qml
delete mode 120000 FileSets/v3.12/Tile.qml
delete mode 120000 FileSets/v3.12/TileText.qml
delete mode 120000 FileSets/v3.12/attributes.csv
delete mode 120000 FileSets/v3.12/dbus_generator.py
delete mode 120000 FileSets/v3.12/main.qml
delete mode 120000 FileSets/v3.12/styles.css
delete mode 120000 FileSets/v3.13/Battery.qml
delete mode 120000 FileSets/v3.13/MbEditBox.qml
delete mode 120000 FileSets/v3.13/MbEditBoxDateTime.qml
delete mode 120000 FileSets/v3.13/MbItem.qml
delete mode 120000 FileSets/v3.13/MbItemDigitalInput.qml
delete mode 120000 FileSets/v3.13/MbSpinBox.qml
delete mode 120000 FileSets/v3.13/MbStyle.qml
delete mode 120000 FileSets/v3.13/MbSubMenu.qml
delete mode 120000 FileSets/v3.13/Multi.qml
delete mode 120000 FileSets/v3.13/OverviewBox.qml
delete mode 120000 FileSets/v3.13/OverviewConnection.qml
delete mode 120000 FileSets/v3.13/OverviewConnectionEnd.qml
delete mode 120000 FileSets/v3.13/OverviewSolarCharger.qml
delete mode 120000 FileSets/v3.13/OverviewSolarInverter.qml
delete mode 120000 FileSets/v3.13/OverviewTankDelegate.qml
delete mode 120000 FileSets/v3.13/OverviewTanks.qml
delete mode 120000 FileSets/v3.13/PageDigitalInput.qml
delete mode 120000 FileSets/v3.13/PageMain.qml
delete mode 120000 FileSets/v3.13/PageSettingsDisplay.qml
delete mode 120000 FileSets/v3.13/Tile.qml
delete mode 120000 FileSets/v3.13/TileText.qml
delete mode 120000 FileSets/v3.13/attributes.csv
delete mode 120000 FileSets/v3.13/dbus_generator.py
delete mode 120000 FileSets/v3.13/dbus_systemcalc.py
delete mode 120000 FileSets/v3.13/main.qml
delete mode 120000 FileSets/v3.13/styles.css
delete mode 120000 FileSets/v3.14/Battery.qml
delete mode 120000 FileSets/v3.14/MbEditBox.qml
delete mode 120000 FileSets/v3.14/MbEditBoxDateTime.qml
delete mode 120000 FileSets/v3.14/MbItem.qml
delete mode 120000 FileSets/v3.14/MbItemDigitalInput.qml
delete mode 120000 FileSets/v3.14/MbSpinBox.qml
delete mode 120000 FileSets/v3.14/MbStyle.qml
delete mode 120000 FileSets/v3.14/MbSubMenu.qml
delete mode 120000 FileSets/v3.14/Multi.qml
delete mode 120000 FileSets/v3.14/OverviewBox.qml
delete mode 120000 FileSets/v3.14/OverviewConnection.qml
delete mode 120000 FileSets/v3.14/OverviewConnectionEnd.qml
delete mode 120000 FileSets/v3.14/OverviewSolarCharger.qml
delete mode 120000 FileSets/v3.14/OverviewSolarInverter.qml
delete mode 120000 FileSets/v3.14/OverviewTankDelegate.qml
delete mode 120000 FileSets/v3.14/OverviewTanks.qml
delete mode 120000 FileSets/v3.14/PageDigitalInput.qml
delete mode 120000 FileSets/v3.14/Tile.qml
delete mode 120000 FileSets/v3.14/TileText.qml
delete mode 120000 FileSets/v3.14/main.qml
delete mode 120000 FileSets/v3.14/styles.css
delete mode 120000 FileSets/v3.20/Battery.qml
delete mode 120000 FileSets/v3.20/MbEditBox.qml
delete mode 120000 FileSets/v3.20/MbEditBoxDateTime.qml
delete mode 120000 FileSets/v3.20/MbItem.qml
delete mode 120000 FileSets/v3.20/MbItemDigitalInput.qml
delete mode 120000 FileSets/v3.20/MbSpinBox.qml
delete mode 120000 FileSets/v3.20/MbStyle.qml
delete mode 120000 FileSets/v3.20/MbSubMenu.qml
delete mode 120000 FileSets/v3.20/Multi.qml
delete mode 120000 FileSets/v3.20/OverviewBox.qml
delete mode 120000 FileSets/v3.20/OverviewConnection.qml
delete mode 120000 FileSets/v3.20/OverviewConnectionEnd.qml
delete mode 120000 FileSets/v3.20/OverviewSolarCharger.qml
delete mode 120000 FileSets/v3.20/OverviewSolarInverter.qml
delete mode 120000 FileSets/v3.20/OverviewTankDelegate.qml
delete mode 120000 FileSets/v3.20/OverviewTanks.qml
delete mode 120000 FileSets/v3.20/PageDigitalInput.qml
delete mode 120000 FileSets/v3.20/PageMain.qml
delete mode 120000 FileSets/v3.20/PageSettingsDisplay.qml
delete mode 120000 FileSets/v3.20/Tile.qml
delete mode 120000 FileSets/v3.20/TileText.qml
delete mode 120000 FileSets/v3.20/attributes.csv
delete mode 120000 FileSets/v3.20/dbus_generator.py
delete mode 120000 FileSets/v3.20/dbus_systemcalc.py
delete mode 120000 FileSets/v3.20/main.qml
delete mode 120000 FileSets/v3.20/styles.css
delete mode 120000 FileSets/v3.21/Battery.qml
delete mode 120000 FileSets/v3.21/MbEditBox.qml
delete mode 120000 FileSets/v3.21/MbEditBoxDateTime.qml
delete mode 120000 FileSets/v3.21/MbItem.qml
delete mode 120000 FileSets/v3.21/MbItemDigitalInput.qml
delete mode 120000 FileSets/v3.21/MbSpinBox.qml
delete mode 120000 FileSets/v3.21/MbStyle.qml
delete mode 120000 FileSets/v3.21/MbSubMenu.qml
delete mode 120000 FileSets/v3.21/Multi.qml
delete mode 120000 FileSets/v3.21/OverviewBox.qml
delete mode 120000 FileSets/v3.21/OverviewConnection.qml
delete mode 120000 FileSets/v3.21/OverviewConnectionEnd.qml
delete mode 120000 FileSets/v3.21/OverviewSolarCharger.qml
delete mode 120000 FileSets/v3.21/OverviewSolarInverter.qml
delete mode 120000 FileSets/v3.21/OverviewTankDelegate.qml
delete mode 120000 FileSets/v3.21/OverviewTanks.qml
delete mode 120000 FileSets/v3.21/PageDigitalInput.qml
delete mode 120000 FileSets/v3.21/PageMain.qml
delete mode 120000 FileSets/v3.21/PageSettingsDisplay.qml
delete mode 120000 FileSets/v3.21/Tile.qml
delete mode 120000 FileSets/v3.21/TileText.qml
delete mode 120000 FileSets/v3.21/attributes.csv
delete mode 120000 FileSets/v3.21/dbus_generator.py
delete mode 120000 FileSets/v3.21/dbus_systemcalc.py
delete mode 120000 FileSets/v3.21/main.qml
delete mode 120000 FileSets/v3.21/styles.css
delete mode 120000 FileSets/v3.22/Battery.qml
delete mode 120000 FileSets/v3.22/MbEditBox.qml
delete mode 120000 FileSets/v3.22/MbEditBoxDateTime.qml
delete mode 120000 FileSets/v3.22/MbItem.qml
delete mode 120000 FileSets/v3.22/MbItemDigitalInput.qml
delete mode 120000 FileSets/v3.22/MbSpinBox.qml
delete mode 120000 FileSets/v3.22/MbStyle.qml
delete mode 120000 FileSets/v3.22/MbSubMenu.qml
delete mode 120000 FileSets/v3.22/Multi.qml
delete mode 120000 FileSets/v3.22/OverviewBox.qml
delete mode 120000 FileSets/v3.22/OverviewConnection.qml
delete mode 120000 FileSets/v3.22/OverviewConnectionEnd.qml
delete mode 120000 FileSets/v3.22/OverviewSolarCharger.qml
delete mode 120000 FileSets/v3.22/OverviewSolarInverter.qml
delete mode 120000 FileSets/v3.22/OverviewTankDelegate.qml
delete mode 120000 FileSets/v3.22/OverviewTanks.qml
delete mode 120000 FileSets/v3.22/PageDigitalInput.qml
delete mode 120000 FileSets/v3.22/Tile.qml
delete mode 120000 FileSets/v3.22/TileText.qml
delete mode 120000 FileSets/v3.22/dbus_generator.py
delete mode 120000 FileSets/v3.22/styles.css
delete mode 120000 FileSets/v3.30/Battery.qml
delete mode 120000 FileSets/v3.30/MbEditBox.qml
delete mode 120000 FileSets/v3.30/MbEditBoxDateTime.qml
delete mode 120000 FileSets/v3.30/MbItem.qml
delete mode 120000 FileSets/v3.30/MbItemDigitalInput.qml
delete mode 120000 FileSets/v3.30/MbSpinBox.qml
delete mode 120000 FileSets/v3.30/MbStyle.qml
delete mode 120000 FileSets/v3.30/MbSubMenu.qml
delete mode 120000 FileSets/v3.30/Multi.qml
delete mode 120000 FileSets/v3.30/OverviewBox.qml
delete mode 120000 FileSets/v3.30/OverviewConnection.qml
delete mode 120000 FileSets/v3.30/OverviewConnectionEnd.qml
delete mode 120000 FileSets/v3.30/OverviewSolarCharger.qml
delete mode 120000 FileSets/v3.30/OverviewSolarInverter.qml
delete mode 120000 FileSets/v3.30/OverviewTankDelegate.qml
delete mode 120000 FileSets/v3.30/OverviewTanks.qml
delete mode 120000 FileSets/v3.30/PageDigitalInput.qml
delete mode 120000 FileSets/v3.30/PageMain.qml
delete mode 120000 FileSets/v3.30/PageSettingsDisplay.qml
delete mode 120000 FileSets/v3.30/Tile.qml
delete mode 120000 FileSets/v3.30/TileText.qml
delete mode 120000 FileSets/v3.30/attributes.csv
delete mode 120000 FileSets/v3.30/dbus_generator.py
delete mode 120000 FileSets/v3.30/dbus_systemcalc.py
delete mode 120000 FileSets/v3.30/main.qml
delete mode 120000 FileSets/v3.30/styles.css
delete mode 120000 FileSets/v3.31/Battery.qml
delete mode 120000 FileSets/v3.31/MbEditBox.qml
delete mode 120000 FileSets/v3.31/MbEditBoxDateTime.qml
delete mode 120000 FileSets/v3.31/MbItem.qml
delete mode 120000 FileSets/v3.31/MbItemDigitalInput.qml
delete mode 120000 FileSets/v3.31/MbSpinBox.qml
delete mode 120000 FileSets/v3.31/MbStyle.qml
delete mode 120000 FileSets/v3.31/MbSubMenu.qml
delete mode 120000 FileSets/v3.31/Multi.qml
delete mode 120000 FileSets/v3.31/OverviewBox.qml
delete mode 120000 FileSets/v3.31/OverviewConnection.qml
delete mode 120000 FileSets/v3.31/OverviewConnectionEnd.qml
delete mode 120000 FileSets/v3.31/OverviewSolarCharger.qml
delete mode 120000 FileSets/v3.31/OverviewSolarInverter.qml
delete mode 120000 FileSets/v3.31/OverviewTankDelegate.qml
delete mode 120000 FileSets/v3.31/OverviewTanks.qml
delete mode 120000 FileSets/v3.31/PageDigitalInput.qml
delete mode 120000 FileSets/v3.31/PageMain.qml
delete mode 120000 FileSets/v3.31/PageSettingsDisplay.qml
delete mode 120000 FileSets/v3.31/Tile.qml
delete mode 120000 FileSets/v3.31/TileText.qml
delete mode 120000 FileSets/v3.31/attributes.csv
delete mode 120000 FileSets/v3.31/dbus_generator.py
delete mode 120000 FileSets/v3.31/dbus_systemcalc.py
delete mode 120000 FileSets/v3.31/main.qml
delete mode 120000 FileSets/v3.31/styles.css
delete mode 120000 FileSets/v3.33/Battery.qml
delete mode 120000 FileSets/v3.33/MbEditBox.qml
delete mode 120000 FileSets/v3.33/MbEditBoxDateTime.qml
delete mode 120000 FileSets/v3.33/MbItem.qml
delete mode 120000 FileSets/v3.33/MbItemDigitalInput.qml
delete mode 120000 FileSets/v3.33/MbSpinBox.qml
delete mode 120000 FileSets/v3.33/MbStyle.qml
delete mode 120000 FileSets/v3.33/MbSubMenu.qml
delete mode 120000 FileSets/v3.33/Multi.qml
delete mode 120000 FileSets/v3.33/OverviewBox.qml
delete mode 120000 FileSets/v3.33/OverviewConnection.qml
delete mode 120000 FileSets/v3.33/OverviewConnectionEnd.qml
delete mode 120000 FileSets/v3.33/OverviewSolarCharger.qml
delete mode 120000 FileSets/v3.33/OverviewSolarInverter.qml
delete mode 120000 FileSets/v3.33/OverviewTankDelegate.qml
delete mode 120000 FileSets/v3.33/OverviewTanks.qml
delete mode 120000 FileSets/v3.33/PageDigitalInput.qml
delete mode 120000 FileSets/v3.33/PageMain.qml
delete mode 120000 FileSets/v3.33/PageSettingsDisplay.qml
delete mode 120000 FileSets/v3.33/Tile.qml
delete mode 120000 FileSets/v3.33/TileText.qml
delete mode 120000 FileSets/v3.33/attributes.csv
delete mode 120000 FileSets/v3.33/dbus_generator.py
delete mode 120000 FileSets/v3.33/dbus_systemcalc.py
delete mode 120000 FileSets/v3.33/main.qml
delete mode 120000 FileSets/v3.33/styles.css
delete mode 120000 FileSets/v3.34/Battery.qml
delete mode 120000 FileSets/v3.34/MbEditBox.qml
delete mode 120000 FileSets/v3.34/MbEditBoxDateTime.qml
delete mode 120000 FileSets/v3.34/MbItem.qml
delete mode 120000 FileSets/v3.34/MbItemDigitalInput.qml
delete mode 120000 FileSets/v3.34/MbSpinBox.qml
delete mode 120000 FileSets/v3.34/MbStyle.qml
delete mode 120000 FileSets/v3.34/MbSubMenu.qml
delete mode 120000 FileSets/v3.34/Multi.qml
delete mode 120000 FileSets/v3.34/OverviewBox.qml
delete mode 120000 FileSets/v3.34/OverviewConnection.qml
delete mode 120000 FileSets/v3.34/OverviewConnectionEnd.qml
delete mode 120000 FileSets/v3.34/OverviewSolarCharger.qml
delete mode 120000 FileSets/v3.34/OverviewSolarInverter.qml
delete mode 120000 FileSets/v3.34/OverviewTankDelegate.qml
delete mode 120000 FileSets/v3.34/OverviewTanks.qml
delete mode 120000 FileSets/v3.34/PageDigitalInput.qml
delete mode 120000 FileSets/v3.34/PageMain.qml
delete mode 120000 FileSets/v3.34/Tile.qml
delete mode 120000 FileSets/v3.34/TileText.qml
delete mode 120000 FileSets/v3.34/dbus_generator.py
delete mode 120000 FileSets/v3.34/dbus_systemcalc.py
delete mode 120000 FileSets/v3.34/main.qml
delete mode 120000 FileSets/v3.34/styles.css
create mode 100644 FileSets/v3.54/COMPLETE
create mode 120000 FileSets/v3.54/DetailAcInput.qml
create mode 120000 FileSets/v3.54/DetailInverter.qml
create mode 120000 FileSets/v3.54/DetailLoadsCombined.qml
create mode 120000 FileSets/v3.54/DetailLoadsOnInput.qml
create mode 120000 FileSets/v3.54/DetailLoadsOnOutput.qml
create mode 100644 FileSets/v3.54/LINKS_ONLY
create mode 120000 FileSets/v3.54/OverviewAcValuesEnhanced.qml
create mode 120000 FileSets/v3.54/OverviewFlowComplex.qml
create mode 120000 FileSets/v3.54/OverviewGeneratorEnhanced.qml
create mode 120000 FileSets/v3.54/OverviewGeneratorRelayEnhanced.qml
create mode 120000 FileSets/v3.54/OverviewHubEnhanced.qml
create mode 120000 FileSets/v3.54/OverviewMobileEnhanced.qml
create mode 120000 FileSets/v3.54/OverviewTanksTempsDigInputs.qml
create mode 100644 FileSets/v3.54/PageGenerator.qml.USE_ORIGINAL
create mode 120000 FileSets/v3.54/PageSettingsGuiMods.qml
create mode 120000 FileSets/v3.54/PowerGauge.qml
create mode 120000 FileSets/v3.54/TileDigIn.qml
create mode 120000 FileSets/v3.54/TileRelay.qml
create mode 100644 FileSets/v3.54~1/COMPLETE
create mode 120000 FileSets/v3.54~1/DetailAcInput.qml
create mode 120000 FileSets/v3.54~1/DetailInverter.qml
create mode 120000 FileSets/v3.54~1/DetailLoadsCombined.qml
create mode 120000 FileSets/v3.54~1/DetailLoadsOnInput.qml
create mode 120000 FileSets/v3.54~1/DetailLoadsOnOutput.qml
create mode 100644 FileSets/v3.54~1/LINKS_ONLY
create mode 120000 FileSets/v3.54~1/OverviewAcValuesEnhanced.qml
create mode 120000 FileSets/v3.54~1/OverviewFlowComplex.qml
create mode 120000 FileSets/v3.54~1/OverviewGeneratorEnhanced.qml
create mode 120000 FileSets/v3.54~1/OverviewGeneratorRelayEnhanced.qml
create mode 120000 FileSets/v3.54~1/OverviewHubEnhanced.qml
create mode 120000 FileSets/v3.54~1/OverviewMobileEnhanced.qml
create mode 120000 FileSets/v3.54~1/OverviewTanksTempsDigInputs.qml
create mode 100644 FileSets/v3.54~1/PageGenerator.qml.USE_ORIGINAL
create mode 120000 FileSets/v3.54~1/PageSettingsGuiMods.qml
create mode 120000 FileSets/v3.54~1/PowerGauge.qml
create mode 120000 FileSets/v3.54~1/TileDigIn.qml
create mode 120000 FileSets/v3.54~1/TileRelay.qml
create mode 100644 FileSets/v3.60~15/COMPLETE
create mode 120000 FileSets/v3.60~15/DetailAcInput.qml
create mode 120000 FileSets/v3.60~15/DetailInverter.qml
create mode 120000 FileSets/v3.60~15/DetailLoadsCombined.qml
create mode 120000 FileSets/v3.60~15/DetailLoadsOnInput.qml
create mode 120000 FileSets/v3.60~15/DetailLoadsOnOutput.qml
create mode 100644 FileSets/v3.60~15/LINKS_ONLY
create mode 120000 FileSets/v3.60~15/OverviewAcValuesEnhanced.qml
create mode 120000 FileSets/v3.60~15/OverviewFlowComplex.qml
create mode 120000 FileSets/v3.60~15/OverviewGeneratorEnhanced.qml
create mode 120000 FileSets/v3.60~15/OverviewGeneratorRelayEnhanced.qml
create mode 120000 FileSets/v3.60~15/OverviewHubEnhanced.qml
create mode 120000 FileSets/v3.60~15/OverviewMobileEnhanced.qml
create mode 120000 FileSets/v3.60~15/OverviewTanksTempsDigInputs.qml
create mode 100644 FileSets/v3.60~15/PageGenerator.qml.USE_ORIGINAL
create mode 120000 FileSets/v3.60~15/PageSettingsGuiMods.qml
create mode 120000 FileSets/v3.60~15/PowerGauge.qml
create mode 120000 FileSets/v3.60~15/TileDigIn.qml
create mode 120000 FileSets/v3.60~15/TileRelay.qml
create mode 100644 FileSets/v3.60~16/COMPLETE
create mode 120000 FileSets/v3.60~16/DetailAcInput.qml
create mode 120000 FileSets/v3.60~16/DetailInverter.qml
create mode 120000 FileSets/v3.60~16/DetailLoadsCombined.qml
create mode 120000 FileSets/v3.60~16/DetailLoadsOnInput.qml
create mode 120000 FileSets/v3.60~16/DetailLoadsOnOutput.qml
create mode 100644 FileSets/v3.60~16/LINKS_ONLY
create mode 120000 FileSets/v3.60~16/OverviewAcValuesEnhanced.qml
create mode 120000 FileSets/v3.60~16/OverviewFlowComplex.qml
create mode 120000 FileSets/v3.60~16/OverviewGeneratorEnhanced.qml
create mode 120000 FileSets/v3.60~16/OverviewGeneratorRelayEnhanced.qml
create mode 120000 FileSets/v3.60~16/OverviewHubEnhanced.qml
create mode 120000 FileSets/v3.60~16/OverviewMobileEnhanced.qml
create mode 120000 FileSets/v3.60~16/OverviewTanksTempsDigInputs.qml
create mode 100644 FileSets/v3.60~16/PageGenerator.qml.USE_ORIGINAL
create mode 120000 FileSets/v3.60~16/PageSettingsGuiMods.qml
create mode 120000 FileSets/v3.60~16/PowerGauge.qml
create mode 120000 FileSets/v3.60~16/TileDigIn.qml
create mode 120000 FileSets/v3.60~16/TileRelay.qml
mode change 100644 => 120000 FileSets/v3.60~17/TileDigIn.qml
create mode 100644 FileSets/v3.60~21/COMPLETE
create mode 120000 FileSets/v3.60~21/DetailAcInput.qml
create mode 120000 FileSets/v3.60~21/DetailInverter.qml
create mode 120000 FileSets/v3.60~21/DetailLoadsCombined.qml
create mode 120000 FileSets/v3.60~21/DetailLoadsOnInput.qml
create mode 120000 FileSets/v3.60~21/DetailLoadsOnOutput.qml
create mode 100644 FileSets/v3.60~21/LINKS_ONLY
create mode 120000 FileSets/v3.60~21/OverviewAcValuesEnhanced.qml
create mode 120000 FileSets/v3.60~21/OverviewFlowComplex.qml
create mode 120000 FileSets/v3.60~21/OverviewGeneratorEnhanced.qml
create mode 120000 FileSets/v3.60~21/OverviewGeneratorRelayEnhanced.qml
create mode 120000 FileSets/v3.60~21/OverviewHubEnhanced.qml
create mode 120000 FileSets/v3.60~21/OverviewMobileEnhanced.qml
create mode 120000 FileSets/v3.60~21/OverviewTanksTempsDigInputs.qml
create mode 100644 FileSets/v3.60~21/PageGenerator.qml.USE_ORIGINAL
create mode 100644 FileSets/v3.60~21/PageGenerator.qml.orig
create mode 120000 FileSets/v3.60~21/PageSettingsGuiMods.qml
create mode 120000 FileSets/v3.60~21/PowerGauge.qml
create mode 120000 FileSets/v3.60~21/TileDigIn.qml
create mode 100644 FileSets/v3.60~21/TileRelay.orig
create mode 120000 FileSets/v3.60~21/TileRelay.qml
create mode 100644 FileSets/v3.60~25/COMPLETE
create mode 100644 FileSets/v3.60~25/DetailAcInput.qml
create mode 100644 FileSets/v3.60~25/DetailAcInput.qml.orig
create mode 100644 FileSets/v3.60~25/DetailInverter.qml
create mode 100644 FileSets/v3.60~25/DetailInverter.qml.orig
create mode 100644 FileSets/v3.60~25/DetailLoadsCombined.qml
create mode 100644 FileSets/v3.60~25/DetailLoadsCombined.qml.orig
create mode 100644 FileSets/v3.60~25/DetailLoadsOnInput.qml
create mode 100644 FileSets/v3.60~25/DetailLoadsOnInput.qml.orig
create mode 100644 FileSets/v3.60~25/DetailLoadsOnOutput.qml
create mode 100644 FileSets/v3.60~25/DetailLoadsOnOutput.qml.orig
create mode 100644 FileSets/v3.60~25/OverviewAcValuesEnhanced.qml
create mode 100644 FileSets/v3.60~25/OverviewAcValuesEnhanced.qml.orig
create mode 100644 FileSets/v3.60~25/OverviewFlowComplex.qml
create mode 100644 FileSets/v3.60~25/OverviewFlowComplex.qml.orig
create mode 100644 FileSets/v3.60~25/OverviewGeneratorEnhanced.qml
create mode 100644 FileSets/v3.60~25/OverviewGeneratorEnhanced.qml.orig
create mode 100644 FileSets/v3.60~25/OverviewGeneratorRelayEnhanced.qml
create mode 100644 FileSets/v3.60~25/OverviewGeneratorRelayEnhanced.qml.orig
create mode 100644 FileSets/v3.60~25/OverviewHubEnhanced.qml
create mode 100644 FileSets/v3.60~25/OverviewHubEnhanced.qml.orig
create mode 100644 FileSets/v3.60~25/OverviewMobileEnhanced.qml
create mode 100644 FileSets/v3.60~25/OverviewMobileEnhanced.qml.orig
create mode 100644 FileSets/v3.60~25/OverviewTanksTempsDigInputs.qml
create mode 100644 FileSets/v3.60~25/OverviewTanksTempsDigInputs.qml.orig
create mode 100644 FileSets/v3.60~25/PageGenerator.qml.USE_ORIGINAL
create mode 100644 FileSets/v3.60~25/PageGenerator.qml.orig
create mode 100644 FileSets/v3.60~25/PageSettingsGuiMods.qml
create mode 100644 FileSets/v3.60~25/PageSettingsGuiMods.qml.orig
create mode 100644 FileSets/v3.60~25/PowerGauge.qml
create mode 100644 FileSets/v3.60~25/PowerGauge.qml.orig
create mode 100644 FileSets/v3.60~25/TileDigIn.qml
rename FileSets/{v3.60~17 => v3.60~25}/TileDigIn.qml.orig (100%)
create mode 100644 FileSets/v3.60~25/TileRelay.qml
create mode 100644 FileSets/v3.60~25/TileRelay.qml.orig
diff --git a/FileSets/PatchSource/HubData.qml b/FileSets/PatchSource/HubData.qml
index 6c0413dc..5d61f363 100644
--- a/FileSets/PatchSource/HubData.qml
+++ b/FileSets/PatchSource/HubData.qml
@@ -1,11 +1,7 @@
-//////// modified for VE.Direct inverter support
-//////// modified for grid/genset meter
-//////// added alternator, AC charger, wind generator
-
import QtQuick 1.1
-import com.victron.velib 1.0
import "utils.js" as Utils
+import com.victron.velib 1.0
Item {
id: root
diff --git a/FileSets/PatchSource/HubData.qml.patch b/FileSets/PatchSource/HubData.qml.patch
index 86c5d113..6c3fb54f 100644
--- a/FileSets/PatchSource/HubData.qml.patch
+++ b/FileSets/PatchSource/HubData.qml.patch
@@ -1,14 +1,10 @@
--- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/HubData.qml.orig 2025-01-12 08:25:03
-+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/HubData.qml 2024-05-17 06:58:33
-@@ -1,14 +1,32 @@
-+//////// modified for VE.Direct inverter support
-+//////// modified for grid/genset meter
-+//////// added alternator, AC charger, wind generator
-+
++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/HubData.qml 2025-02-02 21:49:14
+@@ -1,14 +1,28 @@
import QtQuick 1.1
-+import com.victron.velib 1.0
import "utils.js" as Utils
++import com.victron.velib 1.0
Item {
id: root
@@ -33,7 +29,7 @@
property alias pvCharger: _pvCharger
property alias pvOnAcIn1: _pvOnAcIn1
property alias pvOnAcIn2: _pvOnAcIn2
-@@ -18,8 +36,8 @@
+@@ -18,8 +32,8 @@
property alias acInLoad: _acInLoad
property alias acOutLoad: _acOutLoad
property alias grid: _grid
@@ -43,7 +39,7 @@
property VBusItem systemType: VBusItem { bind: Utils.path(systemPrefix, "/SystemType") }
property bool hasGridMeter: _hasGridMeter.valid
property variant acSource: _acSource.value
-@@ -60,6 +78,30 @@
+@@ -60,6 +74,30 @@
property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Pv/Power"); unit: "W"}
}
@@ -74,7 +70,7 @@
ObjectAcConnection {
id: _pvOnAcOut
bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnOutput")
-@@ -77,8 +119,11 @@
+@@ -77,8 +115,11 @@
ObjectAcConnection {
id: _genset
@@ -87,7 +83,7 @@
}
VBusItem {
-@@ -91,20 +136,20 @@
+@@ -91,20 +132,20 @@
bind: Utils.path(systemPrefix, "/Ac/Grid/DeviceType")
}
@@ -121,7 +117,7 @@
VBusItem {
id: _l2L1OutSummed
-@@ -114,32 +159,42 @@
+@@ -114,32 +155,42 @@
ObjectAcConnection {
id: _grid
@@ -172,7 +168,7 @@
bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnInput")
}
-@@ -167,4 +222,47 @@
+@@ -167,4 +218,47 @@
id: _dcSystem
property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/System/Power"); unit: "W"}
}
diff --git a/FileSets/PatchSource/MbItemDigitalInput.qml-v3.40 b/FileSets/PatchSource/MbItemDigitalInput.qml-v3.40
index 599ed90a..412ef280 100644
--- a/FileSets/PatchSource/MbItemDigitalInput.qml-v3.40
+++ b/FileSets/PatchSource/MbItemDigitalInput.qml-v3.40
@@ -1,5 +1,3 @@
-//// modified for ExtTransferSwitch package
-
import QtQuick 1.1
MbItemOptions {
diff --git a/FileSets/PatchSource/MbItemDigitalInput.qml-v3.40.patch b/FileSets/PatchSource/MbItemDigitalInput.qml-v3.40.patch
index cbfd2dc7..6c557615 100644
--- a/FileSets/PatchSource/MbItemDigitalInput.qml-v3.40.patch
+++ b/FileSets/PatchSource/MbItemDigitalInput.qml-v3.40.patch
@@ -1,12 +1,6 @@
--- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/MbItemDigitalInput.qml-v3.40.orig 2024-07-09 10:19:45
-+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/MbItemDigitalInput.qml-v3.40 2024-05-14 07:00:12
-@@ -1,3 +1,5 @@
-+//// modified for ExtTransferSwitch package
-+
- import QtQuick 1.1
-
- MbItemOptions {
-@@ -14,8 +16,11 @@
++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/MbItemDigitalInput.qml-v3.40 2025-02-02 17:42:41
+@@ -14,8 +14,11 @@
MbOption { description: qsTr("Smoke alarm"); value: 6 },
MbOption { description: qsTr("Fire alarm"); value: 7 },
MbOption { description: qsTr("CO2 alarm"); value: 8 },
diff --git a/FileSets/PatchSource/ObjectAcConnection.qml b/FileSets/PatchSource/ObjectAcConnection.qml
index 9e87e2db..351e5b28 100644
--- a/FileSets/PatchSource/ObjectAcConnection.qml
+++ b/FileSets/PatchSource/ObjectAcConnection.qml
@@ -1,7 +1,3 @@
-////// modified to show voltage, current and frequency in flow overview
-////// modified to show bar graphs
-////// modified to use grid or genset meter if present
-
import QtQuick 1.1
import com.victron.velib 1.0
import "utils.js" as Utils
diff --git a/FileSets/PatchSource/ObjectAcConnection.qml.patch b/FileSets/PatchSource/ObjectAcConnection.qml.patch
index b78f55c1..60be74d9 100644
--- a/FileSets/PatchSource/ObjectAcConnection.qml.patch
+++ b/FileSets/PatchSource/ObjectAcConnection.qml.patch
@@ -1,13 +1,6 @@
--- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/ObjectAcConnection.qml.orig 2025-01-12 08:28:05
-+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/ObjectAcConnection.qml 2024-05-14 07:00:12
-@@ -1,19 +1,41 @@
-+////// modified to show voltage, current and frequency in flow overview
-+////// modified to show bar graphs
-+////// modified to use grid or genset meter if present
-+
- import QtQuick 1.1
- import com.victron.velib 1.0
- import "utils.js" as Utils
++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/ObjectAcConnection.qml 2025-02-02 17:43:09
+@@ -4,16 +4,34 @@
QtObject {
property string bindPrefix
diff --git a/FileSets/PatchSource/PageDigitalInput.qml b/FileSets/PatchSource/PageDigitalInput.qml
index 3a9483ca..14bf985e 100644
--- a/FileSets/PatchSource/PageDigitalInput.qml
+++ b/FileSets/PatchSource/PageDigitalInput.qml
@@ -1,5 +1,3 @@
-//// modified for ExtTransferSwitch package
-
import QtQuick 1.1
import com.victron.velib 1.0
import "utils.js" as Utils
diff --git a/FileSets/PatchSource/PageDigitalInput.qml.patch b/FileSets/PatchSource/PageDigitalInput.qml.patch
index d0a89525..150b7bc3 100644
--- a/FileSets/PatchSource/PageDigitalInput.qml.patch
+++ b/FileSets/PatchSource/PageDigitalInput.qml.patch
@@ -1,12 +1,6 @@
--- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageDigitalInput.qml.orig 2024-07-09 10:19:57
-+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageDigitalInput.qml 2024-05-14 07:00:12
-@@ -1,3 +1,5 @@
-+//// modified for ExtTransferSwitch package
-+
- import QtQuick 1.1
- import com.victron.velib 1.0
- import "utils.js" as Utils
-@@ -18,6 +20,20 @@
++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageDigitalInput.qml 2025-02-02 17:43:22
+@@ -18,6 +18,20 @@
bind: service.path("/DeviceInstance")
}
@@ -27,7 +21,7 @@
// Handle translations
function getType(type){
switch (type) {
-@@ -41,6 +57,9 @@
+@@ -41,6 +55,9 @@
return qsTr("CO2 alarm")
case "Generator":
return qsTr("Generator")
@@ -37,7 +31,7 @@
}
return type;
}
-@@ -72,6 +91,11 @@
+@@ -72,6 +89,11 @@
return qsTr("Running")
case 11:
return qsTr("Stopped")
@@ -49,7 +43,7 @@
}
return qsTr("Unknown")
-@@ -105,5 +129,19 @@
+@@ -105,5 +127,19 @@
}
}
}
diff --git a/FileSets/PatchSource/PageMain.qml-v3.22 b/FileSets/PatchSource/PageMain.qml-v3.22
index 454c2498..78fc4236 100644
--- a/FileSets/PatchSource/PageMain.qml-v3.22
+++ b/FileSets/PatchSource/PageMain.qml-v3.22
@@ -1,5 +1,3 @@
-//////// GuiMods modified order to put Settings, then Notifications at top of list
-
import QtQuick 1.1
import "utils.js" as Utils
import com.victron.velib 1.0
diff --git a/FileSets/PatchSource/PageMain.qml-v3.22.patch b/FileSets/PatchSource/PageMain.qml-v3.22.patch
index cbdceadf..a800e562 100644
--- a/FileSets/PatchSource/PageMain.qml-v3.22.patch
+++ b/FileSets/PatchSource/PageMain.qml-v3.22.patch
@@ -1,8 +1,6 @@
--- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageMain.qml-v3.22.orig 2024-07-08 09:45:00
-+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageMain.qml-v3.22 2024-07-09 09:53:20
-@@ -1,11 +1,52 @@
-+//////// GuiMods modified order to put Settings, then Notifications at top of list
-+
++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageMain.qml-v3.22 2025-02-02 17:47:12
+@@ -1,11 +1,50 @@
import QtQuick 1.1
+import "utils.js" as Utils
import com.victron.velib 1.0
@@ -53,7 +51,7 @@
VisualDataModel {
model: VeSortFilterProxyModel {
model: DeviceList {
-@@ -27,7 +68,7 @@
+@@ -27,7 +66,7 @@
subpage: model.page
}
}
@@ -62,7 +60,7 @@
MbSubMenu {
id: menuNotifications
description: qsTr("Notifications")
-@@ -37,17 +78,22 @@
+@@ -37,17 +76,22 @@
value: active.length > 0 ? active.length : ""
}
subpage: Component { PageNotifications {} }
diff --git a/FileSets/PatchSource/PageMain.qml-v3.34 b/FileSets/PatchSource/PageMain.qml-v3.34
index 34850138..dc24808f 100644
--- a/FileSets/PatchSource/PageMain.qml-v3.34
+++ b/FileSets/PatchSource/PageMain.qml-v3.34
@@ -1,5 +1,3 @@
-//////// GuiMods modified order to put Settings, then Notifications at top of list
-
import QtQuick 1.1
import "utils.js" as Utils
import com.victron.velib 1.0
diff --git a/FileSets/PatchSource/PageMain.qml-v3.34.patch b/FileSets/PatchSource/PageMain.qml-v3.34.patch
index 10ff3252..95e1ea04 100644
--- a/FileSets/PatchSource/PageMain.qml-v3.34.patch
+++ b/FileSets/PatchSource/PageMain.qml-v3.34.patch
@@ -1,8 +1,6 @@
--- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageMain.qml-v3.34.orig 2024-07-08 09:45:00
-+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageMain.qml-v3.34 2024-07-09 09:53:47
-@@ -1,11 +1,48 @@
-+//////// GuiMods modified order to put Settings, then Notifications at top of list
-+
++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageMain.qml-v3.34 2025-02-02 17:47:35
+@@ -1,11 +1,46 @@
import QtQuick 1.1
+import "utils.js" as Utils
import com.victron.velib 1.0
@@ -49,7 +47,7 @@
VisualDataModel {
model: VeSortFilterProxyModel {
model: DeviceList {
-@@ -27,23 +64,28 @@
+@@ -27,23 +62,28 @@
subpage: model.page
}
}
diff --git a/FileSets/PatchSource/PageMain.qml-v3.40 b/FileSets/PatchSource/PageMain.qml-v3.40
index 9600c264..44d11166 100644
--- a/FileSets/PatchSource/PageMain.qml-v3.40
+++ b/FileSets/PatchSource/PageMain.qml-v3.40
@@ -1,5 +1,3 @@
-//////// GuiMods modified order to put Settings, then Notifications at top of list
-
import QtQuick 1.1
import "utils.js" as Utils
import com.victron.velib 1.0
diff --git a/FileSets/PatchSource/PageMain.qml-v3.40.patch b/FileSets/PatchSource/PageMain.qml-v3.40.patch
index 8357d94c..8dee0347 100644
--- a/FileSets/PatchSource/PageMain.qml-v3.40.patch
+++ b/FileSets/PatchSource/PageMain.qml-v3.40.patch
@@ -1,8 +1,6 @@
--- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageMain.qml-v3.40.orig 2024-07-08 09:45:00
-+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageMain.qml-v3.40 2024-07-09 09:55:28
-@@ -1,11 +1,48 @@
-+//////// GuiMods modified order to put Settings, then Notifications at top of list
-+
++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageMain.qml-v3.40 2025-02-02 17:47:45
+@@ -1,11 +1,46 @@
import QtQuick 1.1
+import "utils.js" as Utils
import com.victron.velib 1.0
@@ -49,7 +47,7 @@
VisualDataModel {
model: VeSortFilterProxyModel {
model: DeviceList {
-@@ -27,23 +64,28 @@
+@@ -27,23 +62,28 @@
subpage: model.page
}
}
@@ -80,7 +78,7 @@
editable: true
function clicked() {
-@@ -155,7 +197,8 @@
+@@ -155,7 +195,8 @@
page = vebusPage
break;
case DBusService.DBUS_SERVICE_MULTI_RS:
diff --git a/FileSets/PatchSource/PageMain.qml-v3.60~21 b/FileSets/PatchSource/PageMain.qml-v3.60~21
new file mode 100644
index 00000000..1f990841
--- /dev/null
+++ b/FileSets/PatchSource/PageMain.qml-v3.60~21
@@ -0,0 +1,294 @@
+import QtQuick 2
+import "utils.js" as Utils
+import com.victron.velib 1.0
+
+MbPage {
+ id: root
+ title: qsTr("Device List")
+
+//////// GuiMods put Settings, Notifications, Remove disconnected... at top of list
+ property VBusItem moveSettings: VBusItem { id: moveSettings; bind: Utils.path("com.victronenergy.settings", "/Settings/GuiMods/MoveSettings")}
+ property bool settingsAtTop: moveSettings.valid && moveSettings.value === 1
+
+ model: VisualModels {
+//////// GuiMods put Settings, Notifications, Remove disconnected... at top of list
+ VisibleItemModel { //////// use VisualItemModel below for v2.93 and earlier
+ MbSubMenu {
+ description: qsTr("Settings")
+ subpage: Component { PageSettings {} }
+ show: settingsAtTop
+ }
+
+ MbSubMenu {
+ id: menuNotificationsTop
+ description: qsTr("Notifications")
+ item: VBusItem { value: menuNotifications.subpage.summary }
+ subpage: PageNotifications { }
+ show: settingsAtTop
+ }
+
+ MbOK {
+ description: qsTr("Remove disconnected devices")
+ value: qsTr("Press to remove")
+ show: settingsAtTop && deviceList.disconnectedDevices != 0
+ editable: true
+
+ onClicked: {
+ listview.decrementCurrentIndex()
+ deviceList.removeDisconnected()
+ }
+ }
+ }
+//////// end GuiMods put Settings, Notifications, Remove disconnected... at top of list
+
+ DelegateModel {
+ model: VeSortFilterProxyModel {
+ model: DeviceList {
+ id: deviceList
+ onRowsAboutToBeRemoved: {
+ for (var i = first; i <= last; i++)
+ deviceList.page(i).destroy()
+ }
+ }
+ sortRole: DeviceList.DescriptionRole
+ dynamicSortFilter: true
+ naturalSort: true
+ sortCaseSensitivity: Qt.CaseInsensitive
+ }
+
+ delegate: MbDevice {
+ iconId: "icon-toolbar-enter"
+ service: model.page.service
+ subpage: model.page
+ }
+ }
+ VisibleItemModel {
+ MbSubMenu {
+ id: menuNotifications
+ description: qsTr("Notifications")
+ item: VBusItem { value: menuNotifications.subpage.summary }
+ subpage: PageNotifications { }
+//////// GuiMods hide this if added at top
+ show: !settingsAtTop
+ }
+
+ MbSubMenu {
+ description: qsTr("Settings")
+ subpage: Component { PageSettings {} }
+//////// GuiMods hide this if added at top
+ show: !settingsAtTop
+ }
+
+ MbOK {
+ description: qsTr("Remove disconnected devices")
+ value: qsTr("Press to remove")
+//////// GuiMods hide this if added at top
+ show: !settingsAtTop && deviceList.disconnectedDevices != 0
+ editable: true
+
+ onClicked: {
+ listview.decrementCurrentIndex()
+ deviceList.removeDisconnected()
+ }
+ }
+ }
+ }
+
+ Component {
+ id: vebusPage
+ PageVebus {}
+ }
+
+ Component {
+ id: batteryPage
+ PageBattery {}
+ }
+
+ Component {
+ id: solarChargerPage
+ PageSolarCharger {}
+ }
+
+ Component {
+ id: acInPage
+ PageAcIn {}
+ }
+
+ Component {
+ id: acChargerPage
+ PageAcCharger {}
+ }
+
+ Component {
+ id: tankPage
+ PageTankSensor {}
+ }
+
+ Component {
+ id: motorDrivePage
+ PageMotorDrive {}
+ }
+
+ Component {
+ id: inverterPage
+ PageInverter {}
+ }
+
+ Component {
+ id: pulseCounterPage
+ PagePulseCounter {}
+ }
+
+ Component {
+ id: digitalInputPage
+ PageDigitalInput {}
+ }
+
+ Component {
+ id: temperatureSensorPage
+ PageTemperatureSensor {}
+ }
+
+ Component {
+ id: unsupportedDevicePage
+ PageUnsupportedDevice {}
+ }
+
+ Component {
+ id: meteoDevicePage
+ PageMeteo {}
+ }
+
+ Component {
+ id: evChargerPage
+ PageEvCharger {}
+ }
+
+ Component {
+ id: dcMeterPage
+ PageDcMeter {}
+ }
+
+ Component {
+ id: alternatorPage
+ PageAlternator {}
+ }
+
+ Component {
+ id: dcDcConverterPage
+ PageDcDcConverter {}
+ }
+
+ Component {
+ id: acSystemPage
+ PageRsSystem {}
+ }
+
+ Component {
+ id: gensetPage
+ PageGenset {}
+ }
+
+ function addService(service)
+ {
+ var name = service.name
+
+ var page
+ switch(service.type)
+ {
+ case DBusService.DBUS_SERVICE_MULTI:
+ page = vebusPage
+ break;
+ case DBusService.DBUS_SERVICE_MULTI_RS:
+ return;
+ case DBusService.DBUS_SERVICE_BATTERY:
+ page = batteryPage
+ break;
+ case DBusService.DBUS_SERVICE_SOLAR_CHARGER:
+ page = solarChargerPage
+ break;
+ case DBusService.DBUS_SERVICE_PV_INVERTER:
+ page = acInPage
+ break;
+ case DBusService.DBUS_SERVICE_AC_CHARGER:
+ page = acChargerPage
+ break;
+ case DBusService.DBUS_SERVICE_TANK:
+ page = tankPage
+ break;
+ case DBusService.DBUS_SERVICE_GRIDMETER:
+ page = acInPage
+ break
+ case DBusService.DBUS_SERVICE_GENSET:
+ case DBusService.DBUS_SERVICE_DCGENSET:
+ page = gensetPage
+ break
+ case DBusService.DBUS_SERVICE_MOTOR_DRIVE:
+ page = motorDrivePage
+ break
+ case DBusService.DBUS_SERVICE_INVERTER:
+ page = inverterPage
+ break;
+ case DBusService.DBUS_SERVICE_TEMPERATURE_SENSOR:
+ page = temperatureSensorPage
+ break;
+ case DBusService.DBUS_SERVICE_SYSTEM_CALC:
+ return;
+ case DBusService.DBUS_SERVICE_DIGITAL_INPUT:
+ page = digitalInputPage
+ break;
+ case DBusService.DBUS_SERVICE_PULSE_COUNTER:
+ page = pulseCounterPage
+ break;
+ case DBusService.DBUS_SERVICE_UNSUPPORTED:
+ page = unsupportedDevicePage
+ break;
+ case DBusService.DBUS_SERVICE_METEO:
+ page = meteoDevicePage
+ break;
+ case DBusService.DBUS_SERVICE_VECAN:
+ return;
+ case DBusService.DBUS_SERVICE_EVCHARGER:
+ page = evChargerPage
+ break
+ case DBusService.DBUS_SERVICE_ACLOAD:
+ page = acInPage
+ break
+ case DBusService.DBUS_SERVICE_HUB4:
+ return;
+ case DBusService.DBUS_SERVICE_FUELCELL:
+ case DBusService.DBUS_SERVICE_DCSOURCE:
+ case DBusService.DBUS_SERVICE_DCLOAD:
+ case DBusService.DBUS_SERVICE_DCSYSTEM:
+ page = dcMeterPage
+ break
+ case DBusService.DBUS_SERVICE_ALTERNATOR:
+ page = alternatorPage
+ break
+ case DBusService.DBUS_SERVICE_DCDC:
+ page = dcDcConverterPage
+ break
+ case DBusService.DBUS_SERVICE_ACSYSTEM:
+ page = acSystemPage
+ break
+ case DBusService.DBUS_SERVICE_HEATPUMP:
+ page = acInPage
+ break
+ default:
+ console.log("unknown service " + name)
+ return;
+ }
+
+ deviceList.append(service, page.createObject(root, {service: service, bindPrefix: service.name}))
+ }
+
+ Component.onCompleted: {
+ for (var i = 0; i < DBusServices.count; i++)
+ addService(DBusServices.at(i))
+ }
+
+ Connections {
+ target: DBusServices
+ onDbusServiceFound: addService(service)
+ }
+}
diff --git a/FileSets/PatchSource/PageMain.qml-v3.60~21.orig b/FileSets/PatchSource/PageMain.qml-v3.60~21.orig
new file mode 100644
index 00000000..9cf0c4cb
--- /dev/null
+++ b/FileSets/PatchSource/PageMain.qml-v3.60~21.orig
@@ -0,0 +1,254 @@
+import QtQuick 2
+import com.victron.velib 1.0
+
+MbPage {
+ id: root
+ title: qsTr("Device List")
+
+ model: VisualModels {
+ DelegateModel {
+ model: VeSortFilterProxyModel {
+ model: DeviceList {
+ id: deviceList
+ onRowsAboutToBeRemoved: {
+ for (var i = first; i <= last; i++)
+ deviceList.page(i).destroy()
+ }
+ }
+ sortRole: DeviceList.DescriptionRole
+ dynamicSortFilter: true
+ naturalSort: true
+ sortCaseSensitivity: Qt.CaseInsensitive
+ }
+
+ delegate: MbDevice {
+ iconId: "icon-toolbar-enter"
+ service: model.page.service
+ subpage: model.page
+ }
+ }
+ VisibleItemModel {
+ MbSubMenu {
+ id: menuNotifications
+ description: qsTr("Notifications")
+ item: VBusItem { value: menuNotifications.subpage.summary }
+ subpage: PageNotifications { }
+ }
+
+ MbSubMenu {
+ description: qsTr("Settings")
+ subpage: Component { PageSettings {} }
+ }
+
+ MbOK {
+ description: qsTr("Remove disconnected devices")
+ value: qsTr("Press to remove")
+ show: deviceList.disconnectedDevices != 0
+ editable: true
+
+ onClicked: {
+ listview.decrementCurrentIndex()
+ deviceList.removeDisconnected()
+ }
+ }
+ }
+ }
+
+ Component {
+ id: vebusPage
+ PageVebus {}
+ }
+
+ Component {
+ id: batteryPage
+ PageBattery {}
+ }
+
+ Component {
+ id: solarChargerPage
+ PageSolarCharger {}
+ }
+
+ Component {
+ id: acInPage
+ PageAcIn {}
+ }
+
+ Component {
+ id: acChargerPage
+ PageAcCharger {}
+ }
+
+ Component {
+ id: tankPage
+ PageTankSensor {}
+ }
+
+ Component {
+ id: motorDrivePage
+ PageMotorDrive {}
+ }
+
+ Component {
+ id: inverterPage
+ PageInverter {}
+ }
+
+ Component {
+ id: pulseCounterPage
+ PagePulseCounter {}
+ }
+
+ Component {
+ id: digitalInputPage
+ PageDigitalInput {}
+ }
+
+ Component {
+ id: temperatureSensorPage
+ PageTemperatureSensor {}
+ }
+
+ Component {
+ id: unsupportedDevicePage
+ PageUnsupportedDevice {}
+ }
+
+ Component {
+ id: meteoDevicePage
+ PageMeteo {}
+ }
+
+ Component {
+ id: evChargerPage
+ PageEvCharger {}
+ }
+
+ Component {
+ id: dcMeterPage
+ PageDcMeter {}
+ }
+
+ Component {
+ id: alternatorPage
+ PageAlternator {}
+ }
+
+ Component {
+ id: dcDcConverterPage
+ PageDcDcConverter {}
+ }
+
+ Component {
+ id: acSystemPage
+ PageRsSystem {}
+ }
+
+ Component {
+ id: gensetPage
+ PageGenset {}
+ }
+
+ function addService(service)
+ {
+ var name = service.name
+
+ var page
+ switch(service.type)
+ {
+ case DBusService.DBUS_SERVICE_MULTI:
+ page = vebusPage
+ break;
+ case DBusService.DBUS_SERVICE_MULTI_RS:
+ return;
+ case DBusService.DBUS_SERVICE_BATTERY:
+ page = batteryPage
+ break;
+ case DBusService.DBUS_SERVICE_SOLAR_CHARGER:
+ page = solarChargerPage
+ break;
+ case DBusService.DBUS_SERVICE_PV_INVERTER:
+ page = acInPage
+ break;
+ case DBusService.DBUS_SERVICE_AC_CHARGER:
+ page = acChargerPage
+ break;
+ case DBusService.DBUS_SERVICE_TANK:
+ page = tankPage
+ break;
+ case DBusService.DBUS_SERVICE_GRIDMETER:
+ page = acInPage
+ break
+ case DBusService.DBUS_SERVICE_GENSET:
+ case DBusService.DBUS_SERVICE_DCGENSET:
+ page = gensetPage
+ break
+ case DBusService.DBUS_SERVICE_MOTOR_DRIVE:
+ page = motorDrivePage
+ break
+ case DBusService.DBUS_SERVICE_INVERTER:
+ page = inverterPage
+ break;
+ case DBusService.DBUS_SERVICE_TEMPERATURE_SENSOR:
+ page = temperatureSensorPage
+ break;
+ case DBusService.DBUS_SERVICE_SYSTEM_CALC:
+ return;
+ case DBusService.DBUS_SERVICE_DIGITAL_INPUT:
+ page = digitalInputPage
+ break;
+ case DBusService.DBUS_SERVICE_PULSE_COUNTER:
+ page = pulseCounterPage
+ break;
+ case DBusService.DBUS_SERVICE_UNSUPPORTED:
+ page = unsupportedDevicePage
+ break;
+ case DBusService.DBUS_SERVICE_METEO:
+ page = meteoDevicePage
+ break;
+ case DBusService.DBUS_SERVICE_VECAN:
+ return;
+ case DBusService.DBUS_SERVICE_EVCHARGER:
+ page = evChargerPage
+ break
+ case DBusService.DBUS_SERVICE_ACLOAD:
+ page = acInPage
+ break
+ case DBusService.DBUS_SERVICE_HUB4:
+ return;
+ case DBusService.DBUS_SERVICE_FUELCELL:
+ case DBusService.DBUS_SERVICE_DCSOURCE:
+ case DBusService.DBUS_SERVICE_DCLOAD:
+ case DBusService.DBUS_SERVICE_DCSYSTEM:
+ page = dcMeterPage
+ break
+ case DBusService.DBUS_SERVICE_ALTERNATOR:
+ page = alternatorPage
+ break
+ case DBusService.DBUS_SERVICE_DCDC:
+ page = dcDcConverterPage
+ break
+ case DBusService.DBUS_SERVICE_ACSYSTEM:
+ page = acSystemPage
+ break
+ case DBusService.DBUS_SERVICE_HEATPUMP:
+ page = acInPage
+ break
+ default:
+ console.log("unknown service " + name)
+ return;
+ }
+
+ deviceList.append(service, page.createObject(root, {service: service, bindPrefix: service.name}))
+ }
+
+ Component.onCompleted: {
+ for (var i = 0; i < DBusServices.count; i++)
+ addService(DBusServices.at(i))
+ }
+
+ Connections {
+ target: DBusServices
+ onDbusServiceFound: addService(service)
+ }
+}
diff --git a/FileSets/PatchSource/PageMain.qml-v3.60~21.patch b/FileSets/PatchSource/PageMain.qml-v3.60~21.patch
new file mode 100644
index 00000000..3762c926
--- /dev/null
+++ b/FileSets/PatchSource/PageMain.qml-v3.60~21.patch
@@ -0,0 +1,73 @@
+--- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageMain.qml-v3.60~21.orig 2025-01-29 07:31:17
++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageMain.qml-v3.60~21 2025-02-02 22:03:18
+@@ -1,11 +1,46 @@
+ import QtQuick 2
++import "utils.js" as Utils
+ import com.victron.velib 1.0
+
+ MbPage {
+ id: root
+ title: qsTr("Device List")
+
++//////// GuiMods put Settings, Notifications, Remove disconnected... at top of list
++ property VBusItem moveSettings: VBusItem { id: moveSettings; bind: Utils.path("com.victronenergy.settings", "/Settings/GuiMods/MoveSettings")}
++ property bool settingsAtTop: moveSettings.valid && moveSettings.value === 1
++
+ model: VisualModels {
++//////// GuiMods put Settings, Notifications, Remove disconnected... at top of list
++ VisibleItemModel { //////// use VisualItemModel below for v2.93 and earlier
++ MbSubMenu {
++ description: qsTr("Settings")
++ subpage: Component { PageSettings {} }
++ show: settingsAtTop
++ }
++
++ MbSubMenu {
++ id: menuNotificationsTop
++ description: qsTr("Notifications")
++ item: VBusItem { value: menuNotifications.subpage.summary }
++ subpage: PageNotifications { }
++ show: settingsAtTop
++ }
++
++ MbOK {
++ description: qsTr("Remove disconnected devices")
++ value: qsTr("Press to remove")
++ show: settingsAtTop && deviceList.disconnectedDevices != 0
++ editable: true
++
++ onClicked: {
++ listview.decrementCurrentIndex()
++ deviceList.removeDisconnected()
++ }
++ }
++ }
++//////// end GuiMods put Settings, Notifications, Remove disconnected... at top of list
++
+ DelegateModel {
+ model: VeSortFilterProxyModel {
+ model: DeviceList {
+@@ -33,17 +68,22 @@
+ description: qsTr("Notifications")
+ item: VBusItem { value: menuNotifications.subpage.summary }
+ subpage: PageNotifications { }
++//////// GuiMods hide this if added at top
++ show: !settingsAtTop
+ }
+
+ MbSubMenu {
+ description: qsTr("Settings")
+ subpage: Component { PageSettings {} }
++//////// GuiMods hide this if added at top
++ show: !settingsAtTop
+ }
+
+ MbOK {
+ description: qsTr("Remove disconnected devices")
+ value: qsTr("Press to remove")
+- show: deviceList.disconnectedDevices != 0
++//////// GuiMods hide this if added at top
++ show: !settingsAtTop && deviceList.disconnectedDevices != 0
+ editable: true
+
+ onClicked: {
diff --git a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.14 b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.14
index de3b95d4..e4b5276b 100644
--- a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.14
+++ b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.14
@@ -1,5 +1,3 @@
-//////// modified to add GuiMods controls
-
import QtQuick 1.1
import com.victron.velib 1.0
import "utils.js" as Utils
diff --git a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.14.patch b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.14.patch
index 74fcc5ee..d42fa297 100644
--- a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.14.patch
+++ b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.14.patch
@@ -1,12 +1,6 @@
--- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsDisplay.qml-v3.14.orig 2024-07-09 10:19:58
-+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsDisplay.qml-v3.14 2024-01-24 10:19:41
-@@ -1,3 +1,5 @@
-+//////// modified to add GuiMods controls
-+
- import QtQuick 1.1
- import com.victron.velib 1.0
- import "utils.js" as Utils
-@@ -52,6 +54,15 @@
++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsDisplay.qml-v3.14 2025-02-02 17:43:45
+@@ -52,6 +52,15 @@
]
}
@@ -22,7 +16,7 @@
MbSwitch {
bind: Utils.path(bindPrefix, "/MobileOverview")
name: qsTr("Show boat & motorhome overview")
-@@ -65,12 +76,20 @@
+@@ -65,12 +74,20 @@
name: qsTr("Show tanks overview")
}
@@ -44,7 +38,7 @@
// NOTE: do make sure application.cpp returns the correct fontForLanguage.
// The current font might not be able to display these values / the default
// font might not be contain the characters required for the selected language.
-@@ -83,11 +102,9 @@
+@@ -83,11 +100,9 @@
MbOptionLang { description: "Français"; value: "fr" },
MbOptionLang { description: "Italiano"; value: "it" },
MbOptionLang { description: "Nederlands"; value: "nl" },
diff --git a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.22 b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.22
index b1c8f278..ad3f7d85 100644
--- a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.22
+++ b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.22
@@ -1,5 +1,3 @@
-//////// modified to add GuiMods controls
-
import QtQuick 1.1
import com.victron.velib 1.0
import "utils.js" as Utils
diff --git a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.22.patch b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.22.patch
index 2b87c002..58907266 100644
--- a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.22.patch
+++ b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.22.patch
@@ -1,12 +1,6 @@
--- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsDisplay.qml-v3.22.orig 2024-07-09 10:19:58
-+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsDisplay.qml-v3.22 2024-03-24 08:13:08
-@@ -1,3 +1,5 @@
-+//////// modified to add GuiMods controls
-+
- import QtQuick 1.1
- import com.victron.velib 1.0
- import "utils.js" as Utils
-@@ -52,6 +54,15 @@
++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsDisplay.qml-v3.22 2025-02-02 17:43:59
+@@ -52,6 +52,15 @@
]
}
@@ -22,7 +16,7 @@
MbSwitch {
bind: Utils.path(bindPrefix, "/MobileOverview")
name: qsTr("Show boat & motorhome overview")
-@@ -65,6 +76,15 @@
+@@ -65,6 +74,15 @@
name: qsTr("Show tanks overview")
}
@@ -38,7 +32,7 @@
MbItemOptions {
id: languageSelect
description: qsTr("Language")
-@@ -75,23 +95,23 @@
+@@ -75,23 +93,23 @@
// The current font might not be able to display these values / the default
// font might not be contain the characters required for the selected language.
possibleValues: [
diff --git a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.34 b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.34
index bc182a38..e6f1cb52 100644
--- a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.34
+++ b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.34
@@ -1,5 +1,3 @@
-//////// modified to add GuiMods controls
-
import QtQuick 1.1
import com.victron.velib 1.0
import "utils.js" as Utils
diff --git a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.34.patch b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.34.patch
index c1ff3b58..5aac065c 100644
--- a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.34.patch
+++ b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.34.patch
@@ -1,12 +1,6 @@
--- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsDisplay.qml-v3.34.orig 2024-07-09 10:19:58
-+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsDisplay.qml-v3.34 2024-03-24 08:13:08
-@@ -1,3 +1,5 @@
-+//////// modified to add GuiMods controls
-+
- import QtQuick 1.1
- import com.victron.velib 1.0
- import "utils.js" as Utils
-@@ -65,6 +67,15 @@
++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsDisplay.qml-v3.34 2025-02-02 17:44:10
+@@ -65,6 +65,15 @@
name: qsTr("Show tanks overview")
}
diff --git a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.40 b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.40
index ad1b1187..a0461853 100644
--- a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.40
+++ b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.40
@@ -1,5 +1,3 @@
-//////// modified to add GuiMods controls
-
import QtQuick 1.1
import com.victron.velib 1.0
import "utils.js" as Utils
diff --git a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.40.patch b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.40.patch
index fa17a507..d0349312 100644
--- a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.40.patch
+++ b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.40.patch
@@ -1,12 +1,6 @@
--- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsDisplay.qml-v3.40.orig 2024-07-09 10:19:58
-+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsDisplay.qml-v3.40 2024-05-14 07:00:12
-@@ -1,3 +1,5 @@
-+//////// modified to add GuiMods controls
-+
- import QtQuick 1.1
- import com.victron.velib 1.0
- import "utils.js" as Utils
-@@ -65,6 +67,15 @@
++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsDisplay.qml-v3.40 2025-02-02 17:43:38
+@@ -65,6 +65,15 @@
name: qsTr("Show tanks overview")
}
diff --git a/FileSets/PatchSource/PageSettingsGenerator.qml b/FileSets/PatchSource/PageSettingsGenerator.qml
index 9b2625a6..ae056da8 100644
--- a/FileSets/PatchSource/PageSettingsGenerator.qml
+++ b/FileSets/PatchSource/PageSettingsGenerator.qml
@@ -1,6 +1,3 @@
-//// GuiMods
-//// added link to external state enable
-
import QtQuick 1.1
import com.victron.velib 1.0
import "utils.js" as Utils
diff --git a/FileSets/PatchSource/PageSettingsGenerator.qml.patch b/FileSets/PatchSource/PageSettingsGenerator.qml.patch
index a0a8eac6..45ef77de 100644
--- a/FileSets/PatchSource/PageSettingsGenerator.qml.patch
+++ b/FileSets/PatchSource/PageSettingsGenerator.qml.patch
@@ -1,13 +1,6 @@
--- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsGenerator.qml.orig 2025-01-12 08:33:22
-+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsGenerator.qml 2024-09-28 14:15:31
-@@ -1,3 +1,6 @@
-+//// GuiMods
-+//// added link to external state enable
-+
- import QtQuick 1.1
- import com.victron.velib 1.0
- import "utils.js" as Utils
-@@ -66,7 +69,18 @@
++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsGenerator.qml 2025-02-02 17:44:35
+@@ -66,7 +66,18 @@
}
}
diff --git a/FileSets/PatchSource/PageSettingsRelay.qml-v3.22 b/FileSets/PatchSource/PageSettingsRelay.qml-v3.22
index 0c401e3e..921f24cf 100644
--- a/FileSets/PatchSource/PageSettingsRelay.qml-v3.22
+++ b/FileSets/PatchSource/PageSettingsRelay.qml-v3.22
@@ -1,8 +1,3 @@
-//////// modified to
-//////// add 6 relays for Raspberry PI
-//////// custom relay name for Relay Overview
-//////// show/hide relay in Relay Overview
-
import QtQuick 1.1
import com.victron.velib 1.0
import "utils.js" as Utils
diff --git a/FileSets/PatchSource/PageSettingsRelay.qml-v3.22.patch b/FileSets/PatchSource/PageSettingsRelay.qml-v3.22.patch
index 2c034d95..81302df9 100644
--- a/FileSets/PatchSource/PageSettingsRelay.qml-v3.22.patch
+++ b/FileSets/PatchSource/PageSettingsRelay.qml-v3.22.patch
@@ -1,15 +1,6 @@
--- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsRelay.qml-v3.22.orig 2025-01-09 14:05:43
-+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsRelay.qml-v3.22 2024-02-27 21:25:25
-@@ -1,3 +1,8 @@
-+//////// modified to
-+//////// add 6 relays for Raspberry PI
-+//////// custom relay name for Relay Overview
-+//////// show/hide relay in Relay Overview
-+
- import QtQuick 1.1
- import com.victron.velib 1.0
- import "utils.js" as Utils
-@@ -6,13 +11,85 @@
++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsRelay.qml-v3.22 2025-02-02 17:44:48
+@@ -6,13 +6,85 @@
id: pageRelaySettings
title: qsTr("Relay")
property string bindPrefix: "com.victronenergy.settings"
@@ -98,7 +89,7 @@
bind: Utils.path(bindPrefix, "/Settings/Relay/Function")
possibleValues:[
MbOption { description: qsTr("Alarm relay"); value: 0 },
-@@ -21,12 +98,13 @@
+@@ -21,12 +93,13 @@
MbOption { description: qsTr("Manual"); value: 2 },
MbOption { description: qsTr("Temperature"); value: 4 }
]
@@ -113,7 +104,7 @@
possibleValues: [
MbOption { description: qsTr("Normally open"); value: 0 },
MbOption { description: qsTr("Normally closed"); value: 1 }
-@@ -34,58 +112,422 @@
+@@ -34,52 +107,147 @@
}
MbSwitch {
@@ -276,10 +267,10 @@
subpage: Component {
PageSettingsRelayTempSensors {
id: relayPage
- title: qsTr("Temperature control rules")
+@@ -87,5 +255,274 @@
}
}
-+ }
+ }
+
+ MbEditBox {
+ id: relay1name
@@ -309,7 +300,7 @@
+ name: qsTr("Show Relay 2 in overview")
+ bind: "com.victronenergy.settings/Settings/Relay/1/Show"
+ show: hasRelay2
- }
++ }
+
+ MbEditBox {
+ id: relay3name
diff --git a/FileSets/PatchSource/PageSettingsRelay.qml-v3.60~16 b/FileSets/PatchSource/PageSettingsRelay.qml-v3.60~16
index 44c08871..8d9781ad 100644
--- a/FileSets/PatchSource/PageSettingsRelay.qml-v3.60~16
+++ b/FileSets/PatchSource/PageSettingsRelay.qml-v3.60~16
@@ -1,8 +1,3 @@
-//////// modified to
-//////// add up to 18 relays
-//////// custom relay name for Relay Overview
-//////// show/hide relay in Relay Overview
-
import QtQuick 1.1
import com.victron.velib 1.0
import "utils.js" as Utils
diff --git a/FileSets/PatchSource/PageSettingsRelay.qml-v3.60~16.patch b/FileSets/PatchSource/PageSettingsRelay.qml-v3.60~16.patch
index a0eb7c1e..ca912a0b 100644
--- a/FileSets/PatchSource/PageSettingsRelay.qml-v3.60~16.patch
+++ b/FileSets/PatchSource/PageSettingsRelay.qml-v3.60~16.patch
@@ -1,15 +1,6 @@
--- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsRelay.qml-v3.60~16.orig 2025-01-09 14:05:42
-+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsRelay.qml-v3.60~16 2024-10-02 11:55:16
-@@ -1,3 +1,8 @@
-+//////// modified to
-+//////// add up to 18 relays
-+//////// custom relay name for Relay Overview
-+//////// show/hide relay in Relay Overview
-+
- import QtQuick 1.1
- import com.victron.velib 1.0
- import "utils.js" as Utils
-@@ -6,14 +11,85 @@
++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsRelay.qml-v3.60~16 2025-02-02 17:45:04
+@@ -6,14 +6,85 @@
id: pageRelaySettings
title: qsTr("Relay")
property string bindPrefix: "com.victronenergy.settings"
@@ -99,7 +90,7 @@
bind: Utils.path(bindPrefix, "/Settings/Relay/Function")
possibleValues:[
MbOption { description: qsTr("Alarm relay"); value: 0 },
-@@ -23,12 +99,13 @@
+@@ -23,12 +94,13 @@
MbOption { description: qsTr("Manual"); value: 2 },
MbOption { description: qsTr("Temperature"); value: 4 }
]
@@ -114,7 +105,7 @@
possibleValues: [
MbOption { description: qsTr("Normally open"); value: 0 },
MbOption { description: qsTr("Normally closed"); value: 1 }
-@@ -36,34 +113,129 @@
+@@ -36,40 +108,404 @@
}
MbSwitch {
@@ -257,10 +248,10 @@
subpage: Component {
PageSettingsRelayTempSensors {
id: relayPage
-@@ -71,5 +243,274 @@
+ title: qsTr("Temperature control rules")
}
}
- }
++ }
+
+ MbEditBox {
+ id: relay1name
@@ -365,7 +356,7 @@
+ name: qsTr("Show Relay 7 in overview")
+ bind: "com.victronenergy.settings/Settings/Relay/6/Show"
+ show: hasRelay7
-+ }
+ }
+
+ MbEditBox {
+ id: relay8name
diff --git a/FileSets/PatchSource/main.qml-v3.22 b/FileSets/PatchSource/main.qml-v3.22
index 6df292cd..ccfffc9a 100644
--- a/FileSets/PatchSource/main.qml-v3.22
+++ b/FileSets/PatchSource/main.qml-v3.22
@@ -1,6 +1,3 @@
-//////// Modified to hide the OverviewTiles page
-//////// Modified to substitute flow overview pages
-
import QtQuick 1.1
import Qt.labs.components.native 1.0
diff --git a/FileSets/PatchSource/main.qml-v3.22.patch b/FileSets/PatchSource/main.qml-v3.22.patch
index fb43f14f..4f459b8b 100644
--- a/FileSets/PatchSource/main.qml-v3.22.patch
+++ b/FileSets/PatchSource/main.qml-v3.22.patch
@@ -1,13 +1,6 @@
--- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.22.orig 2024-07-08 09:16:57
-+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.22 2024-01-24 10:19:41
-@@ -1,3 +1,6 @@
-+//////// Modified to hide the OverviewTiles page
-+//////// Modified to substitute flow overview pages
-+
- import QtQuick 1.1
-
- import Qt.labs.components.native 1.0
-@@ -16,48 +19,108 @@
++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.22 2025-02-02 17:46:00
+@@ -16,48 +16,108 @@
property bool completed: false
property bool showAlert: NotificationCenter.alert
property bool alarm: NotificationCenter.alarm
@@ -138,7 +131,7 @@
VBusItem {
bind: "com.victronenergy.generator.startstop1/GensetProductId"
onValueChanged: {
-@@ -75,21 +138,103 @@
+@@ -75,21 +135,103 @@
}
}
@@ -180,7 +173,7 @@
- extraOverview("OverviewTanks.qml", value === 1)
- }
- }
-
++
+//////// handle OverviewMobileEnhanced page
+ VBusItem
+ {
@@ -236,7 +229,7 @@
+ bind: "com.victronenergy.settings/Settings/GuiMods/ShowRelayOverview"
+ onValueChanged: extraOverview ("OverviewRelays.qml", value === 1)
+ }
-+
+
+//////// show/hide the Overview Tanks/Temps/Digital Inputs page
+ VBusItem {
+ id: showOverviewTanksTemps
@@ -255,7 +248,7 @@
VBusItem {
id: startWithMenu
bind: "com.victronenergy.settings/Settings/Gui/StartWithMenuView"
-@@ -169,76 +314,111 @@
+@@ -169,76 +311,111 @@
id: mbTools
height: parent.height
@@ -419,7 +412,7 @@
}
}
}
-@@ -253,9 +433,10 @@
+@@ -253,9 +430,10 @@
ListElement {
pageSource: "OverviewHub.qml"
}
@@ -433,7 +426,7 @@
}
Component {
-@@ -274,7 +455,12 @@
+@@ -274,7 +452,12 @@
Timer {
interval: 2000
running: completed && overviewsLoaded && startWithMenu.valid
@@ -447,7 +440,7 @@
}
function getDefaultOverviewIndex()
-@@ -297,6 +483,7 @@
+@@ -297,6 +480,7 @@
Component {
id: offlineFwUpdates
PageSettingsFirmwareOffline { checkOnCompleted: true}
@@ -455,7 +448,7 @@
}
// Add or remove extra overviews. for example, generator overview
-@@ -327,11 +514,19 @@
+@@ -327,11 +511,19 @@
}
}
diff --git a/FileSets/PatchSource/main.qml-v3.34 b/FileSets/PatchSource/main.qml-v3.34
index d300de63..3398b6e7 100644
--- a/FileSets/PatchSource/main.qml-v3.34
+++ b/FileSets/PatchSource/main.qml-v3.34
@@ -1,6 +1,3 @@
-//////// Modified to hide the OverviewTiles page
-//////// Modified to substitute flow overview pages
-
import QtQuick 1.1
import Qt.labs.components.native 1.0
diff --git a/FileSets/PatchSource/main.qml-v3.34.patch b/FileSets/PatchSource/main.qml-v3.34.patch
index b49adf80..ec6bbcfd 100644
--- a/FileSets/PatchSource/main.qml-v3.34.patch
+++ b/FileSets/PatchSource/main.qml-v3.34.patch
@@ -1,13 +1,6 @@
--- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.34.orig 2024-07-08 09:16:57
-+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.34 2024-05-14 07:00:12
-@@ -1,3 +1,6 @@
-+//////// Modified to hide the OverviewTiles page
-+//////// Modified to substitute flow overview pages
-+
- import QtQuick 1.1
-
- import Qt.labs.components.native 1.0
-@@ -16,48 +19,108 @@
++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.34 2025-02-02 17:46:10
+@@ -16,48 +16,108 @@
property bool completed: false
property bool alarm: alarmNotification.valid ? alarmNotification.value : 0
property bool showAlert: alertNotification.valid ? alertNotification.value : 0
@@ -138,7 +131,7 @@
VBusItem {
bind: "com.victronenergy.generator.startstop1/GensetProductId"
onValueChanged: {
-@@ -75,21 +138,103 @@
+@@ -75,21 +135,103 @@
}
}
@@ -180,7 +173,7 @@
- extraOverview("OverviewTanks.qml", value === 1)
- }
- }
-
++
+//////// handle OverviewMobileEnhanced page
+ VBusItem
+ {
@@ -236,7 +229,7 @@
+ bind: "com.victronenergy.settings/Settings/GuiMods/ShowRelayOverview"
+ onValueChanged: extraOverview ("OverviewRelays.qml", value === 1)
+ }
-+
+
+//////// show/hide the Overview Tanks/Temps/Digital Inputs page
+ VBusItem {
+ id: showOverviewTanksTemps
@@ -255,7 +248,7 @@
VBusItem {
id: startWithMenu
bind: "com.victronenergy.settings/Settings/Gui/StartWithMenuView"
-@@ -179,76 +324,111 @@
+@@ -179,76 +321,111 @@
id: mbTools
height: parent.height
@@ -419,7 +412,7 @@
}
}
}
-@@ -263,9 +443,10 @@
+@@ -263,9 +440,10 @@
ListElement {
pageSource: "OverviewHub.qml"
}
@@ -433,7 +426,7 @@
}
Component {
-@@ -284,7 +465,12 @@
+@@ -284,7 +462,12 @@
Timer {
interval: 2000
running: completed && overviewsLoaded && startWithMenu.valid
@@ -447,7 +440,7 @@
}
function getDefaultOverviewIndex()
-@@ -307,6 +493,7 @@
+@@ -307,6 +490,7 @@
Component {
id: offlineFwUpdates
PageSettingsFirmwareOffline { checkOnCompleted: true}
@@ -455,7 +448,7 @@
}
// Add or remove extra overviews. for example, generator overview
-@@ -337,11 +524,19 @@
+@@ -337,11 +521,19 @@
}
}
diff --git a/FileSets/PatchSource/main.qml-v3.40 b/FileSets/PatchSource/main.qml-v3.40
index f90616b4..30e839c1 100644
--- a/FileSets/PatchSource/main.qml-v3.40
+++ b/FileSets/PatchSource/main.qml-v3.40
@@ -1,6 +1,3 @@
-//////// Modified to hide the OverviewTiles page
-//////// Modified to substitute flow overview pages
-
import QtQuick 1.1
import Qt.labs.components.native 1.0
diff --git a/FileSets/PatchSource/main.qml-v3.40.patch b/FileSets/PatchSource/main.qml-v3.40.patch
index ce4e1617..70735501 100644
--- a/FileSets/PatchSource/main.qml-v3.40.patch
+++ b/FileSets/PatchSource/main.qml-v3.40.patch
@@ -1,13 +1,6 @@
--- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.40.orig 2024-07-08 09:16:57
-+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.40 2024-05-17 07:34:02
-@@ -1,3 +1,6 @@
-+//////// Modified to hide the OverviewTiles page
-+//////// Modified to substitute flow overview pages
-+
- import QtQuick 1.1
-
- import Qt.labs.components.native 1.0
-@@ -16,7 +19,8 @@
++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.40 2025-02-02 17:46:23
+@@ -16,7 +16,8 @@
property bool completed: false
property bool alarm: alarmNotification.valid ? alarmNotification.value : 0
property bool showAlert: alertNotification.valid ? alertNotification.value : 0
@@ -17,7 +10,7 @@
property string bindPrefix: "com.victronenergy.settings"
property bool isNotificationPage: pageStack.currentPage && pageStack.currentPage.title === qsTr("Notifications")
-@@ -25,25 +29,83 @@
+@@ -25,25 +26,83 @@
property bool hasGridMeter: theSystem.hasGridMeter
@@ -107,7 +100,7 @@
VBusItem {
bind: "com.victronenergy.generator.startstop1/GensetProductId"
onValueChanged: {
-@@ -52,7 +114,7 @@
+@@ -52,7 +111,7 @@
// Show generic overview for ComAp and DSE
extraOverview("OverviewGeneratorOther.qml",
@@ -116,7 +109,7 @@
// Switch to FP overview in case it is the default one
if (isOverviewPage) {
-@@ -61,20 +123,102 @@
+@@ -61,21 +120,103 @@
}
}
@@ -158,7 +151,7 @@
- extraOverview("OverviewTanks.qml", value === 1)
- }
- }
-+
+
+//////// handle OverviewMobileEnhanced page
+ VBusItem
+ {
@@ -229,10 +222,11 @@
+ extraOverview("OverviewTanks.qml", value === 1)
+ }
+ }
-
++
VBusItem {
id: startWithMenu
-@@ -165,76 +309,111 @@
+ bind: "com.victronenergy.settings/Settings/Gui/StartWithMenuView"
+@@ -165,76 +306,111 @@
id: mbTools
height: parent.height
@@ -397,7 +391,7 @@
}
}
}
-@@ -249,9 +428,10 @@
+@@ -249,9 +425,10 @@
ListElement {
pageSource: "OverviewHub.qml"
}
@@ -411,7 +405,7 @@
}
Component {
-@@ -270,7 +450,12 @@
+@@ -270,7 +447,12 @@
Timer {
interval: 2000
running: completed && overviewsLoaded && startWithMenu.valid
@@ -425,7 +419,7 @@
}
function getDefaultOverviewIndex()
-@@ -293,6 +478,7 @@
+@@ -293,6 +475,7 @@
Component {
id: offlineFwUpdates
PageSettingsFirmwareOffline { checkOnCompleted: true}
@@ -433,7 +427,7 @@
}
// Add or remove extra overviews. for example, generator overview
-@@ -323,11 +509,19 @@
+@@ -323,11 +506,19 @@
}
}
diff --git a/FileSets/PatchSource/main.qml-v3.50 b/FileSets/PatchSource/main.qml-v3.50
index 0042e812..b821c1e1 100644
--- a/FileSets/PatchSource/main.qml-v3.50
+++ b/FileSets/PatchSource/main.qml-v3.50
@@ -1,6 +1,3 @@
-//////// Modified to hide the OverviewTiles page
-//////// Modified to substitute flow overview pages
-
import QtQuick 1.1
import Qt.labs.components.native 1.0
diff --git a/FileSets/PatchSource/main.qml-v3.50.patch b/FileSets/PatchSource/main.qml-v3.50.patch
index 8b280cdd..3edab79a 100644
--- a/FileSets/PatchSource/main.qml-v3.50.patch
+++ b/FileSets/PatchSource/main.qml-v3.50.patch
@@ -1,13 +1,6 @@
--- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.50.orig 2024-10-29 13:29:52
-+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.50 2024-11-03 13:26:42
-@@ -1,3 +1,6 @@
-+//////// Modified to hide the OverviewTiles page
-+//////// Modified to substitute flow overview pages
-+
- import QtQuick 1.1
-
- import Qt.labs.components.native 1.0
-@@ -16,7 +19,8 @@
++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.50 2025-02-02 17:46:37
+@@ -16,7 +16,8 @@
property bool completed: false
property bool alarm: alarmNotification.valid ? alarmNotification.value : 0
property bool showAlert: alertNotification.valid ? alertNotification.value : 0
@@ -17,7 +10,7 @@
property string bindPrefix: "com.victronenergy.settings"
property bool isNotificationPage: pageStack.currentPage && pageStack.currentPage.title === qsTr("Notifications")
-@@ -27,24 +31,83 @@
+@@ -27,24 +28,83 @@
property bool showInputLoads: theSystem.acInLoad.power.valid && (hasVebusEss ? (theSystem.hasGridMeter && withoutGridMeter.value === 0) : theSystem.hasGridMeter)
property int newUiAnnouncementVersion: 2 // Increase to make the popup appear again
@@ -107,7 +100,7 @@
VBusItem {
bind: "com.victronenergy.generator.startstop1/GensetProductId"
-@@ -63,13 +126,98 @@
+@@ -63,13 +123,98 @@
}
}
@@ -212,7 +205,7 @@
VBusItem {
id: tanksOverview
bind: "com.victronenergy.settings/Settings/Gui/TanksOverview"
-@@ -172,76 +320,111 @@
+@@ -172,76 +317,111 @@
id: mbTools
height: parent.height
@@ -377,7 +370,7 @@
}
}
}
-@@ -256,9 +439,10 @@
+@@ -256,9 +436,10 @@
ListElement {
pageSource: "OverviewHub.qml"
}
@@ -391,7 +384,7 @@
}
Component {
-@@ -284,7 +468,12 @@
+@@ -284,7 +465,12 @@
id: newUiPopupTimer
interval: 10000
running: false
@@ -405,7 +398,7 @@
}
function showNewUiPopup()
-@@ -350,11 +539,19 @@
+@@ -350,11 +536,19 @@
}
}
diff --git a/FileSets/PatchSource/main.qml-v3.51 b/FileSets/PatchSource/main.qml-v3.51
index 5ad419f3..c8e951c5 100644
--- a/FileSets/PatchSource/main.qml-v3.51
+++ b/FileSets/PatchSource/main.qml-v3.51
@@ -1,6 +1,3 @@
-//////// Modified to hide the OverviewTiles page
-//////// Modified to substitute flow overview pages
-
import QtQuick 1.1
import Qt.labs.components.native 1.0
diff --git a/FileSets/PatchSource/main.qml-v3.51.patch b/FileSets/PatchSource/main.qml-v3.51.patch
index 4e4b7016..f9ef1490 100644
--- a/FileSets/PatchSource/main.qml-v3.51.patch
+++ b/FileSets/PatchSource/main.qml-v3.51.patch
@@ -1,13 +1,6 @@
--- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.51.orig 2024-11-04 09:10:11
-+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.51 2024-11-05 11:00:48
-@@ -1,3 +1,6 @@
-+//////// Modified to hide the OverviewTiles page
-+//////// Modified to substitute flow overview pages
-+
- import QtQuick 1.1
-
- import Qt.labs.components.native 1.0
-@@ -8,7 +11,7 @@
++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.51 2025-02-02 17:46:52
+@@ -8,7 +8,7 @@
id: rootWindow
gpsConnected: gpsFix.value === 1
@@ -16,7 +9,7 @@
initialPage: PageMain {}
property VeQuickItem gpsService: VeQuickItem { uid: "dbus/com.victronenergy.system/GpsService" }
-@@ -16,7 +19,8 @@
+@@ -16,7 +16,8 @@
property bool completed: false
property bool alarm: alarmNotification.valid ? alarmNotification.value : 0
property bool showAlert: alertNotification.valid ? alertNotification.value : 0
@@ -26,7 +19,7 @@
property string bindPrefix: "com.victronenergy.settings"
property bool isNotificationPage: pageStack.currentPage && pageStack.currentPage.title === qsTr("Notifications")
-@@ -25,25 +29,85 @@
+@@ -25,25 +26,85 @@
property bool hasVebusEss: ['ESS', 'Hub-4'].indexOf(theSystem.systemType.value) > -1
property bool showInputLoads: theSystem.acInLoad.power.valid && (hasVebusEss ? (theSystem.hasGridMeter && withoutGridMeter.value === 0) : theSystem.hasGridMeter)
@@ -118,7 +111,7 @@
VBusItem {
bind: "com.victronenergy.generator.startstop1/GensetProductId"
-@@ -62,13 +126,98 @@
+@@ -62,13 +123,98 @@
}
}
@@ -223,7 +216,7 @@
VBusItem {
id: tanksOverview
bind: "com.victronenergy.settings/Settings/Gui/TanksOverview"
-@@ -112,6 +261,11 @@
+@@ -112,6 +258,11 @@
bind: "com.victronenergy.platform/Notifications/Alarm"
}
@@ -235,7 +228,7 @@
// Note: finding a firmware image on the storage device is error 4 for vrm storage
// since it should not be used for logging. That fact is used here to determine if
// there is a firmware image.
-@@ -166,76 +320,111 @@
+@@ -166,76 +317,111 @@
id: mbTools
height: parent.height
@@ -399,7 +392,7 @@
}
}
}
-@@ -250,9 +439,10 @@
+@@ -250,9 +436,10 @@
ListElement {
pageSource: "OverviewHub.qml"
}
@@ -413,7 +406,7 @@
}
Component {
-@@ -274,6 +464,26 @@
+@@ -274,6 +461,26 @@
onTriggered: if (startWithMenu.value === 0) showOverview()
}
@@ -440,7 +433,7 @@
function getDefaultOverviewIndex()
{
if(!defaultOverview.valid)
-@@ -324,11 +534,19 @@
+@@ -324,11 +531,19 @@
}
}
diff --git a/FileSets/PatchSource/startstop.py-v3.60~25 b/FileSets/PatchSource/startstop.py-v3.60~25
new file mode 100644
index 00000000..1cf3daed
--- /dev/null
+++ b/FileSets/PatchSource/startstop.py-v3.60~25
@@ -0,0 +1,1552 @@
+#!/usr/bin/python -u
+# -*- coding: utf-8 -*-
+
+#### GuiMods
+#### This file has been modified to allow the generator running state derived from the generator digital input
+#### Previous versions also used the genset AC input but this has been removed from this version with recent changes to stock code !!!!!
+#### If the incoming generator state changes, the manual start state is updated
+#### A switch in the generator settings menu controls whethter the incoming state affects manual start or time accumulaiton
+#### It is now possible to start the generator manually and have it stop automatically based on the preset conditions
+#### for automaitc start / stop
+#### warm-up and cool-down periods have been modified in order to work with an external transfer switch
+#### selecting grid or generator ahead of a MultiPlus input.
+#### Search for #### GuiMods to find changes
+
+# Function
+# dbus_generator monitors the dbus for batteries (com.victronenergy.battery.*) and
+# vebus com.victronenergy.vebus.*
+# Battery and vebus monitors can be configured through the gui.
+# It then monitors SOC, AC loads, battery current and battery voltage,to auto start/stop the generator based
+# on the configuration settings. Generator can be started manually or periodically setting a tes trun period.
+# Time zones function allows to use different values for the conditions along the day depending on time
+
+import dbus
+import datetime
+import calendar
+import time
+import sys
+import json
+import os
+import logging
+from collections import OrderedDict
+import monotonic_time
+from gen_utils import SettingsPrefix, Errors, States, enum
+from gen_utils import create_dbus_service
+# Victron packages
+sys.path.insert(1, os.path.join(os.path.dirname(__file__), 'ext', 'velib_python'))
+from ve_utils import exit_on_error
+from settingsdevice import SettingsDevice
+
+RunningConditions = enum(
+ Stopped = 0,
+ Manual = 1,
+ TestRun = 2,
+ LossOfCommunication = 3,
+ Soc = 4,
+ Acload = 5,
+ BatteryCurrent = 6,
+ BatteryVoltage = 7,
+ InverterHighTemp = 8,
+ InverterOverload = 9,
+ StopOnAc1 = 10,
+ StopOnAc2 = 11)
+
+Capabilities = enum(
+ WarmupCooldown = 1
+)
+
+SYSTEM_SERVICE = 'com.victronenergy.system'
+BATTERY_PREFIX = '/Dc/Battery'
+HISTORY_DAYS = 30
+AUTOSTART_DISABLED_ALARM_TIME = 600
+
+def safe_max(args):
+ try:
+ return max(x for x in args if x is not None)
+ except ValueError:
+ return None
+
+class Condition(object):
+ def __init__(self, parent):
+ self.parent = parent
+ self.reached = False
+ self.start_timer = 0
+ self.stop_timer = 0
+ self.valid = True
+ self.enabled = False
+ self.retries = 0
+
+ def __getitem__(self, key):
+ try:
+ return getattr(self, key)
+ except AttributeError:
+ raise KeyError(key)
+
+ def __setitem__(self, key, value):
+ setattr(self, key, value)
+
+ def get_value(self):
+ raise NotImplementedError("get_value")
+
+ @property
+ def multi_service(self):
+ return self.parent.multiservice
+
+ @property
+ def multi_service_type(self):
+ return self.parent.multiservice_type
+
+ @property
+ def monitor(self):
+ return self.parent._dbusmonitor
+
+class SocCondition(Condition):
+ name = 'soc'
+ monitoring = 'battery'
+ boolean = False
+ timed = True
+
+ def get_value(self):
+ return self.parent._get_battery().soc
+
+class AcLoadCondition(Condition):
+ name = 'acload'
+ monitoring = 'vebus'
+ boolean = False
+ timed = True
+
+ def get_value(self):
+ loadOnAcOut = []
+ totalConsumption = []
+
+ for phase in ['L1', 'L2', 'L3']:
+ # Get the values directly from the inverter, systemcalc doesn't provide raw inverted power
+ loadOnAcOut.append(self.monitor.get_value(self.multi_service, ('/Ac/Out/%s/P' % phase)))
+
+ # Calculate total consumption, '/Ac/Consumption/%s/Power' is deprecated
+ c_i = self.monitor.get_value(SYSTEM_SERVICE, ('/Ac/ConsumptionOnInput/%s/Power' % phase))
+ c_o = self.monitor.get_value(SYSTEM_SERVICE, ('/Ac/ConsumptionOnOutput/%s/Power' % phase))
+ totalConsumption.append(sum(filter(None, (c_i, c_o))))
+
+ # Invalidate if vebus is not available
+ if loadOnAcOut[0] == None:
+ return None
+
+ # Total consumption
+ if self.parent._settings['acloadmeasurement'] == 0:
+ return sum(filter(None, totalConsumption))
+
+ # Load on inverter AC out
+ if self.parent._settings['acloadmeasurement'] == 1:
+ return sum(filter(None, loadOnAcOut))
+
+ # Highest phase load
+ if self.parent._settings['acloadmeasurement'] == 2:
+ return safe_max(loadOnAcOut)
+
+class BatteryCurrentCondition(Condition):
+ name = 'batterycurrent'
+ monitoring = 'battery'
+ boolean = False
+ timed = True
+
+ def get_value(self):
+ c = self.parent._get_battery().current
+ if c is not None:
+ c *= -1
+ return c
+
+class BatteryVoltageCondition(Condition):
+ name = 'batteryvoltage'
+ monitoring = 'battery'
+ boolean = False
+ timed = True
+
+ def get_value(self):
+ return self.parent._get_battery().voltage
+
+class InverterTempCondition(Condition):
+ name = 'inverterhightemp'
+ monitoring = 'vebus'
+ boolean = True
+ timed = True
+
+ def get_value(self):
+ v = self.monitor.get_value(self.multi_service,
+ '/Alarms/HighTemperature')
+
+ # When multi is connected to CAN-bus, alarms are published to
+ # /Alarms/HighTemperature... but when connected to vebus alarms are
+ # splitted in three phases and published to /Alarms/LX/HighTemperature...
+ if v is None:
+ inverterHighTemp = []
+ for phase in ['L1', 'L2', 'L3']:
+ # Inverter alarms must be fetched directly from the inverter service
+ inverterHighTemp.append(self.monitor.get_value(self.multi_service, ('/Alarms/%s/HighTemperature' % phase)))
+ return safe_max(inverterHighTemp)
+ return v
+
+class InverterOverloadCondition(Condition):
+ name = 'inverteroverload'
+ monitoring = 'vebus'
+ boolean = True
+ timed = True
+
+ def get_value(self):
+ v = self.monitor.get_value(self.multi_service,
+ '/Alarms/Overload')
+
+ # When multi is connected to CAN-bus, alarms are published to
+ # /Alarms/Overload... but when connected to vebus alarms are
+ # splitted in three phases and published to /Alarms/LX/Overload...
+ if v is None:
+ inverterOverload = []
+ for phase in ['L1', 'L2', 'L3']:
+ # Inverter alarms must be fetched directly from the inverter service
+ inverterOverload.append(self.monitor.get_value(self.multi_service, ('/Alarms/%s/Overload' % phase)))
+ return safe_max(inverterOverload)
+ return v
+
+# The 'Stop on AC [1/2] conditions are disabled for the Multi RS (acsystem)
+# The 'stop on AC' condition stops the generator, which is connected to one AC input when there is AC detected on the other.
+# Since the Multi RS only has one AC input, these conditions cannot be used.
+class StopOnAc1Condition(Condition):
+ name = 'stoponac1'
+ monitoring = 'vebus'
+ boolean = True
+ timed = False
+
+ def get_value(self):
+ # AC input 1
+ if self.multi_service_type == 'vebus':
+ available = self.monitor.get_value(self.multi_service,
+ '/Ac/State/AcIn1Available')
+ if available is None:
+ # Not supported in firmware, fall back to old behaviour
+ activein = self.monitor.get_value(self.multi_service,
+ '/Ac/ActiveIn/ActiveInput')
+
+ # Active input is connected
+ connected = self.monitor.get_value(self.multi_service,
+ '/Ac/ActiveIn/Connected')
+ if None not in (activein, connected):
+ return activein == 0 and connected == 1
+ return None
+
+ return bool(available)
+ else:
+ # Disabled
+ return False
+
+# The StopOnAc2 condition cannot have the fallback to evaluating based on`/Ac/ActiveIn/ActiveInput` + `/Ac/ActiveIn/Connected`
+# Because these paths were present in the vebus firmware before `/Ac/State/AcIn2Available` was,
+# but switching over to AC input 2 was not supported in that firmware version.
+# So if the fallback were implemented for this condition as well, it would stop the
+# generator when AC in 2 becomes available but the quattro would not switch over.
+class StopOnAc2Condition(Condition):
+ name = 'stoponac2'
+ monitoring = 'vebus'
+ boolean = True
+ timed = False
+
+ def get_value(self):
+ if self.multi_service_type == 'vebus':
+ # AC input 2 available (used when grid is on AC-in-2)
+ available = self.monitor.get_value(self.multi_service,
+ '/Ac/State/AcIn2Available')
+
+ return None if available is None else bool(available)
+ else:
+ # Disabled
+ return False
+
+class Battery(object):
+ def __init__(self, monitor, service, prefix):
+ self.monitor = monitor
+ self.service = service
+ self.prefix = prefix
+
+ @property
+ def voltage(self):
+ return self.monitor.get_value(self.service, self.prefix + '/Voltage')
+
+ @property
+ def current(self):
+ return self.monitor.get_value(self.service, self.prefix + '/Current')
+
+ @property
+ def soc(self):
+ # Soc from the device doesn't have the '/Dc/0' prefix like the current and voltage do, but it does
+ # have the same prefix on systemcalc
+ return self.monitor.get_value(self.service, (BATTERY_PREFIX if self.prefix == BATTERY_PREFIX else '') + '/Soc')
+
+class StartStop(object):
+ _driver = None
+ def __init__(self, instance):
+#### GuiMods #### TODO: check if any of these are needed
+ logging.info ("GuiMods version of startstop.py")
+ self._currentTime = self._get_monotonic_seconds()
+ self._last_update_mtime = 0
+ self._accumulatedRunTime = 0
+ self._lastIsRunning = False
+ self._externalOverrideDelay = 99
+ self._linkToExternalState = False
+#### GuiMods warm-up / cool-down
+ self._warmUpEndTime = 0
+ self._coolDownEndTime = 0
+ self._ac1isIgnored = False
+ self._ac2isIgnored = False
+ self._activeAcInIsIgnored = False
+ self._acInIsGenerator = False
+#### end GuiMods
+
+ self._dbusservice = None
+ self._settings = None
+ self._dbusmonitor = None
+ self._remoteservice = None
+ self._name = None
+ self._enabled = False
+ self._generator_running = False
+ self._useGensetHours = False # Sync with genset operatinghours.
+ self._instance = instance
+
+ # One second per retry
+ self.RETRIES_ON_ERROR = 300
+ self._testrun_soc_retries = 0
+ self._last_counters_check = 0
+
+ # Two different starttime values.
+ # starttime_fb is set by the modules (relay.py, genset.py) and will be set to the current time when
+ # the feedback mechanism (digital input / `/StatusCode`) indicates that the generator is running.
+ # The other one is set by startstop when commanding the module to start the generator and is used by the
+ # warm-up mechanism to ensure warm-up finishes without needing feedback that the generator has actually started.
+ # If there is no feedback mechanism in place, the values will be equal as the module will call '_generator_started()`
+ # right after receiving the start command from startstop.
+ self._starttime_fb = 0 # Starttime of the generator as reported by the feedback mechanism (e.g., digital input), if present
+ self._starttime = 0 # Starttime of the generator, maintained by startstop. Not influenced by feedback mechanism.
+ self._stoptime = 0 # Used for cooldown
+ self._manualstarttimer = 0
+ self._last_runtime_update = 0
+ self._timer_runnning = 0
+
+ # The installer left autostart disabled
+ self._autostart_last_time = self._currentTime
+ self._remote_start_mode_last_time = self._currentTime
+
+
+ # Manual battery service selection is deprecated in favour
+ # of getting the values directly from systemcalc, we keep
+ # manual selected services handling for compatibility reasons.
+ self._vebusservice = None
+ self._acsystemservice = None
+ self._errorstate = 0
+ self._battery_service = None
+ self._battery_prefix = None
+
+ self._power_input_timer = {
+ 'timeout': 0,
+ 'unabletostart': False
+ }
+
+ # Order is important. Conditions are evaluated in the order listed.
+ self._condition_stack = OrderedDict({
+ SocCondition.name: SocCondition(self),
+ AcLoadCondition.name: AcLoadCondition(self),
+ BatteryCurrentCondition.name: BatteryCurrentCondition(self),
+ BatteryVoltageCondition.name: BatteryVoltageCondition(self),
+ InverterTempCondition.name: InverterTempCondition(self),
+ InverterOverloadCondition.name: InverterOverloadCondition(self),
+ StopOnAc1Condition.name: StopOnAc1Condition(self),
+ StopOnAc2Condition.name: StopOnAc2Condition(self)
+ })
+
+ # Return the active vebusservice or the acsystemservice.
+ @property
+ def multiservice(self):
+ return self._vebusservice if self._vebusservice else self._acsystemservice if self._acsystemservice else None
+
+ @property
+ def multiservice_type(self):
+ return self.multiservice.split('.')[2] if self.multiservice is not None else None
+
+ def set_sources(self, dbusmonitor, settings, name, remoteservice):
+ self._settings = SettingsPrefix(settings, name)
+ self._dbusmonitor = dbusmonitor
+ self._remoteservice = remoteservice
+ self._name = name
+
+ self.log_info('Start/stop instance created for %s.' % self._remoteservice)
+ self._remote_setup()
+
+ def _create_service(self):
+ self._dbusservice = self._create_dbus_service()
+
+ # The driver used for this start/stop service
+ self._dbusservice.add_path('/Type', value=self._driver)
+ # State: None = invalid, 0 = stopped, 1 = running, 2=Warm-up, 3=Cool-down
+ self._dbusservice.add_path('/State', value=None, gettextcallback=lambda p, v: States.get_description(v))
+ self._dbusservice.add_path('/Enabled', value=1)
+ # RunningByConditionCode: Numeric Companion to /RunningByCondition below, but
+ # also encompassing a Stopped state.
+ self._dbusservice.add_path('/RunningByConditionCode', value=None)
+ # Error
+ self._dbusservice.add_path('/Error', value=None, gettextcallback=lambda p, v: Errors.get_description(v))
+ # Condition that made the generator start
+ self._dbusservice.add_path('/RunningByCondition', value=None)
+ # Runtime
+ self._dbusservice.add_path('/Runtime', value=None, gettextcallback=self._seconds_to_text)
+ # Today runtime
+ self._dbusservice.add_path('/TodayRuntime', value=None, gettextcallback=self._seconds_to_text)
+ # Test run runtime
+ self._dbusservice.add_path('/TestRunIntervalRuntime', value=None , gettextcallback=self._seconds_to_text)
+ # Next test run date, values is 0 for test run disabled
+ self._dbusservice.add_path('/NextTestRun', value=None, gettextcallback=lambda p, v: datetime.datetime.fromtimestamp(v).strftime('%c'))
+ # Next test run is needed 1, not needed 0
+ self._dbusservice.add_path('/SkipTestRun', value=None)
+ # Manual start
+ self._dbusservice.add_path('/ManualStart', value=None, writeable=True)
+ # Manual start timer
+ self._dbusservice.add_path('/ManualStartTimer', value=None, writeable=True)
+ # Silent mode active
+ self._dbusservice.add_path('/QuietHours', value=None)
+ # Alarms
+ self._dbusservice.add_path('/Alarms/NoGeneratorAtAcIn', value=None)
+ self._dbusservice.add_path('/Alarms/NoGeneratorAtDcIn', value=None)
+ self._dbusservice.add_path('/Alarms/ServiceIntervalExceeded', value=None)
+ self._dbusservice.add_path('/Alarms/AutoStartDisabled', value=None)
+ self._dbusservice.add_path('/Alarms/RemoteStartModeDisabled', value=None)
+ # Autostart
+ self._dbusservice.add_path('/AutoStartEnabled', value=None, writeable=True, onchangecallback=self._set_autostart)
+ # Accumulated runtime
+ self._dbusservice.add_path('/AccumulatedRuntime', value=None)
+ # Service interval
+ self._dbusservice.add_path('/ServiceInterval', value=None)
+ # Capabilities, where we can add bits
+ self._dbusservice.add_path('/Capabilities', value=0)
+ # Service countdown, calculated by running time and service interval
+ self._dbusservice.add_path('/ServiceCounter', value=None)
+ self._dbusservice.add_path('/ServiceCounterReset', value=None, writeable=True, onchangecallback=self._reset_service_counter)
+ # Publish what service we're controlling, and the productid
+ self._dbusservice.add_path('/GensetService', value=self._remoteservice)
+ self._dbusservice.add_path('/GensetServiceType',
+ value=self._remoteservice.split('.')[2] if self._remoteservice is not None else None)
+ self._dbusservice.add_path('/GensetInstance',
+ value=self._dbusmonitor.get_value(self._remoteservice, '/DeviceInstance'))
+ self._dbusservice.add_path('/GensetProductId',
+ value=self._dbusmonitor.get_value(self._remoteservice, '/ProductId'))
+ self._dbusservice.add_path('/DigitalInput/Running', value=None, writeable=True, onchangecallback=self._running_by_digital_input)
+ self._dbusservice.add_path('/DigitalInput/Input', value=None, writeable=True, onchangecallback=self._running_by_digital_input)
+
+ self._dbusservice.register()
+ # We need to set the values after creating the paths to trigger the 'onValueChanged' event for the gui
+ # otherwise the gui will report the paths as invalid if we remove and recreate the paths without
+ # restarting the dbusservice.
+ self._dbusservice['/State'] = 0
+ self._dbusservice['/RunningByConditionCode'] = RunningConditions.Stopped
+ self._dbusservice['/Error'] = 0
+ self._dbusservice['/RunningByCondition'] = ''
+ self._dbusservice['/Runtime'] = 0
+ self._dbusservice['/TodayRuntime'] = 0
+ self._dbusservice['/TestRunIntervalRuntime'] = self._interval_runtime(self._settings['testruninterval'])
+ self._dbusservice['/NextTestRun'] = None
+ self._dbusservice['/SkipTestRun'] = None
+ self._dbusservice['/ProductName'] = "Generator start/stop"
+ self._dbusservice['/ManualStart'] = 0
+ self._dbusservice['/ManualStartTimer'] = 0
+ self._dbusservice['/QuietHours'] = 0
+ self._dbusservice['/Alarms/NoGeneratorAtAcIn'] = 0
+ self._dbusservice['/Alarms/ServiceIntervalExceeded'] = 0
+ self._dbusservice['/Alarms/AutoStartDisabled'] = 0 # GX auto start/stop
+ self._dbusservice['/Alarms/RemoteStartModeDisabled'] = 0 # Genset remote start mode
+ self._dbusservice['/AutoStartEnabled'] = self._settings['autostart']
+ self._dbusservice['/AccumulatedRuntime'] = int(self._settings['accumulatedtotal'])
+ self._dbusservice['/ServiceInterval'] = int(self._settings['serviceinterval'])
+ self._dbusservice['/ServiceCounter'] = None
+ self._dbusservice['/ServiceCounterReset'] = 0
+ self._dbusservice['/DigitalInput/Running'] = 0
+ self._dbusservice['/DigitalInput/Input'] = 0
+
+ # When this startstop instance controls a genset which reports operatinghours, make sure to synchronize with that.
+ self._useGensetHours = self._dbusmonitor.get_value(self._remoteservice, '/Engine/OperatingHours', None) is not None
+
+#### GuiMods
+ # generator input running state
+ # external override active
+ self._dbusservice.add_path('/ExternalOverride', value=None)
+ self._dbusservice['/ExternalOverride'] = False
+ self._ignoreAutoStartCondition = False
+
+
+ @property
+ def _is_running(self):
+ return self._generator_running
+
+ @property
+ def capabilities(self):
+ return self._dbusservice['/Capabilities']
+
+ def _set_autostart(self, path, value):
+ if 0 <= value <= 1:
+ self._settings['autostart'] = int(value)
+ return True
+ return False
+
+ def enable(self):
+ if self._enabled:
+ return
+ self.log_info('Enabling auto start/stop and taking control of remote switch')
+ self._create_service()
+ self._determineservices()
+ self._update_remote_switch()
+ # If cooldown or warmup is enabled, the Quattro may be left in a bad
+ # state if there is an unfortunate crash or a reboot. Set the ignore_ac
+ # flag to a sane value on startup.
+ if self._settings['cooldowntime'] > 0 or \
+ self._settings['warmuptime'] > 0:
+ self._set_ignore_ac(False)
+ self._enabled = True
+
+ def disable(self):
+ if not self._enabled:
+ return
+ self.log_info('Disabling auto start/stop, releasing control of remote switch')
+ self._remove_service()
+ self._enabled = False
+
+ def remove(self):
+ self.disable()
+ self.log_info('Removed from start/stop instances')
+
+ def _remove_service(self):
+ self._dbusservice.__del__()
+ self._dbusservice = None
+
+ def device_added(self, dbusservicename, instance):
+ self._determineservices()
+
+ def device_removed(self, dbusservicename, instance):
+ self._determineservices()
+
+ def get_error(self):
+ return self._dbusservice['/Error']
+
+ def set_error(self, errorn):
+ self._dbusservice['/Error'] = errorn
+
+ def clear_error(self):
+ self._dbusservice['/Error'] = Errors.NONE
+
+ def dbus_value_changed(self, dbusServiceName, dbusPath, options, changes, deviceInstance):
+ if self._dbusservice is None:
+ return
+
+ # AcIn1Available is needed to determine capabilities, but may
+ # only show up later. So we have to wait for it here.
+ if self.multiservice is not None and \
+ dbusServiceName == self.multiservice and \
+ dbusPath == '/Ac/State/AcIn1Available' or dbusPath == '/Ac/Control/IgnoreAcIn1':
+ self._set_capabilities()
+
+ # If gensethours is updated, update the accumulated time.
+ if dbusPath == '/Engine/OperatingHours' and self._useGensetHours:
+ self._update_accumulated_time(gensetHours=changes['Value'])
+
+ if dbusServiceName != 'com.victronenergy.system':
+ return
+ if dbusPath == '/AutoSelectedBatteryMeasurement' and self._settings['batterymeasurement'] == 'default':
+ self._determineservices()
+
+ if dbusPath == '/VebusService':
+ self._determineservices()
+
+ def handlechangedsetting(self, setting, oldvalue, newvalue):
+ if self._dbusservice is None:
+ return
+ if self._name not in setting:
+ # Not our setting
+ return
+
+ s = self._settings.removeprefix(setting)
+
+ if s == 'batterymeasurement':
+ self._determineservices()
+ # Reset retries and valid if service changes
+ for condition in self._condition_stack.values():
+ if condition['monitoring'] == 'battery':
+ condition['valid'] = True
+ condition['retries'] = 0
+
+ if s == 'autostart':
+ self.log_info('Autostart function %s.' % ('enabled' if newvalue == 1 else 'disabled'))
+ self._dbusservice['/AutoStartEnabled'] = self._settings['autostart']
+
+ if self._dbusservice is not None and s == 'testruninterval':
+ self._dbusservice['/TestRunIntervalRuntime'] = self._interval_runtime(
+ self._settings['testruninterval'])
+
+ if s == 'serviceinterval':
+ try:
+ self._dbusservice['/ServiceInterval'] = int(newvalue)
+ except TypeError:
+ pass
+ if newvalue == 0:
+ self._dbusservice['/ServiceCounter'] = None
+ else:
+ self._update_accumulated_time()
+ if s == 'lastservicereset':
+ self._update_accumulated_time()
+
+ def _reset_service_counter(self, path, value):
+ if (path == '/ServiceCounterReset' and value == int(1) and self._dbusservice['/AccumulatedRuntime']):
+ self._settings['lastservicereset'] = self._dbusservice['/AccumulatedRuntime']
+ self._update_accumulated_time()
+ self.log_info('Service counter reset triggered.')
+
+ return True
+
+ def _seconds_to_text(self, path, value):
+ m, s = divmod(value, 60)
+ h, m = divmod(m, 60)
+ return '%dh, %dm, %ds' % (h, m, s)
+
+ def log_info(self, msg):
+ logging.info(self._name + ': %s' % msg)
+
+ def tick(self):
+ if not self._enabled:
+ return
+
+#### GuiMods warm-up / cool-down
+ self._currentTime = self._get_monotonic_seconds ()
+
+ self._check_remote_status()
+#### GuiMods
+ self._linkToExternalState = self._settings['linkManualStartToExternal'] == 1
+ self.syncManualRunToExternalState ()
+
+ self._evaluate_startstop_conditions()
+ self._evaluate_autostart_disabled_alarm()
+ self._detect_generator_at_input()
+ if self._dbusservice['/ServiceCounterReset'] == 1:
+ self._dbusservice['/ServiceCounterReset'] = 0
+
+#### GuiMods warm-up / cool-down
+
+ # shed load for active generator input in warm-up and cool-down
+ # note that external transfer switch might change the state of on generator
+ # so this needs to be checked and load adjusted every pass
+ # restore load for sources no longer in use or if state is not in warm-up/cool-down
+ # restoring load is delayed 1following end of cool-down
+ # to allow the generator to actually stop producing power
+ state = self._dbusservice['/State']
+ if state in (States.WARMUP, States.COOLDOWN, States.STOPPING):
+ self._set_ignore_ac (True)
+ else:
+ self._set_ignore_ac (False)
+
+ # update cool down end time while running and generator has the load
+ # this is done because acInIsGenerator may change by an external transfer switch
+ # and we want an accurate picture of the cool down end time
+ # based on the last time the generatot was loaded
+ if state == States.RUNNING and self._acInIsGenerator:
+ self._coolDownEndTime = self._currentTime + self._settings['cooldowntime']
+#### end GuiMods warm-up / cool-down
+
+
+ def _evaluate_startstop_conditions(self):
+ if self.get_error() != Errors.NONE:
+ # First evaluation after an error, log it
+ if self._errorstate == 0:
+ self._errorstate = 1
+ self._dbusservice['/State'] = States.ERROR
+ self.log_info('Error: #%i - %s, stop controlling remote.' %
+ (self.get_error(),
+ Errors.get_description(self.get_error())))
+ elif self._errorstate == 1:
+ # Error cleared
+ self._errorstate = 0
+ self._dbusservice['/State'] = States.STOPPED
+ self.log_info('Error state cleared, taking control of remote switch.')
+
+ start = False
+ startbycondition = None
+ activecondition = self._dbusservice['/RunningByCondition']
+ today = calendar.timegm(datetime.date.today().timetuple())
+ self._timer_runnning = False
+ connection_lost = False
+ running = self._dbusservice['/State'] in (States.RUNNING, States.WARMUP)
+
+ self._check_quiet_hours()
+
+ # New day, register it
+ if self._last_counters_check < today and self._dbusservice['/State'] == States.STOPPED:
+ self._last_counters_check = today
+ self._update_accumulated_time()
+
+ self._update_runtime()
+
+#### GuiMods
+ # A negative /ManualStartTimer is used by the GUI to signal the generator should start now
+ # but stop when all auto stop conditions have been met
+ # so we skip manual start evaluation if this is the case
+ # and set a flag for use below to ignore auto start conditions
+ # the generator is actually started by the auto start/stop logic below
+ if self._dbusservice['/ManualStartTimer'] < 0 and self._dbusservice['/ManualStart'] == 1:
+ self._dbusservice['/ManualStartTimer'] = 0
+ self._dbusservice['/ManualStart'] = 0
+ self._ignoreAutoStartCondition = True
+
+ else:
+ self._ignoreAutoStartCondition = False
+ if self._evaluate_manual_start():
+ startbycondition = 'manual'
+ start = True
+#### end GuiMods
+
+ # Conditions will only be evaluated if the autostart functionality is enabled
+ if self._settings['autostart'] == 1:
+
+ if self._evaluate_testrun_condition():
+ startbycondition = 'testrun'
+ start = True
+
+ # Evaluate stop on AC IN conditions first, when this conditions are enabled and reached the generator
+ # will stop as soon as AC IN in active. Manual and testrun conditions will make the generator start
+ # or keep it running.
+ stop_on_ac_reached = (self._evaluate_condition(self._condition_stack[StopOnAc1Condition.name]) or
+ self._evaluate_condition(self._condition_stack[StopOnAc2Condition.name]))
+ stop_by_ac1_ac2 = startbycondition not in ['manual', 'testrun'] and stop_on_ac_reached
+
+ if stop_by_ac1_ac2 and running and activecondition not in ['manual', 'testrun']:
+ self.log_info('AC input available, stopping')
+
+ # Evaluate value conditions
+ for condition, data in self._condition_stack.items():
+ # Do not evaluate rest of conditions if generator is configured to stop
+ # when AC IN is available
+ if stop_by_ac1_ac2:
+ start = False
+ if running:
+ self._reset_condition(data)
+ continue
+ else:
+ break
+
+ # Don't short-circuit this, _evaluate_condition sets .reached
+ start = self._evaluate_condition(data) or start
+ startbycondition = condition if start and startbycondition is None else startbycondition
+ # Connection lost is set to true if the number of retries of one or more enabled conditions
+ # >= RETRIES_ON_ERROR
+ if data.enabled:
+ connection_lost = data.retries >= self.RETRIES_ON_ERROR
+
+ # If none condition is reached check if connection is lost and start/keep running the generator
+ # depending on '/OnLossCommunication' setting
+ if not start and connection_lost:
+ # Start always
+ if self._settings['onlosscommunication'] == 1:
+ start = True
+ startbycondition = 'lossofcommunication'
+ # Keep running if generator already started
+ if running and self._settings['onlosscommunication'] == 2:
+ start = True
+ startbycondition = 'lossofcommunication'
+
+#### GuiMods
+ ## auto start disabled and generator is stopped - clear the 'reached' flags
+ elif self._dbusservice['/State'] == States.STOPPED:
+ for condition, data in self._condition_stack.items():
+ self._reset_condition(data)
+
+ if not start and self._errorstate:
+ self._stop_generator()
+
+ if self._errorstate:
+ return
+
+ if start:
+ self._start_generator(startbycondition)
+#### GuiMods
+ # bypass the minimum run time check if External Override is active
+ elif (self._dbusservice['/Runtime'] >= self._settings['minimumruntime'] * 60
+ or activecondition == 'manual') or self._dbusservice['/ExternalOverride']:
+ self._stop_generator()
+#### end GuiMods
+
+ def _update_runtime(self, just_stopped=False):
+ # Update current and accumulated runtime.
+ # By performance reasons, accumulated runtime is only updated
+ # once per 60s. When the generator stops is also updated.
+ if self._is_running or just_stopped:
+ mtime = monotonic_time.monotonic_time().to_seconds_double()
+ if (mtime - self._starttime_fb) - self._last_runtime_update >= 60 or just_stopped:
+ self._dbusservice['/Runtime'] = int(mtime - self._starttime_fb)
+ self._update_accumulated_time()
+ elif self._last_runtime_update == 0:
+ self._dbusservice['/Runtime'] = int(mtime - self._starttime_fb)
+
+ def _evaluate_autostart_disabled_alarm(self):
+
+ if self._settings['autostartdisabledalarm'] == 0:
+ self._autostart_last_time = self._currentTime
+ self._remote_start_mode_last_time = self._currentTime
+ if self._dbusservice['/Alarms/AutoStartDisabled'] != 0:
+ self._dbusservice['/Alarms/AutoStartDisabled'] = 0
+ if self._dbusservice['/Alarms/RemoteStartModeDisabled'] != 0:
+ self._dbusservice['/Alarms/RemoteStartModeDisabled'] = 0
+ return
+
+ # GX auto start/stop alarm
+ if self._settings['autostart'] == 1:
+ self._autostart_last_time = self._currentTime
+ if self._dbusservice['/Alarms/AutoStartDisabled'] != 0:
+ self._dbusservice['/Alarms/AutoStartDisabled'] = 0
+ else:
+ timedisabled = self._currentTime - self._autostart_last_time
+ if timedisabled > AUTOSTART_DISABLED_ALARM_TIME and self._dbusservice['/Alarms/AutoStartDisabled'] != 2:
+ self.log_info("Autostart was left for more than %i seconds, triggering alarm." % int(timedisabled))
+ self._dbusservice['/Alarms/AutoStartDisabled'] = 2
+
+ # Genset remote start mode alarm
+ if self.get_error() != Errors.REMOTEDISABLED:
+ self._remote_start_mode_last_time = self._currentTime
+ if self._dbusservice['/Alarms/RemoteStartModeDisabled'] != 0:
+ self._dbusservice['/Alarms/RemoteStartModeDisabled'] = 0
+ else:
+ timedisabled = self._currentTime - self._remote_start_mode_last_time
+ if timedisabled > AUTOSTART_DISABLED_ALARM_TIME and self._dbusservice['/Alarms/RemoteStartModeDisabled'] != 2:
+ self.log_info("Autostart was left for more than %i seconds, triggering alarm." % int(timedisabled))
+ self._dbusservice['/Alarms/RemoteStartModeDisabled'] = 2
+
+#### GuiMods warm-up / cool-down - rewrote so acInIsGenerator is updated even if alarm is disabled
+ def _detect_generator_at_input(self):
+ self._acInIsGenerator = False # covers all conditions that result in a return
+
+ state = self._dbusservice['/State']
+ if state in [States.STOPPED, States.COOLDOWN, States.WARMUP]:
+ self._reset_power_input_timer()
+ return
+
+ if self._settings['nogeneratoratacinalarm'] == 0:
+ self._reset_acpower_inverter_input()
+ return
+
+ if self.multiservice_type == 'vebus':
+ activein_state = self._dbusmonitor.get_value(
+ self.multiservice, '/Ac/ActiveIn/Connected')
+ else:
+ active_input = self._dbusmonitor.get_value(
+ self.multiservice, '/Ac/ActiveIn/ActiveInput')
+ activein_state = None if active_input is None else 1 if 0 <= active_input <= 1 else 0
+
+ # Path not supported, skip evaluation
+ if activein_state == None:
+ return
+
+ # Sources 0 = Not available, 1 = Grid, 2 = Generator, 3 = Shore
+ generator_acsource = self._dbusmonitor.get_value(
+ SYSTEM_SERVICE, '/Ac/ActiveIn/Source') == 2
+ # Not connected = 0, connected = 1
+ activein_connected = activein_state == 1
+
+#### GuiMods warm-up / cool-down
+ if self._settings['nogeneratoratacinalarm'] == 0:
+ processAlarm = False
+ self._reset_acpower_inverter_input()
+ else:
+ processAlarm = True
+
+ if generator_acsource and activein_connected:
+#### GuiMods warm-up / cool-down
+ self._acInIsGenerator = True
+#### GuiMods warm-up / cool-down
+ if processAlarm and self._power_input_timer['unabletostart']:
+ self.log_info('Generator detected at inverter AC input, alarm removed')
+ self._reset_power_inverter_input()
+#### GuiMods warm-up / cool-down
+ elif not processAlarm:
+ self._reset_power_inverter_input()
+ return
+ elif self._aower_inverter_input['timeout'] < self.RETRIES_ON_ERROR:
+ self._power_inverter_input['timeout'] += 1
+ elif not self._power_inverter_input['unabletostart']:
+ self._power_inverter_input['unabletostart'] = True
+ self._dbusservice['/Alarms/NoGeneratorAtAcIn'] = 2
+ self.log_info('Generator not detected at inverter AC input, triggering alarm')
+#### end GuiMods
+
+ def _reset_power_input_timer(self):
+ if self._acpower_inverter_input['timeout'] != 0:
+ self._acpower_inverter_input['timeout'] = 0
+
+ if self._acpower_inverter_input['unabletostart'] != 0:
+ self._acpower_inverter_input['unabletostart'] = 0
+
+ self._dbusservice['/Alarms/NoGeneratorAtAcIn'] = 0
+
+ def _reset_condition(self, condition):
+ condition['reached'] = False
+ if condition['timed']:
+ condition['start_timer'] = 0
+ condition['stop_timer'] = 0
+
+ def _check_condition(self, condition, value):
+ name = condition['name']
+
+ if self._settings[name + 'enabled'] == 0:
+ if condition['enabled']:
+ condition['enabled'] = False
+ self.log_info('Disabling (%s) condition' % name)
+ condition['retries'] = 0
+ condition['valid'] = True
+ self._reset_condition(condition)
+ return False
+
+ elif not condition['enabled']:
+ condition['enabled'] = True
+ self.log_info('Enabling (%s) condition' % name)
+
+ if (condition['monitoring'] == 'battery') and (self._settings['batterymeasurement'] == 'nobattery'):
+ # If no battery monitor is selected reset the condition
+ self._reset_condition(condition)
+ return False
+
+ if value is None and condition['valid']:
+ if condition['retries'] >= self.RETRIES_ON_ERROR:
+ logging.info('Error getting (%s) value, skipping evaluation till get a valid value' % name)
+ self._reset_condition(condition)
+ self._comunnication_lost = True
+ condition['valid'] = False
+ else:
+ condition['retries'] += 1
+ if condition['retries'] == 1 or (condition['retries'] % 10) == 0:
+ self.log_info('Error getting (%s) value, retrying(#%i)' % (name, condition['retries']))
+ return False
+
+ elif value is not None and not condition['valid']:
+ self.log_info('Success getting (%s) value, resuming evaluation' % name)
+ condition['valid'] = True
+ condition['retries'] = 0
+
+ # Reset retries if value is valid
+ if value is not None and condition['retries'] > 0:
+ self.log_info('Success getting (%s) value, resuming evaluation' % name)
+ condition['retries'] = 0
+
+ return condition['valid']
+
+ def _evaluate_condition(self, condition):
+ name = condition['name']
+ value = condition.get_value()
+ setting = ('qh_' if self._dbusservice['/QuietHours'] == 1 else '') + name
+ startvalue = self._settings[setting + 'start'] if not condition['boolean'] else 1
+ stopvalue = self._settings[setting + 'stop'] if not condition['boolean'] else 0
+
+ # Check if the condition has to be evaluated
+ if not self._check_condition(condition, value):
+ # If generator is started by this condition and value is invalid
+ # wait till RETRIES_ON_ERROR to skip the condition
+ if condition['reached'] and condition['retries'] <= self.RETRIES_ON_ERROR:
+ if condition['retries'] > 0:
+ return True
+
+ return False
+
+ # As this is a generic evaluation method, we need to know how to compare the values
+ # first check if start value should be greater than stop value and then compare
+ start_is_greater = startvalue > stopvalue
+
+ # When the condition is already reached only the stop value can set it to False
+ start = condition['reached'] or (value >= startvalue if start_is_greater else value <= startvalue)
+ stop = value <= stopvalue if start_is_greater else value >= stopvalue
+
+#### GuiMods
+ stop = value <= stopvalue if start_is_greater else value >= stopvalue
+ # when starting manually and stopping based on auto stop values,
+ # start if stop condition is not satisfied
+
+ if self._ignoreAutoStartCondition:
+ start = not stop
+ else:
+ # When the condition is already reached only the stop value can set it to False
+ start = condition['reached'] or (value >= startvalue if start_is_greater else value <= startvalue)
+#### end GuiMods
+
+ # Timed conditions must start/stop after the condition has been reached for a minimum
+ # time.
+ if condition['timed']:
+ if not condition['reached'] and start:
+ condition['start_timer'] += time.time() if condition['start_timer'] == 0 else 0
+ start = time.time() - condition['start_timer'] >= self._settings[name + 'starttimer']
+ condition['stop_timer'] *= int(not start)
+ self._timer_runnning = True
+ else:
+ condition['start_timer'] = 0
+
+ if condition['reached'] and stop:
+ condition['stop_timer'] += time.time() if condition['stop_timer'] == 0 else 0
+ stop = time.time() - condition['stop_timer'] >= self._settings[name + 'stoptimer']
+ condition['stop_timer'] *= int(not stop)
+ self._timer_runnning = True
+ else:
+ condition['stop_timer'] = 0
+
+ condition['reached'] = start and not stop
+ return condition['reached']
+
+ def _evaluate_manual_start(self):
+ if self._dbusservice['/ManualStart'] == 0:
+ if self._dbusservice['/RunningByCondition'] == 'manual':
+ self._dbusservice['/ManualStartTimer'] = 0
+ return False
+
+ start = True
+ # If /ManualStartTimer has a value greater than zero will use it to set a stop timer.
+ # If no timer is set, the generator will not stop until the user stops it manually.
+ # Once started by manual start, each evaluation the timer is decreased
+#### GuiMods - change test to > 0 from != 0 to allow for start now / auto stop
+ if self._dbusservice['/ManualStartTimer'] > 0:
+ self._manualstarttimer += time.time() if self._manualstarttimer == 0 else 0
+ self._dbusservice['/ManualStartTimer'] -= int(time.time()) - int(self._manualstarttimer)
+ self._manualstarttimer = time.time()
+ start = self._dbusservice['/ManualStartTimer'] > 0
+ self._dbusservice['/ManualStart'] = int(start)
+ # Reset if timer is finished
+ self._manualstarttimer *= int(start)
+ self._dbusservice['/ManualStartTimer'] *= int(start)
+
+ return start
+
+ def _evaluate_testrun_condition(self):
+ if self._settings['testrunenabled'] == 0:
+ self._dbusservice['/SkipTestRun'] = None
+ self._dbusservice['/NextTestRun'] = None
+ return False
+
+ today = datetime.date.today()
+ yesterday = today - datetime.timedelta(days=1) # Should deal well with DST
+ now = time.time()
+ runtillbatteryfull = self._settings['testruntillbatteryfull'] == 1
+ soc = self._condition_stack['soc'].get_value()
+ batteryisfull = runtillbatteryfull and soc == 100
+ duration = 60 if runtillbatteryfull else self._settings['testrunruntime']
+
+ try:
+ startdate = datetime.date.fromtimestamp(self._settings['testrunstartdate'])
+ _starttime = time.mktime(yesterday.timetuple()) + self._settings['testrunstarttimer']
+
+ # today might in fact still be yesterday, if this test run started
+ # before midnight and finishes after. If `now` still falls in
+ # yesterday's window, then by the temporal anthropic principle,
+ # which I just made up but loosely states that time must have
+ # these properties for observers to exist, it must be yesterday
+ # because we are here to observe it.
+ if _starttime <= now <= _starttime + duration:
+ today = yesterday
+ starttime = _starttime
+ else:
+ starttime = time.mktime(today.timetuple()) + self._settings['testrunstarttimer']
+ except ValueError:
+ logging.debug('Invalid dates, skipping testrun')
+ return False
+
+ # If start date is in the future set as NextTestRun and stop evaluating
+ if startdate > today:
+ self._dbusservice['/NextTestRun'] = time.mktime(startdate.timetuple())
+ return False
+
+ start = False
+ # If the accumulated runtime during the test run interval is greater than '/TestRunIntervalRuntime'
+ # the test run must be skipped
+ needed = (self._settings['testrunskipruntime'] > self._dbusservice['/TestRunIntervalRuntime']
+ or self._settings['testrunskipruntime'] == 0)
+ self._dbusservice['/SkipTestRun'] = int(not needed)
+
+ interval = self._settings['testruninterval']
+ stoptime = starttime + duration
+ elapseddays = (today - startdate).days
+ mod = elapseddays % interval
+
+ start = not bool(mod) and starttime <= now <= stoptime
+
+ if runtillbatteryfull:
+ if soc is not None:
+ self._testrun_soc_retries = 0
+ start = (start or self._dbusservice['/RunningByCondition'] == 'testrun') and not batteryisfull
+ elif self._dbusservice['/RunningByCondition'] == 'testrun':
+ if self._testrun_soc_retries < self.RETRIES_ON_ERROR:
+ self._testrun_soc_retries += 1
+ start = True
+ if (self._testrun_soc_retries % 10) == 0:
+ self.log_info('Test run failed to get SOC value, retrying(#%i)' % self._testrun_soc_retries)
+ else:
+ self.log_info('Failed to get SOC after %i retries, terminating test run condition' % self._testrun_soc_retries)
+ start = False
+ else:
+ start = False
+
+ if not bool(mod) and (now <= stoptime):
+ self._dbusservice['/NextTestRun'] = starttime
+ else:
+ self._dbusservice['/NextTestRun'] = (time.mktime((today + datetime.timedelta(days=interval - mod)).timetuple()) +
+ self._settings['testrunstarttimer'])
+ return start and needed
+
+ def _check_quiet_hours(self):
+ active = False
+ if self._settings['quiethoursenabled'] == 1:
+ # Seconds after today 00:00
+ timeinseconds = time.time() - time.mktime(datetime.date.today().timetuple())
+ quiethoursstart = self._settings['quiethoursstarttime']
+ quiethoursend = self._settings['quiethoursendtime']
+
+ # Check if the current time is between the start time and end time
+ if quiethoursstart < quiethoursend:
+ active = quiethoursstart <= timeinseconds and timeinseconds < quiethoursend
+ else: # End time is lower than start time, example Start: 21:00, end: 08:00
+ active = not (quiethoursend < timeinseconds and timeinseconds < quiethoursstart)
+
+ if self._dbusservice['/QuietHours'] == 0 and active:
+ self.log_info('Entering to quiet mode')
+
+ elif self._dbusservice['/QuietHours'] == 1 and not active:
+ self.log_info('Leaving quiet mode')
+
+ self._dbusservice['/QuietHours'] = int(active)
+
+ return active
+
+ def _update_accumulated_time(self, gensetHours=None):
+
+ # Check if this instance is connected to a genset which reports operating hours.
+ # If so, synchronize with that.
+ if (self._useGensetHours):
+ if (gensetHours is None):
+ gensetHours = self._dbusmonitor.get_value(self._remoteservice, '/Engine/OperatingHours', None)
+
+ # Failsafe
+ if (gensetHours is not None):
+ # If connected genset reports /Engine/OperatingHours, use that and also clear the offset value.
+ self._settings['accumulatedtotalOffset'] = 0
+ self._settings['accumulatedtotal'] = accumulatedtotal = gensetHours
+ else:
+ # Do not use genset hours
+ gensetHours = None
+
+ seconds = self._dbusservice['/Runtime']
+ accumulated = seconds - self._last_runtime_update
+
+ self._settings['accumulatedtotal'] = accumulatedtotal = gensetHours or int(self._settings['accumulatedtotal']) + accumulated
+ # Using calendar to get timestamp in UTC, not local time
+ today_date = str(calendar.timegm(datetime.date.today().timetuple()))
+
+ # If something goes wrong getting the json string create a new one
+ try:
+ accumulated_days = json.loads(self._settings['accumulateddaily'])
+ except ValueError:
+ accumulated_days = {today_date: 0}
+
+ if (today_date in accumulated_days):
+ accumulated_days[today_date] += accumulated
+ else:
+ accumulated_days[today_date] = accumulated
+
+ if self._dbusservice['/State'] in (States.RUNNING, States.WARMUP, States.COOLDOWN, States.STOPPING):
+ mtime = monotonic_time.monotonic_time().to_seconds_double()
+ self._dbusservice['/Runtime'] = int(mtime - self._starttime_fb)
+
+ self._last_runtime_update = seconds
+
+ # Keep the historical with a maximum of HISTORY_DAYS
+ while len(accumulated_days) > HISTORY_DAYS:
+ accumulated_days.pop(min(accumulated_days.keys()), None)
+
+ # Update settings
+ self._settings['accumulateddaily'] = json.dumps(accumulated_days, sort_keys=True)
+ self._dbusservice['/TodayRuntime'] = self._interval_runtime(0)
+ self._dbusservice['/TestRunIntervalRuntime'] = self._interval_runtime(self._settings['testruninterval'])
+ self._dbusservice['/AccumulatedRuntime'] = accumulatedtotal
+
+ # Service counter
+ serviceinterval = self._settings['serviceinterval']
+ lastservicereset = self._settings['lastservicereset']
+ if serviceinterval > 0:
+ servicecountdown = (lastservicereset + serviceinterval) - accumulatedtotal
+ self._dbusservice['/ServiceCounter'] = servicecountdown
+ if servicecountdown <= 0:
+ self._dbusservice['/Alarms/ServiceIntervalExceeded'] = 1
+ elif self._dbusservice['/Alarms/ServiceIntervalExceeded'] != 0:
+ self._dbusservice['/Alarms/ServiceIntervalExceeded'] = 0
+
+
+
+ def _interval_runtime(self, days):
+ summ = 0
+ try:
+ daily_record = json.loads(self._settings['accumulateddaily'])
+ except ValueError:
+ return 0
+
+ for i in range(days + 1):
+ previous_day = calendar.timegm((datetime.date.today() - datetime.timedelta(days=i)).timetuple())
+ if str(previous_day) in daily_record.keys():
+ summ += daily_record[str(previous_day)] if str(previous_day) in daily_record.keys() else 0
+
+ return summ
+
+ def _get_battery(self):
+ if self._settings['batterymeasurement'] == 'default':
+ return Battery(self._dbusmonitor, SYSTEM_SERVICE, BATTERY_PREFIX)
+
+ return Battery(self._dbusmonitor,
+ self._battery_service if self._battery_service else '',
+ self._battery_prefix if self._battery_prefix else '')
+
+ def _set_capabilities(self):
+ # Update capabilities
+ # The ability to ignore AC1/AC2 came in at the same time as
+ # AC availability and is used to detect it here.
+ if self.multiservice_type == 'vebus':
+ readout_supported = self._dbusmonitor.get_value(self.multiservice,
+ '/Ac/State/AcIn1Available') is not None
+ self._dbusservice['/Capabilities'] |= (
+ Capabilities.WarmupCooldown if readout_supported else 0)
+ # /Ac/State/AcIn1Available is not available on acsystem
+ # The vebus system also has the path '/Ac/Control/IgnoreAcIn1', but we can't use this to check for the
+ # capabilities because this path was already there before ignoring ac input 2 was supported in the vebus quattro.
+ # So if we were to use '/Ac/Control/IgnoreAcIn1' to set the capabilities on a vebus system, it would
+ # also be enabled on systems with older firmware which do not support it on input 2.
+ elif self.multiservice_type == 'acsystem':
+ ignore_ac_supported = self._dbusmonitor.get_value(self.multiservice,
+ '/Ac/Control/IgnoreAcIn1') is not None
+ self._dbusservice['/Capabilities'] |= (
+ Capabilities.WarmupCooldown if ignore_ac_supported else 0)
+
+ def _determineservices(self):
+ # batterymeasurement is either 'default' or 'com_victronenergy_battery_288/Dc/0'.
+ # In case it is set to default, we use the AutoSelected battery
+ # measurement, given by SystemCalc.
+ batterymeasurement = None
+ newbatteryservice = None
+ batteryprefix = ''
+ selectedbattery = self._settings['batterymeasurement']
+ vebusservice = None
+
+ if selectedbattery == 'default':
+ batterymeasurement = 'default'
+ elif len(selectedbattery.split('/', 1)) == 2: # Only very basic sanity checking..
+ batterymeasurement = self._settings['batterymeasurement']
+ elif selectedbattery == 'nobattery':
+ batterymeasurement = None
+ else:
+ # Exception: unexpected value for batterymeasurement
+ pass
+
+ if batterymeasurement and batterymeasurement != 'default':
+ batteryprefix = '/' + batterymeasurement.split('/', 1)[1]
+
+ # Get the current battery servicename
+ if self._battery_service:
+ oldservice = self._battery_service
+ else:
+ oldservice = None
+
+ if batterymeasurement == 'default':
+ newbatteryservice = 'default'
+ elif batterymeasurement:
+ battery_instance = int(batterymeasurement.split('_', 3)[3].split('/')[0])
+ service_type = None
+
+ if 'vebus' in batterymeasurement:
+ service_type = 'vebus'
+ elif 'battery' in batterymeasurement:
+ service_type = 'battery'
+
+ newbatteryservice = self._get_servicename_by_instance(battery_instance, service_type)
+
+ if newbatteryservice and newbatteryservice != oldservice:
+ if selectedbattery == 'default':
+ self.log_info('Getting battery values from systemcalc.')
+ if selectedbattery == 'nobattery':
+ self.log_info('Battery monitoring disabled! Stop evaluating related conditions')
+ self._battery_service = None
+ self._battery_prefix = None
+ self.log_info('Battery service we need (%s) found! Using it for generator start/stop' % batterymeasurement)
+ self._battery_service = newbatteryservice
+ self._battery_prefix = batteryprefix
+ elif not newbatteryservice and newbatteryservice != oldservice:
+ self.log_info('Error getting battery service!')
+ self._battery_service = newbatteryservice
+ self._battery_prefix = batteryprefix
+
+ # Get the default VE.Bus service
+ vebusservice = self._dbusmonitor.get_value('com.victronenergy.system', '/VebusService')
+ if vebusservice:
+ if self.multiservice != vebusservice:
+ self._vebusservice = vebusservice
+ self._acsystemservice = None
+ self._set_capabilities()
+ self.log_info('Vebus service (%s) found! Using it for generator start/stop' % vebusservice)
+ else:
+ acsystemservice = self._get_acsystem_service()
+ if acsystemservice:
+ if self.multiservice != acsystemservice:
+ self._acsystemservice = acsystemservice
+ self._vebusservice = None
+ self._set_capabilities()
+ self.log_info('AC system service (%s) found! Using it for generator start/stop' % acsystemservice)
+ else:
+ if self._vebusservice is not None:
+ self.log_info('Vebus service (%s) dissapeared! Stop evaluating related conditions' % self._vebusservice)
+ elif self._acsystemservice is not None:
+ self.log_info('AC system service (%s) dissapeared! Stop evaluating related conditions' % self._acsystemservice)
+ else:
+ self.log_info('Error getting Vebus or AC system service!')
+ self._vebusservice = None
+ self._acsystemservice = None
+
+ def _get_acsystem_service(self):
+ services = self._dbusmonitor.get_service_list()
+ for service in services:
+ if service.startswith('com.victronenergy.acsystem'):
+ if self._dbusmonitor.get_value(service, '/DeviceInstance') == 0:
+ return service
+ return None
+
+ def _get_servicename_by_instance(self, instance, service_type=None):
+ sv = None
+ services = self._dbusmonitor.get_service_list()
+
+ for service in services:
+ if service_type and service_type not in service:
+ continue
+
+ if services[service] == instance:
+ sv = service
+ break
+
+ return sv
+
+ def _get_monotonic_seconds(self):
+ return monotonic_time.monotonic_time().to_seconds_double()
+
+ def _start_generator(self, condition):
+ state = self._dbusservice['/State']
+ remote_running = self._get_remote_switch_state()
+
+ # This function will start the generator in the case generator not
+ # already running. When differs, the RunningByCondition is updated
+ running = state in (States.WARMUP, States.COOLDOWN, States.STOPPING, States.RUNNING)
+ if not (running and remote_running): # STOPPED, ERROR
+ # There is an option to skip warm-up for the inverteroverload condition.
+#### GuiMods warm-up / cool-down
+ self.log_info('Starting generator by %s condition' % condition)
+ # if there is a warmup time specified, always go through warm-up state
+ # regardless of AC input in use
+ warmUpPeriod = self._settings['warmuptime']
+ if warmUpPeriod > 0:
+ self._warmUpEndTime = self._currentTime + warmUpPeriod
+ self.log_info ("starting warm-up")
+ self._dbusservice['/State'] = States.WARMUP
+ # no warm-up go directly to running
+ else:
+ self._dbusservice['/State'] = States.RUNNING
+ self._warmUpEndTime = 0
+
+ self._coolDownEndTime = 0
+ self._stoptime = 0
+
+ self._update_remote_switch()
+ else: # WARMUP, COOLDOWN, RUNNING, STOPPING
+ if state in (States.COOLDOWN, States.STOPPING):
+ # Start request during cool-down run, go back to RUNNING
+ self.log_info ("aborting cool-down - returning to running")
+ self._dbusservice['/State'] = States.RUNNING
+
+ elif state == States.WARMUP:
+ if self._currentTime > self._warmUpEndTime:
+ self.log_info ("warm-up complete")
+ self._dbusservice['/State'] = States.RUNNING
+
+ # Update the RunningByCondition
+ if self._dbusservice['/RunningByCondition'] != condition:
+ self.log_info('Generator previously running by %s condition is now running by %s condition'
+ % (self._dbusservice['/RunningByCondition'], condition))
+#### end GuiMods warm-up / cool-down
+
+ self._dbusservice['/RunningByCondition'] = condition
+ self._dbusservice['/RunningByConditionCode'] = RunningConditions.lookup(condition)
+
+ def _stop_generator(self):
+ state = self._dbusservice['/State']
+ remote_running = self._get_remote_switch_state()
+ running = state in (States.WARMUP, States.COOLDOWN, States.STOPPING, States.RUNNING)
+
+ if running or remote_running:
+#### GuiMods warm-up / cool-down
+ if state == States.RUNNING:
+ state = States.COOLDOWN
+ if self._currentTime < self._coolDownEndTime:
+ self.log_info ("starting cool-down")
+ elif self._settings['cooldowntime'] != 0:
+ self.log_info ("skipping cool-down -- no AC load on generator")
+
+ # warm-up should also transition to stopping
+ # cool-down time will have expired since it's set to 0 when starting
+ # and there has not yet been a load on the generator
+ if state in (States.WARMUP, States.COOLDOWN):
+ # cool down complete
+ if self._currentTime > self._coolDownEndTime:
+ state = States.STOPPING
+ self.log_info('Stopping generator that was running by %s condition' %
+ str(self._dbusservice['/RunningByCondition']))
+ self._update_remote_switch() # Stop engine
+ self._stoptime = self._currentTime + self._settings['generatorstoptime']
+ if self._currentTime < self._stoptime:
+ self.log_info ("waiting for generator so stop")
+
+ if state == States.STOPPING:
+ # wait for stop period expired - finish up transition to STOPPED
+ if self._currentTime > self._stoptime:
+ if self._settings['generatorstoptime'] != 0:
+ self.log_info ("generator stop time reached - OK to reconnect AC")
+ state = States.STOPPED
+ self._update_remote_switch()
+ self._dbusservice['/RunningByCondition'] = ''
+ self._dbusservice['/RunningByConditionCode'] = RunningConditions.Stopped
+ self._update_accumulated_time()
+ self._starttime = 0
+ self._dbusservice['/Runtime'] = 0
+ self._dbusservice['/ManualStartTimer'] = 0
+ self._manualstarttimer = 0
+ self._last_runtime_update = 0
+
+ self._dbusservice['/State'] = state
+#### end GuiMods warm-up / cool-down
+
+ @property
+ def _ac1_is_generator(self):
+ return self._dbusmonitor.get_value('com.victronenergy.settings',
+ '/Settings/SystemSetup/AcInput1') == 2
+
+ @property
+ def _ac2_is_generator(self):
+ return self._dbusmonitor.get_value('com.victronenergy.settings',
+ '/Settings/SystemSetup/AcInput2') == 2
+
+#### GuiMods warm-up / cool-down
+ # stock code does not handle changes in the input type
+ # which could happen with an external transfer switch
+ # doing things this way should handle it
+
+ def _set_ignore_ac(self, ignore):
+ # This is here so the Multi/Quattro can be told to disconnect AC-in,
+ # so that we can do warm-up and cool-down.
+ if self.multiservice is None:
+ return
+ self._activeAcInIsIgnored = ignore
+ ignore1 = False
+ ignore2 = False
+ if self._ac1_is_generator:
+ ignore1 = ignore
+ elif self._ac2_is_generator:
+ ignore2 = ignore
+
+ if ignore1 != self._ac1isIgnored:
+ if ignore1:
+ self.log_info ("shedding load - AC input 1")
+ else:
+ self.log_info ("restoring load - AC input 1")
+ self._dbusmonitor.set_value_async(self.multiservice, '/Ac/Control/IgnoreAcIn1', dbus.Int32(ignore1, variant_level=1))
+ self._ac1isIgnored = ignore1
+
+ if ignore2 != self._ac2isIgnored:
+ if ignore2:
+ self.log_info ("shedding load - AC input 2")
+ else:
+ self.log_info ("restoring load - AC input 2")
+ self._dbusmonitor.set_value_async(self.multiservice, '/Ac/Control/IgnoreAcIn2', dbus.Int32(ignore2, variant_level=1))
+ self._ac2isIgnored = ignore2
+#### end GuiMods warm-up / cool-down
+
+ def _update_remote_switch(self):
+ # Engine should be started in these states
+ v = self._dbusservice['/State'] in (States.RUNNING, States.WARMUP, States.COOLDOWN)
+ self._set_remote_switch_state(dbus.Int32(v, variant_level=1))
+
+ def _running_by_digital_input(self, path, value):
+ return
+
+ def _generator_started(self):
+ if (not self._generator_running):
+ self._starttime_fb = monotonic_time.monotonic_time().to_seconds_double()
+ self._generator_running = True
+
+ def _generator_stopped(self):
+ if (self._generator_running):
+ self._generator_running = False
+ self._update_runtime(just_stopped=True)
+ self._dbusservice['/Runtime'] = 0
+ self._starttime_fb = 0
+ self._last_runtime_update = 0
+
+ def _get_remote_switch_state(self):
+ raise Exception('This function should be overridden')
+
+ def _set_remote_switch_state(self, value):
+ raise Exception('This function should be overridden')
+
+ # Check the remote status, for example errors
+ def _check_remote_status(self):
+ raise Exception('This function should be overridden')
+
+ def _remote_setup(self):
+ raise Exception('This function should be overridden')
+
+ def _create_dbus_monitor(self, *args, **kwargs):
+ raise Exception('This function should be overridden')
+
+ def _create_settings(self, *args, **kwargs):
+ raise Exception('This function should be overridden')
+
+ def _create_dbus_service(self):
+ return create_dbus_service(self._instance)
+
+#### GuiMods
+# this function connects the generator digital input (if any)
+# to the generator /ManualStart
+#
+# if the generator digital input changes from stopped to running
+# AND no run conditions are active, a manual start is innitiated
+#
+# if the generator digital input changes from running to stopped
+# AND a manual start is active, a manual stop is innitiated
+#
+# /ExternalOverride is used by the GUI to alert the user when there is a conflict
+# between the running state reported by the generator and the expected state
+# /ExternalOverride is True if the states differ
+# activation is delayed 5 seconds to allow transitions to settle
+
+ def syncManualRunToExternalState (self):
+ internalRun = self._dbusservice['/State'] in (States.RUNNING, States.WARMUP, States.COOLDOWN)
+ externalRun = self._is_running
+ # forward input state changes to /ManualStart
+ if self._linkToExternalState and externalRun != self._lastIsRunning:
+ if externalRun and not internalRun:
+ self.log_info ("generator was started externally - syncing ManualStart state")
+ self._dbusservice['/ManualStart'] = 1
+ elif not externalRun and internalRun and self._dbusservice['/ManualStart'] == 1:
+ self.log_info ("generator was stopped externally - syncing ManualStart state")
+ self._dbusservice['/ManualStart'] = 0
+ self._lastIsRunning = externalRun
+
+ # update ExternalOverride
+ if externalRun != internalRun:
+ if self._externalOverrideDelay > 5:
+ self._dbusservice['/ExternalOverride'] = 1
+ else:
+ self._externalOverrideDelay += 1
+ else:
+ self._dbusservice['/ExternalOverride'] = 0
+ self._externalOverrideDelay = 0
+#### end GuiMods
diff --git a/FileSets/PatchSource/startstop.py-v3.60~25.orig b/FileSets/PatchSource/startstop.py-v3.60~25.orig
new file mode 100644
index 00000000..2e6f8804
--- /dev/null
+++ b/FileSets/PatchSource/startstop.py-v3.60~25.orig
@@ -0,0 +1,1357 @@
+#!/usr/bin/python -u
+# -*- coding: utf-8 -*-
+
+# Function
+# dbus_generator monitors the dbus for batteries (com.victronenergy.battery.*) and
+# vebus com.victronenergy.vebus.*
+# Battery and vebus monitors can be configured through the gui.
+# It then monitors SOC, AC loads, battery current and battery voltage,to auto start/stop the generator based
+# on the configuration settings. Generator can be started manually or periodically setting a tes trun period.
+# Time zones function allows to use different values for the conditions along the day depending on time
+
+import dbus
+import datetime
+import calendar
+import time
+import sys
+import json
+import os
+import logging
+from collections import OrderedDict
+import monotonic_time
+from gen_utils import SettingsPrefix, Errors, States, enum
+from gen_utils import create_dbus_service
+# Victron packages
+sys.path.insert(1, os.path.join(os.path.dirname(__file__), 'ext', 'velib_python'))
+from ve_utils import exit_on_error
+from settingsdevice import SettingsDevice
+
+RunningConditions = enum(
+ Stopped = 0,
+ Manual = 1,
+ TestRun = 2,
+ LossOfCommunication = 3,
+ Soc = 4,
+ Acload = 5,
+ BatteryCurrent = 6,
+ BatteryVoltage = 7,
+ InverterHighTemp = 8,
+ InverterOverload = 9,
+ StopOnAc1 = 10,
+ StopOnAc2 = 11)
+
+Capabilities = enum(
+ WarmupCooldown = 1
+)
+
+SYSTEM_SERVICE = 'com.victronenergy.system'
+BATTERY_PREFIX = '/Dc/Battery'
+HISTORY_DAYS = 30
+AUTOSTART_DISABLED_ALARM_TIME = 600
+
+def safe_max(args):
+ try:
+ return max(x for x in args if x is not None)
+ except ValueError:
+ return None
+
+class Condition(object):
+ def __init__(self, parent):
+ self.parent = parent
+ self.reached = False
+ self.start_timer = 0
+ self.stop_timer = 0
+ self.valid = True
+ self.enabled = False
+ self.retries = 0
+
+ def __getitem__(self, key):
+ try:
+ return getattr(self, key)
+ except AttributeError:
+ raise KeyError(key)
+
+ def __setitem__(self, key, value):
+ setattr(self, key, value)
+
+ def get_value(self):
+ raise NotImplementedError("get_value")
+
+ @property
+ def multi_service(self):
+ return self.parent.multiservice
+
+ @property
+ def multi_service_type(self):
+ return self.parent.multiservice_type
+
+ @property
+ def monitor(self):
+ return self.parent._dbusmonitor
+
+class SocCondition(Condition):
+ name = 'soc'
+ monitoring = 'battery'
+ boolean = False
+ timed = True
+
+ def get_value(self):
+ return self.parent._get_battery().soc
+
+class AcLoadCondition(Condition):
+ name = 'acload'
+ monitoring = 'vebus'
+ boolean = False
+ timed = True
+
+ def get_value(self):
+ loadOnAcOut = []
+ totalConsumption = []
+
+ for phase in ['L1', 'L2', 'L3']:
+ # Get the values directly from the inverter, systemcalc doesn't provide raw inverted power
+ loadOnAcOut.append(self.monitor.get_value(self.multi_service, ('/Ac/Out/%s/P' % phase)))
+
+ # Calculate total consumption, '/Ac/Consumption/%s/Power' is deprecated
+ c_i = self.monitor.get_value(SYSTEM_SERVICE, ('/Ac/ConsumptionOnInput/%s/Power' % phase))
+ c_o = self.monitor.get_value(SYSTEM_SERVICE, ('/Ac/ConsumptionOnOutput/%s/Power' % phase))
+ totalConsumption.append(sum(filter(None, (c_i, c_o))))
+
+ # Invalidate if vebus is not available
+ if loadOnAcOut[0] == None:
+ return None
+
+ # Total consumption
+ if self.parent._settings['acloadmeasurement'] == 0:
+ return sum(filter(None, totalConsumption))
+
+ # Load on inverter AC out
+ if self.parent._settings['acloadmeasurement'] == 1:
+ return sum(filter(None, loadOnAcOut))
+
+ # Highest phase load
+ if self.parent._settings['acloadmeasurement'] == 2:
+ return safe_max(loadOnAcOut)
+
+class BatteryCurrentCondition(Condition):
+ name = 'batterycurrent'
+ monitoring = 'battery'
+ boolean = False
+ timed = True
+
+ def get_value(self):
+ c = self.parent._get_battery().current
+ if c is not None:
+ c *= -1
+ return c
+
+class BatteryVoltageCondition(Condition):
+ name = 'batteryvoltage'
+ monitoring = 'battery'
+ boolean = False
+ timed = True
+
+ def get_value(self):
+ return self.parent._get_battery().voltage
+
+class InverterTempCondition(Condition):
+ name = 'inverterhightemp'
+ monitoring = 'vebus'
+ boolean = True
+ timed = True
+
+ def get_value(self):
+ v = self.monitor.get_value(self.multi_service,
+ '/Alarms/HighTemperature')
+
+ # When multi is connected to CAN-bus, alarms are published to
+ # /Alarms/HighTemperature... but when connected to vebus alarms are
+ # splitted in three phases and published to /Alarms/LX/HighTemperature...
+ if v is None:
+ inverterHighTemp = []
+ for phase in ['L1', 'L2', 'L3']:
+ # Inverter alarms must be fetched directly from the inverter service
+ inverterHighTemp.append(self.monitor.get_value(self.multi_service, ('/Alarms/%s/HighTemperature' % phase)))
+ return safe_max(inverterHighTemp)
+ return v
+
+class InverterOverloadCondition(Condition):
+ name = 'inverteroverload'
+ monitoring = 'vebus'
+ boolean = True
+ timed = True
+
+ def get_value(self):
+ v = self.monitor.get_value(self.multi_service,
+ '/Alarms/Overload')
+
+ # When multi is connected to CAN-bus, alarms are published to
+ # /Alarms/Overload... but when connected to vebus alarms are
+ # splitted in three phases and published to /Alarms/LX/Overload...
+ if v is None:
+ inverterOverload = []
+ for phase in ['L1', 'L2', 'L3']:
+ # Inverter alarms must be fetched directly from the inverter service
+ inverterOverload.append(self.monitor.get_value(self.multi_service, ('/Alarms/%s/Overload' % phase)))
+ return safe_max(inverterOverload)
+ return v
+
+# The 'Stop on AC [1/2] conditions are disabled for the Multi RS (acsystem)
+# The 'stop on AC' condition stops the generator, which is connected to one AC input when there is AC detected on the other.
+# Since the Multi RS only has one AC input, these conditions cannot be used.
+class StopOnAc1Condition(Condition):
+ name = 'stoponac1'
+ monitoring = 'vebus'
+ boolean = True
+ timed = False
+
+ def get_value(self):
+ # AC input 1
+ if self.multi_service_type == 'vebus':
+ available = self.monitor.get_value(self.multi_service,
+ '/Ac/State/AcIn1Available')
+ if available is None:
+ # Not supported in firmware, fall back to old behaviour
+ activein = self.monitor.get_value(self.multi_service,
+ '/Ac/ActiveIn/ActiveInput')
+
+ # Active input is connected
+ connected = self.monitor.get_value(self.multi_service,
+ '/Ac/ActiveIn/Connected')
+ if None not in (activein, connected):
+ return activein == 0 and connected == 1
+ return None
+
+ return bool(available)
+ else:
+ # Disabled
+ return False
+
+# The StopOnAc2 condition cannot have the fallback to evaluating based on`/Ac/ActiveIn/ActiveInput` + `/Ac/ActiveIn/Connected`
+# Because these paths were present in the vebus firmware before `/Ac/State/AcIn2Available` was,
+# but switching over to AC input 2 was not supported in that firmware version.
+# So if the fallback were implemented for this condition as well, it would stop the
+# generator when AC in 2 becomes available but the quattro would not switch over.
+class StopOnAc2Condition(Condition):
+ name = 'stoponac2'
+ monitoring = 'vebus'
+ boolean = True
+ timed = False
+
+ def get_value(self):
+ if self.multi_service_type == 'vebus':
+ # AC input 2 available (used when grid is on AC-in-2)
+ available = self.monitor.get_value(self.multi_service,
+ '/Ac/State/AcIn2Available')
+
+ return None if available is None else bool(available)
+ else:
+ # Disabled
+ return False
+
+class Battery(object):
+ def __init__(self, monitor, service, prefix):
+ self.monitor = monitor
+ self.service = service
+ self.prefix = prefix
+
+ @property
+ def voltage(self):
+ return self.monitor.get_value(self.service, self.prefix + '/Voltage')
+
+ @property
+ def current(self):
+ return self.monitor.get_value(self.service, self.prefix + '/Current')
+
+ @property
+ def soc(self):
+ # Soc from the device doesn't have the '/Dc/0' prefix like the current and voltage do, but it does
+ # have the same prefix on systemcalc
+ return self.monitor.get_value(self.service, (BATTERY_PREFIX if self.prefix == BATTERY_PREFIX else '') + '/Soc')
+
+class StartStop(object):
+ _driver = None
+ def __init__(self, instance):
+ self._dbusservice = None
+ self._settings = None
+ self._dbusmonitor = None
+ self._remoteservice = None
+ self._name = None
+ self._enabled = False
+ self._generator_running = False
+ self._useGensetHours = False # Sync with genset operatinghours.
+ self._instance = instance
+
+ # One second per retry
+ self.RETRIES_ON_ERROR = 300
+ self._testrun_soc_retries = 0
+ self._last_counters_check = 0
+
+ # Two different starttime values.
+ # starttime_fb is set by the modules (relay.py, genset.py) and will be set to the current time when
+ # the feedback mechanism (digital input / `/StatusCode`) indicates that the generator is running.
+ # The other one is set by startstop when commanding the module to start the generator and is used by the
+ # warm-up mechanism to ensure warm-up finishes without needing feedback that the generator has actually started.
+ # If there is no feedback mechanism in place, the values will be equal as the module will call '_generator_started()`
+ # right after receiving the start command from startstop.
+ self._starttime_fb = 0 # Starttime of the generator as reported by the feedback mechanism (e.g., digital input), if present
+ self._starttime = 0 # Starttime of the generator, maintained by startstop. Not influenced by feedback mechanism.
+ self._stoptime = 0 # Used for cooldown
+ self._manualstarttimer = 0
+ self._last_runtime_update = 0
+ self._timer_runnning = 0
+
+ # The installer left autostart disabled
+ self._autostart_last_time = self._get_monotonic_seconds()
+ self._remote_start_mode_last_time = self._get_monotonic_seconds()
+
+ # Manual battery service selection is deprecated in favour
+ # of getting the values directly from systemcalc, we keep
+ # manual selected services handling for compatibility reasons.
+ self._vebusservice = None
+ self._acsystemservice = None
+ self._errorstate = 0
+ self._battery_service = None
+ self._battery_prefix = None
+
+ self._power_input_timer = {
+ 'timeout': 0,
+ 'unabletostart': False
+ }
+
+ # Order is important. Conditions are evaluated in the order listed.
+ self._condition_stack = OrderedDict({
+ SocCondition.name: SocCondition(self),
+ AcLoadCondition.name: AcLoadCondition(self),
+ BatteryCurrentCondition.name: BatteryCurrentCondition(self),
+ BatteryVoltageCondition.name: BatteryVoltageCondition(self),
+ InverterTempCondition.name: InverterTempCondition(self),
+ InverterOverloadCondition.name: InverterOverloadCondition(self),
+ StopOnAc1Condition.name: StopOnAc1Condition(self),
+ StopOnAc2Condition.name: StopOnAc2Condition(self)
+ })
+
+ # Return the active vebusservice or the acsystemservice.
+ @property
+ def multiservice(self):
+ return self._vebusservice if self._vebusservice else self._acsystemservice if self._acsystemservice else None
+
+ @property
+ def multiservice_type(self):
+ return self.multiservice.split('.')[2] if self.multiservice is not None else None
+
+ def set_sources(self, dbusmonitor, settings, name, remoteservice):
+ self._settings = SettingsPrefix(settings, name)
+ self._dbusmonitor = dbusmonitor
+ self._remoteservice = remoteservice
+ self._name = name
+
+ self.log_info('Start/stop instance created for %s.' % self._remoteservice)
+ self._remote_setup()
+
+ def _create_service(self):
+ self._dbusservice = self._create_dbus_service()
+
+ # The driver used for this start/stop service
+ self._dbusservice.add_path('/Type', value=self._driver)
+ # State: None = invalid, 0 = stopped, 1 = running, 2=Warm-up, 3=Cool-down
+ self._dbusservice.add_path('/State', value=None, gettextcallback=lambda p, v: States.get_description(v))
+ self._dbusservice.add_path('/Enabled', value=1)
+ # RunningByConditionCode: Numeric Companion to /RunningByCondition below, but
+ # also encompassing a Stopped state.
+ self._dbusservice.add_path('/RunningByConditionCode', value=None)
+ # Error
+ self._dbusservice.add_path('/Error', value=None, gettextcallback=lambda p, v: Errors.get_description(v))
+ # Condition that made the generator start
+ self._dbusservice.add_path('/RunningByCondition', value=None)
+ # Runtime
+ self._dbusservice.add_path('/Runtime', value=None, gettextcallback=self._seconds_to_text)
+ # Today runtime
+ self._dbusservice.add_path('/TodayRuntime', value=None, gettextcallback=self._seconds_to_text)
+ # Test run runtime
+ self._dbusservice.add_path('/TestRunIntervalRuntime', value=None , gettextcallback=self._seconds_to_text)
+ # Next test run date, values is 0 for test run disabled
+ self._dbusservice.add_path('/NextTestRun', value=None, gettextcallback=lambda p, v: datetime.datetime.fromtimestamp(v).strftime('%c'))
+ # Next test run is needed 1, not needed 0
+ self._dbusservice.add_path('/SkipTestRun', value=None)
+ # Manual start
+ self._dbusservice.add_path('/ManualStart', value=None, writeable=True)
+ # Manual start timer
+ self._dbusservice.add_path('/ManualStartTimer', value=None, writeable=True)
+ # Silent mode active
+ self._dbusservice.add_path('/QuietHours', value=None)
+ # Alarms
+ self._dbusservice.add_path('/Alarms/NoGeneratorAtAcIn', value=None)
+ self._dbusservice.add_path('/Alarms/NoGeneratorAtDcIn', value=None)
+ self._dbusservice.add_path('/Alarms/ServiceIntervalExceeded', value=None)
+ self._dbusservice.add_path('/Alarms/AutoStartDisabled', value=None)
+ self._dbusservice.add_path('/Alarms/RemoteStartModeDisabled', value=None)
+ # Autostart
+ self._dbusservice.add_path('/AutoStartEnabled', value=None, writeable=True, onchangecallback=self._set_autostart)
+ # Accumulated runtime
+ self._dbusservice.add_path('/AccumulatedRuntime', value=None)
+ # Service interval
+ self._dbusservice.add_path('/ServiceInterval', value=None)
+ # Capabilities, where we can add bits
+ self._dbusservice.add_path('/Capabilities', value=0)
+ # Service countdown, calculated by running time and service interval
+ self._dbusservice.add_path('/ServiceCounter', value=None)
+ self._dbusservice.add_path('/ServiceCounterReset', value=None, writeable=True, onchangecallback=self._reset_service_counter)
+ # Publish what service we're controlling, and the productid
+ self._dbusservice.add_path('/GensetService', value=self._remoteservice)
+ self._dbusservice.add_path('/GensetServiceType',
+ value=self._remoteservice.split('.')[2] if self._remoteservice is not None else None)
+ self._dbusservice.add_path('/GensetInstance',
+ value=self._dbusmonitor.get_value(self._remoteservice, '/DeviceInstance'))
+ self._dbusservice.add_path('/GensetProductId',
+ value=self._dbusmonitor.get_value(self._remoteservice, '/ProductId'))
+ self._dbusservice.add_path('/DigitalInput/Running', value=None, writeable=True, onchangecallback=self._running_by_digital_input)
+ self._dbusservice.add_path('/DigitalInput/Input', value=None, writeable=True, onchangecallback=self._running_by_digital_input)
+
+ self._dbusservice.register()
+ # We need to set the values after creating the paths to trigger the 'onValueChanged' event for the gui
+ # otherwise the gui will report the paths as invalid if we remove and recreate the paths without
+ # restarting the dbusservice.
+ self._dbusservice['/State'] = 0
+ self._dbusservice['/RunningByConditionCode'] = RunningConditions.Stopped
+ self._dbusservice['/Error'] = 0
+ self._dbusservice['/RunningByCondition'] = ''
+ self._dbusservice['/Runtime'] = 0
+ self._dbusservice['/TodayRuntime'] = 0
+ self._dbusservice['/TestRunIntervalRuntime'] = self._interval_runtime(self._settings['testruninterval'])
+ self._dbusservice['/NextTestRun'] = None
+ self._dbusservice['/SkipTestRun'] = None
+ self._dbusservice['/ProductName'] = "Generator start/stop"
+ self._dbusservice['/ManualStart'] = 0
+ self._dbusservice['/ManualStartTimer'] = 0
+ self._dbusservice['/QuietHours'] = 0
+ self._dbusservice['/Alarms/NoGeneratorAtAcIn'] = 0
+ self._dbusservice['/Alarms/ServiceIntervalExceeded'] = 0
+ self._dbusservice['/Alarms/AutoStartDisabled'] = 0 # GX auto start/stop
+ self._dbusservice['/Alarms/RemoteStartModeDisabled'] = 0 # Genset remote start mode
+ self._dbusservice['/AutoStartEnabled'] = self._settings['autostart']
+ self._dbusservice['/AccumulatedRuntime'] = int(self._settings['accumulatedtotal'])
+ self._dbusservice['/ServiceInterval'] = int(self._settings['serviceinterval'])
+ self._dbusservice['/ServiceCounter'] = None
+ self._dbusservice['/ServiceCounterReset'] = 0
+ self._dbusservice['/DigitalInput/Running'] = 0
+ self._dbusservice['/DigitalInput/Input'] = 0
+
+ # When this startstop instance controls a genset which reports operatinghours, make sure to synchronize with that.
+ self._useGensetHours = self._dbusmonitor.get_value(self._remoteservice, '/Engine/OperatingHours', None) is not None
+
+ @property
+ def _is_running(self):
+ return self._generator_running
+
+ @property
+ def capabilities(self):
+ return self._dbusservice['/Capabilities']
+
+ def _set_autostart(self, path, value):
+ if 0 <= value <= 1:
+ self._settings['autostart'] = int(value)
+ return True
+ return False
+
+ def enable(self):
+ if self._enabled:
+ return
+ self.log_info('Enabling auto start/stop and taking control of remote switch')
+ self._create_service()
+ self._determineservices()
+ self._update_remote_switch()
+ # If cooldown or warmup is enabled, the Quattro may be left in a bad
+ # state if there is an unfortunate crash or a reboot. Set the ignore_ac
+ # flag to a sane value on startup.
+ if self._settings['cooldowntime'] > 0 or \
+ self._settings['warmuptime'] > 0:
+ self._set_ignore_ac(False)
+ self._enabled = True
+
+ def disable(self):
+ if not self._enabled:
+ return
+ self.log_info('Disabling auto start/stop, releasing control of remote switch')
+ self._remove_service()
+ self._enabled = False
+
+ def remove(self):
+ self.disable()
+ self.log_info('Removed from start/stop instances')
+
+ def _remove_service(self):
+ self._dbusservice.__del__()
+ self._dbusservice = None
+
+ def device_added(self, dbusservicename, instance):
+ self._determineservices()
+
+ def device_removed(self, dbusservicename, instance):
+ self._determineservices()
+
+ def get_error(self):
+ return self._dbusservice['/Error']
+
+ def set_error(self, errorn):
+ self._dbusservice['/Error'] = errorn
+
+ def clear_error(self):
+ self._dbusservice['/Error'] = Errors.NONE
+
+ def dbus_value_changed(self, dbusServiceName, dbusPath, options, changes, deviceInstance):
+ if self._dbusservice is None:
+ return
+
+ # AcIn1Available is needed to determine capabilities, but may
+ # only show up later. So we have to wait for it here.
+ if self.multiservice is not None and \
+ dbusServiceName == self.multiservice and \
+ dbusPath == '/Ac/State/AcIn1Available' or dbusPath == '/Ac/Control/IgnoreAcIn1':
+ self._set_capabilities()
+
+ # If gensethours is updated, update the accumulated time.
+ if dbusPath == '/Engine/OperatingHours' and self._useGensetHours:
+ self._update_accumulated_time(gensetHours=changes['Value'])
+
+ if dbusServiceName != 'com.victronenergy.system':
+ return
+ if dbusPath == '/AutoSelectedBatteryMeasurement' and self._settings['batterymeasurement'] == 'default':
+ self._determineservices()
+
+ if dbusPath == '/VebusService':
+ self._determineservices()
+
+ def handlechangedsetting(self, setting, oldvalue, newvalue):
+ if self._dbusservice is None:
+ return
+ if self._name not in setting:
+ # Not our setting
+ return
+
+ s = self._settings.removeprefix(setting)
+
+ if s == 'batterymeasurement':
+ self._determineservices()
+ # Reset retries and valid if service changes
+ for condition in self._condition_stack.values():
+ if condition['monitoring'] == 'battery':
+ condition['valid'] = True
+ condition['retries'] = 0
+
+ if s == 'autostart':
+ self.log_info('Autostart function %s.' % ('enabled' if newvalue == 1 else 'disabled'))
+ self._dbusservice['/AutoStartEnabled'] = self._settings['autostart']
+
+ if self._dbusservice is not None and s == 'testruninterval':
+ self._dbusservice['/TestRunIntervalRuntime'] = self._interval_runtime(
+ self._settings['testruninterval'])
+
+ if s == 'serviceinterval':
+ try:
+ self._dbusservice['/ServiceInterval'] = int(newvalue)
+ except TypeError:
+ pass
+ if newvalue == 0:
+ self._dbusservice['/ServiceCounter'] = None
+ else:
+ self._update_accumulated_time()
+ if s == 'lastservicereset':
+ self._update_accumulated_time()
+
+ def _reset_service_counter(self, path, value):
+ if (path == '/ServiceCounterReset' and value == int(1) and self._dbusservice['/AccumulatedRuntime']):
+ self._settings['lastservicereset'] = self._dbusservice['/AccumulatedRuntime']
+ self._update_accumulated_time()
+ self.log_info('Service counter reset triggered.')
+
+ return True
+
+ def _seconds_to_text(self, path, value):
+ m, s = divmod(value, 60)
+ h, m = divmod(m, 60)
+ return '%dh, %dm, %ds' % (h, m, s)
+
+ def log_info(self, msg):
+ logging.info(self._name + ': %s' % msg)
+
+ def tick(self):
+ if not self._enabled:
+ return
+ self._check_remote_status()
+ self._evaluate_startstop_conditions()
+ self._evaluate_autostart_disabled_alarm()
+ self._detect_generator_at_input()
+ if self._dbusservice['/ServiceCounterReset'] == 1:
+ self._dbusservice['/ServiceCounterReset'] = 0
+
+ def _evaluate_startstop_conditions(self):
+ if self.get_error() != Errors.NONE:
+ # First evaluation after an error, log it
+ if self._errorstate == 0:
+ self._errorstate = 1
+ self._dbusservice['/State'] = States.ERROR
+ self.log_info('Error: #%i - %s, stop controlling remote.' %
+ (self.get_error(),
+ Errors.get_description(self.get_error())))
+ elif self._errorstate == 1:
+ # Error cleared
+ self._errorstate = 0
+ self._dbusservice['/State'] = States.STOPPED
+ self.log_info('Error state cleared, taking control of remote switch.')
+
+ start = False
+ startbycondition = None
+ activecondition = self._dbusservice['/RunningByCondition']
+ today = calendar.timegm(datetime.date.today().timetuple())
+ self._timer_runnning = False
+ connection_lost = False
+ running = self._dbusservice['/State'] in (States.RUNNING, States.WARMUP)
+
+ self._check_quiet_hours()
+
+ # New day, register it
+ if self._last_counters_check < today and self._dbusservice['/State'] == States.STOPPED:
+ self._last_counters_check = today
+ self._update_accumulated_time()
+
+ self._update_runtime()
+
+ if self._evaluate_manual_start():
+ startbycondition = 'manual'
+ start = True
+
+ # Conditions will only be evaluated if the autostart functionality is enabled
+ if self._settings['autostart'] == 1:
+
+ if self._evaluate_testrun_condition():
+ startbycondition = 'testrun'
+ start = True
+
+ # Evaluate stop on AC IN conditions first, when this conditions are enabled and reached the generator
+ # will stop as soon as AC IN in active. Manual and testrun conditions will make the generator start
+ # or keep it running.
+ stop_on_ac_reached = (self._evaluate_condition(self._condition_stack[StopOnAc1Condition.name]) or
+ self._evaluate_condition(self._condition_stack[StopOnAc2Condition.name]))
+ stop_by_ac1_ac2 = startbycondition not in ['manual', 'testrun'] and stop_on_ac_reached
+
+ if stop_by_ac1_ac2 and running and activecondition not in ['manual', 'testrun']:
+ self.log_info('AC input available, stopping')
+
+ # Evaluate value conditions
+ for condition, data in self._condition_stack.items():
+ # Do not evaluate rest of conditions if generator is configured to stop
+ # when AC IN is available
+ if stop_by_ac1_ac2:
+ start = False
+ if running:
+ self._reset_condition(data)
+ continue
+ else:
+ break
+
+ # Don't short-circuit this, _evaluate_condition sets .reached
+ start = self._evaluate_condition(data) or start
+ startbycondition = condition if start and startbycondition is None else startbycondition
+ # Connection lost is set to true if the number of retries of one or more enabled conditions
+ # >= RETRIES_ON_ERROR
+ if data.enabled:
+ connection_lost = data.retries >= self.RETRIES_ON_ERROR
+
+ # If none condition is reached check if connection is lost and start/keep running the generator
+ # depending on '/OnLossCommunication' setting
+ if not start and connection_lost:
+ # Start always
+ if self._settings['onlosscommunication'] == 1:
+ start = True
+ startbycondition = 'lossofcommunication'
+ # Keep running if generator already started
+ if running and self._settings['onlosscommunication'] == 2:
+ start = True
+ startbycondition = 'lossofcommunication'
+
+ if not start and self._errorstate:
+ self._stop_generator()
+
+ if self._errorstate:
+ return
+
+ mtime = monotonic_time.monotonic_time().to_seconds_double()
+ if start:
+ self._start_generator(startbycondition)
+ elif (int(mtime - self._starttime) >= self._settings['minimumruntime'] * 60
+ or activecondition == 'manual'):
+ self._stop_generator()
+
+ def _update_runtime(self, just_stopped=False):
+ # Update current and accumulated runtime.
+ # By performance reasons, accumulated runtime is only updated
+ # once per 60s. When the generator stops is also updated.
+ if self._is_running or just_stopped:
+ mtime = monotonic_time.monotonic_time().to_seconds_double()
+ if (mtime - self._starttime_fb) - self._last_runtime_update >= 60 or just_stopped:
+ self._dbusservice['/Runtime'] = int(mtime - self._starttime_fb)
+ self._update_accumulated_time()
+ elif self._last_runtime_update == 0:
+ self._dbusservice['/Runtime'] = int(mtime - self._starttime_fb)
+
+ def _evaluate_autostart_disabled_alarm(self):
+
+ if self._settings['autostartdisabledalarm'] == 0:
+ self._autostart_last_time = self._get_monotonic_seconds()
+ self._remote_start_mode_last_time = self._get_monotonic_seconds()
+ if self._dbusservice['/Alarms/AutoStartDisabled'] != 0:
+ self._dbusservice['/Alarms/AutoStartDisabled'] = 0
+ if self._dbusservice['/Alarms/RemoteStartModeDisabled'] != 0:
+ self._dbusservice['/Alarms/RemoteStartModeDisabled'] = 0
+ return
+
+ # GX auto start/stop alarm
+ if self._settings['autostart'] == 1:
+ self._autostart_last_time = self._get_monotonic_seconds()
+ if self._dbusservice['/Alarms/AutoStartDisabled'] != 0:
+ self._dbusservice['/Alarms/AutoStartDisabled'] = 0
+ else:
+ timedisabled = self._get_monotonic_seconds() - self._autostart_last_time
+ if timedisabled > AUTOSTART_DISABLED_ALARM_TIME and self._dbusservice['/Alarms/AutoStartDisabled'] != 2:
+ self.log_info("Autostart was left for more than %i seconds, triggering alarm." % int(timedisabled))
+ self._dbusservice['/Alarms/AutoStartDisabled'] = 2
+
+ # Genset remote start mode alarm
+ if self.get_error() != Errors.REMOTEDISABLED:
+ self._remote_start_mode_last_time = self._get_monotonic_seconds()
+ if self._dbusservice['/Alarms/RemoteStartModeDisabled'] != 0:
+ self._dbusservice['/Alarms/RemoteStartModeDisabled'] = 0
+ else:
+ timedisabled = self._get_monotonic_seconds() - self._remote_start_mode_last_time
+ if timedisabled > AUTOSTART_DISABLED_ALARM_TIME and self._dbusservice['/Alarms/RemoteStartModeDisabled'] != 2:
+ self.log_info("Autostart was left for more than %i seconds, triggering alarm." % int(timedisabled))
+ self._dbusservice['/Alarms/RemoteStartModeDisabled'] = 2
+
+ def _detect_generator_at_input(self):
+ state = self._dbusservice['/State']
+
+ if state in [States.STOPPED, States.COOLDOWN, States.WARMUP]:
+ self._reset_power_input_timer()
+ return
+
+ if self._settings['nogeneratoratacinalarm'] == 0:
+ self._reset_power_input_timer()
+ return
+
+ if self.multiservice_type == 'vebus':
+ activein_state = self._dbusmonitor.get_value(
+ self.multiservice, '/Ac/ActiveIn/Connected')
+ else:
+ active_input = self._dbusmonitor.get_value(
+ self.multiservice, '/Ac/ActiveIn/ActiveInput')
+ activein_state = None if active_input is None else 1 if 0 <= active_input <= 1 else 0
+
+ # Path not supported, skip evaluation
+ if activein_state == None:
+ return
+
+ # Sources 0 = Not available, 1 = Grid, 2 = Generator, 3 = Shore
+ generator_acsource = self._dbusmonitor.get_value(
+ SYSTEM_SERVICE, '/Ac/ActiveIn/Source') == 2
+ # Not connected = 0, connected = 1
+ activein_connected = activein_state == 1
+
+ if generator_acsource and activein_connected:
+ if self._power_input_timer['unabletostart']:
+ self.log_info('Generator detected at inverter AC input, alarm removed')
+ self._reset_power_input_timer()
+ elif self._power_input_timer['timeout'] < self.RETRIES_ON_ERROR:
+ self._power_input_timer['timeout'] += 1
+ elif not self._power_input_timer['unabletostart']:
+ self._power_input_timer['unabletostart'] = True
+ self._dbusservice['/Alarms/NoGeneratorAtAcIn'] = 2
+ self.log_info('Generator not detected at inverter AC input, triggering alarm')
+
+ def _reset_power_input_timer(self):
+ if self._power_input_timer['timeout'] != 0:
+ self._power_input_timer['timeout'] = 0
+
+ if self._power_input_timer['unabletostart'] != 0:
+ self._power_input_timer['unabletostart'] = False
+
+ self._dbusservice['/Alarms/NoGeneratorAtAcIn'] = 0
+
+ def _reset_condition(self, condition):
+ condition['reached'] = False
+ if condition['timed']:
+ condition['start_timer'] = 0
+ condition['stop_timer'] = 0
+
+ def _check_condition(self, condition, value):
+ name = condition['name']
+
+ if self._settings[name + 'enabled'] == 0:
+ if condition['enabled']:
+ condition['enabled'] = False
+ self.log_info('Disabling (%s) condition' % name)
+ condition['retries'] = 0
+ condition['valid'] = True
+ self._reset_condition(condition)
+ return False
+
+ elif not condition['enabled']:
+ condition['enabled'] = True
+ self.log_info('Enabling (%s) condition' % name)
+
+ if (condition['monitoring'] == 'battery') and (self._settings['batterymeasurement'] == 'nobattery'):
+ # If no battery monitor is selected reset the condition
+ self._reset_condition(condition)
+ return False
+
+ if value is None and condition['valid']:
+ if condition['retries'] >= self.RETRIES_ON_ERROR:
+ logging.info('Error getting (%s) value, skipping evaluation till get a valid value' % name)
+ self._reset_condition(condition)
+ self._comunnication_lost = True
+ condition['valid'] = False
+ else:
+ condition['retries'] += 1
+ if condition['retries'] == 1 or (condition['retries'] % 10) == 0:
+ self.log_info('Error getting (%s) value, retrying(#%i)' % (name, condition['retries']))
+ return False
+
+ elif value is not None and not condition['valid']:
+ self.log_info('Success getting (%s) value, resuming evaluation' % name)
+ condition['valid'] = True
+ condition['retries'] = 0
+
+ # Reset retries if value is valid
+ if value is not None and condition['retries'] > 0:
+ self.log_info('Success getting (%s) value, resuming evaluation' % name)
+ condition['retries'] = 0
+
+ return condition['valid']
+
+ def _evaluate_condition(self, condition):
+ name = condition['name']
+ value = condition.get_value()
+ setting = ('qh_' if self._dbusservice['/QuietHours'] == 1 else '') + name
+ startvalue = self._settings[setting + 'start'] if not condition['boolean'] else 1
+ stopvalue = self._settings[setting + 'stop'] if not condition['boolean'] else 0
+
+ # Check if the condition has to be evaluated
+ if not self._check_condition(condition, value):
+ # If generator is started by this condition and value is invalid
+ # wait till RETRIES_ON_ERROR to skip the condition
+ if condition['reached'] and condition['retries'] <= self.RETRIES_ON_ERROR:
+ if condition['retries'] > 0:
+ return True
+
+ return False
+
+ # As this is a generic evaluation method, we need to know how to compare the values
+ # first check if start value should be greater than stop value and then compare
+ start_is_greater = startvalue > stopvalue
+
+ # When the condition is already reached only the stop value can set it to False
+ start = condition['reached'] or (value >= startvalue if start_is_greater else value <= startvalue)
+ stop = value <= stopvalue if start_is_greater else value >= stopvalue
+
+ # Timed conditions must start/stop after the condition has been reached for a minimum
+ # time.
+ if condition['timed']:
+ if not condition['reached'] and start:
+ condition['start_timer'] += time.time() if condition['start_timer'] == 0 else 0
+ start = time.time() - condition['start_timer'] >= self._settings[name + 'starttimer']
+ condition['stop_timer'] *= int(not start)
+ self._timer_runnning = True
+ else:
+ condition['start_timer'] = 0
+
+ if condition['reached'] and stop:
+ condition['stop_timer'] += time.time() if condition['stop_timer'] == 0 else 0
+ stop = time.time() - condition['stop_timer'] >= self._settings[name + 'stoptimer']
+ condition['stop_timer'] *= int(not stop)
+ self._timer_runnning = True
+ else:
+ condition['stop_timer'] = 0
+
+ condition['reached'] = start and not stop
+ return condition['reached']
+
+ def _evaluate_manual_start(self):
+ if self._dbusservice['/ManualStart'] == 0:
+ if self._dbusservice['/RunningByCondition'] == 'manual':
+ self._dbusservice['/ManualStartTimer'] = 0
+ return False
+
+ start = True
+ # If /ManualStartTimer has a value greater than zero will use it to set a stop timer.
+ # If no timer is set, the generator will not stop until the user stops it manually.
+ # Once started by manual start, each evaluation the timer is decreased
+ if self._dbusservice['/ManualStartTimer'] != 0:
+ self._manualstarttimer += time.time() if self._manualstarttimer == 0 else 0
+ self._dbusservice['/ManualStartTimer'] -= int(time.time()) - int(self._manualstarttimer)
+ self._manualstarttimer = time.time()
+ start = self._dbusservice['/ManualStartTimer'] > 0
+ self._dbusservice['/ManualStart'] = int(start)
+ # Reset if timer is finished
+ self._manualstarttimer *= int(start)
+ self._dbusservice['/ManualStartTimer'] *= int(start)
+
+ return start
+
+ def _evaluate_testrun_condition(self):
+ if self._settings['testrunenabled'] == 0:
+ self._dbusservice['/SkipTestRun'] = None
+ self._dbusservice['/NextTestRun'] = None
+ return False
+
+ today = datetime.date.today()
+ yesterday = today - datetime.timedelta(days=1) # Should deal well with DST
+ now = time.time()
+ runtillbatteryfull = self._settings['testruntillbatteryfull'] == 1
+ soc = self._condition_stack['soc'].get_value()
+ batteryisfull = runtillbatteryfull and soc == 100
+ duration = 60 if runtillbatteryfull else self._settings['testrunruntime']
+
+ try:
+ startdate = datetime.date.fromtimestamp(self._settings['testrunstartdate'])
+ _starttime = time.mktime(yesterday.timetuple()) + self._settings['testrunstarttimer']
+
+ # today might in fact still be yesterday, if this test run started
+ # before midnight and finishes after. If `now` still falls in
+ # yesterday's window, then by the temporal anthropic principle,
+ # which I just made up but loosely states that time must have
+ # these properties for observers to exist, it must be yesterday
+ # because we are here to observe it.
+ if _starttime <= now <= _starttime + duration:
+ today = yesterday
+ starttime = _starttime
+ else:
+ starttime = time.mktime(today.timetuple()) + self._settings['testrunstarttimer']
+ except ValueError:
+ logging.debug('Invalid dates, skipping testrun')
+ return False
+
+ # If start date is in the future set as NextTestRun and stop evaluating
+ if startdate > today:
+ self._dbusservice['/NextTestRun'] = time.mktime(startdate.timetuple())
+ return False
+
+ start = False
+ # If the accumulated runtime during the test run interval is greater than '/TestRunIntervalRuntime'
+ # the test run must be skipped
+ needed = (self._settings['testrunskipruntime'] > self._dbusservice['/TestRunIntervalRuntime']
+ or self._settings['testrunskipruntime'] == 0)
+ self._dbusservice['/SkipTestRun'] = int(not needed)
+
+ interval = self._settings['testruninterval']
+ stoptime = starttime + duration
+ elapseddays = (today - startdate).days
+ mod = elapseddays % interval
+
+ start = not bool(mod) and starttime <= now <= stoptime
+
+ if runtillbatteryfull:
+ if soc is not None:
+ self._testrun_soc_retries = 0
+ start = (start or self._dbusservice['/RunningByCondition'] == 'testrun') and not batteryisfull
+ elif self._dbusservice['/RunningByCondition'] == 'testrun':
+ if self._testrun_soc_retries < self.RETRIES_ON_ERROR:
+ self._testrun_soc_retries += 1
+ start = True
+ if (self._testrun_soc_retries % 10) == 0:
+ self.log_info('Test run failed to get SOC value, retrying(#%i)' % self._testrun_soc_retries)
+ else:
+ self.log_info('Failed to get SOC after %i retries, terminating test run condition' % self._testrun_soc_retries)
+ start = False
+ else:
+ start = False
+
+ if not bool(mod) and (now <= stoptime):
+ self._dbusservice['/NextTestRun'] = starttime
+ else:
+ self._dbusservice['/NextTestRun'] = (time.mktime((today + datetime.timedelta(days=interval - mod)).timetuple()) +
+ self._settings['testrunstarttimer'])
+ return start and needed
+
+ def _check_quiet_hours(self):
+ active = False
+ if self._settings['quiethoursenabled'] == 1:
+ # Seconds after today 00:00
+ timeinseconds = time.time() - time.mktime(datetime.date.today().timetuple())
+ quiethoursstart = self._settings['quiethoursstarttime']
+ quiethoursend = self._settings['quiethoursendtime']
+
+ # Check if the current time is between the start time and end time
+ if quiethoursstart < quiethoursend:
+ active = quiethoursstart <= timeinseconds and timeinseconds < quiethoursend
+ else: # End time is lower than start time, example Start: 21:00, end: 08:00
+ active = not (quiethoursend < timeinseconds and timeinseconds < quiethoursstart)
+
+ if self._dbusservice['/QuietHours'] == 0 and active:
+ self.log_info('Entering to quiet mode')
+
+ elif self._dbusservice['/QuietHours'] == 1 and not active:
+ self.log_info('Leaving quiet mode')
+
+ self._dbusservice['/QuietHours'] = int(active)
+
+ return active
+
+ def _update_accumulated_time(self, gensetHours=None):
+
+ # Check if this instance is connected to a genset which reports operating hours.
+ # If so, synchronize with that.
+ if (self._useGensetHours):
+ if (gensetHours is None):
+ gensetHours = self._dbusmonitor.get_value(self._remoteservice, '/Engine/OperatingHours', None)
+
+ # Failsafe
+ if (gensetHours is not None):
+ # If connected genset reports /Engine/OperatingHours, use that and also clear the offset value.
+ self._settings['accumulatedtotalOffset'] = 0
+ self._settings['accumulatedtotal'] = accumulatedtotal = gensetHours
+ else:
+ # Do not use genset hours
+ gensetHours = None
+
+ seconds = self._dbusservice['/Runtime']
+ accumulated = seconds - self._last_runtime_update
+
+ self._settings['accumulatedtotal'] = accumulatedtotal = gensetHours or int(self._settings['accumulatedtotal']) + accumulated
+ # Using calendar to get timestamp in UTC, not local time
+ today_date = str(calendar.timegm(datetime.date.today().timetuple()))
+
+ # If something goes wrong getting the json string create a new one
+ try:
+ accumulated_days = json.loads(self._settings['accumulateddaily'])
+ except ValueError:
+ accumulated_days = {today_date: 0}
+
+ if (today_date in accumulated_days):
+ accumulated_days[today_date] += accumulated
+ else:
+ accumulated_days[today_date] = accumulated
+
+ if self._dbusservice['/State'] in (States.RUNNING, States.WARMUP, States.COOLDOWN, States.STOPPING):
+ mtime = monotonic_time.monotonic_time().to_seconds_double()
+ self._dbusservice['/Runtime'] = int(mtime - self._starttime_fb)
+
+ self._last_runtime_update = seconds
+
+ # Keep the historical with a maximum of HISTORY_DAYS
+ while len(accumulated_days) > HISTORY_DAYS:
+ accumulated_days.pop(min(accumulated_days.keys()), None)
+
+ # Update settings
+ self._settings['accumulateddaily'] = json.dumps(accumulated_days, sort_keys=True)
+ self._dbusservice['/TodayRuntime'] = self._interval_runtime(0)
+ self._dbusservice['/TestRunIntervalRuntime'] = self._interval_runtime(self._settings['testruninterval'])
+ self._dbusservice['/AccumulatedRuntime'] = accumulatedtotal
+
+ # Service counter
+ serviceinterval = self._settings['serviceinterval']
+ lastservicereset = self._settings['lastservicereset']
+ if serviceinterval > 0:
+ servicecountdown = (lastservicereset + serviceinterval) - accumulatedtotal
+ self._dbusservice['/ServiceCounter'] = servicecountdown
+ if servicecountdown <= 0:
+ self._dbusservice['/Alarms/ServiceIntervalExceeded'] = 1
+ elif self._dbusservice['/Alarms/ServiceIntervalExceeded'] != 0:
+ self._dbusservice['/Alarms/ServiceIntervalExceeded'] = 0
+
+
+
+ def _interval_runtime(self, days):
+ summ = 0
+ try:
+ daily_record = json.loads(self._settings['accumulateddaily'])
+ except ValueError:
+ return 0
+
+ for i in range(days + 1):
+ previous_day = calendar.timegm((datetime.date.today() - datetime.timedelta(days=i)).timetuple())
+ if str(previous_day) in daily_record.keys():
+ summ += daily_record[str(previous_day)] if str(previous_day) in daily_record.keys() else 0
+
+ return summ
+
+ def _get_battery(self):
+ if self._settings['batterymeasurement'] == 'default':
+ return Battery(self._dbusmonitor, SYSTEM_SERVICE, BATTERY_PREFIX)
+
+ return Battery(self._dbusmonitor,
+ self._battery_service if self._battery_service else '',
+ self._battery_prefix if self._battery_prefix else '')
+
+ def _set_capabilities(self):
+ # Update capabilities
+ # The ability to ignore AC1/AC2 came in at the same time as
+ # AC availability and is used to detect it here.
+ if self.multiservice_type == 'vebus':
+ readout_supported = self._dbusmonitor.get_value(self.multiservice,
+ '/Ac/State/AcIn1Available') is not None
+ self._dbusservice['/Capabilities'] |= (
+ Capabilities.WarmupCooldown if readout_supported else 0)
+ # /Ac/State/AcIn1Available is not available on acsystem
+ # The vebus system also has the path '/Ac/Control/IgnoreAcIn1', but we can't use this to check for the
+ # capabilities because this path was already there before ignoring ac input 2 was supported in the vebus quattro.
+ # So if we were to use '/Ac/Control/IgnoreAcIn1' to set the capabilities on a vebus system, it would
+ # also be enabled on systems with older firmware which do not support it on input 2.
+ elif self.multiservice_type == 'acsystem':
+ ignore_ac_supported = self._dbusmonitor.get_value(self.multiservice,
+ '/Ac/Control/IgnoreAcIn1') is not None
+ self._dbusservice['/Capabilities'] |= (
+ Capabilities.WarmupCooldown if ignore_ac_supported else 0)
+
+ def _determineservices(self):
+ # batterymeasurement is either 'default' or 'com_victronenergy_battery_288/Dc/0'.
+ # In case it is set to default, we use the AutoSelected battery
+ # measurement, given by SystemCalc.
+ batterymeasurement = None
+ newbatteryservice = None
+ batteryprefix = ''
+ selectedbattery = self._settings['batterymeasurement']
+ vebusservice = None
+
+ if selectedbattery == 'default':
+ batterymeasurement = 'default'
+ elif len(selectedbattery.split('/', 1)) == 2: # Only very basic sanity checking..
+ batterymeasurement = self._settings['batterymeasurement']
+ elif selectedbattery == 'nobattery':
+ batterymeasurement = None
+ else:
+ # Exception: unexpected value for batterymeasurement
+ pass
+
+ if batterymeasurement and batterymeasurement != 'default':
+ batteryprefix = '/' + batterymeasurement.split('/', 1)[1]
+
+ # Get the current battery servicename
+ if self._battery_service:
+ oldservice = self._battery_service
+ else:
+ oldservice = None
+
+ if batterymeasurement == 'default':
+ newbatteryservice = 'default'
+ elif batterymeasurement:
+ battery_instance = int(batterymeasurement.split('_', 3)[3].split('/')[0])
+ service_type = None
+
+ if 'vebus' in batterymeasurement:
+ service_type = 'vebus'
+ elif 'battery' in batterymeasurement:
+ service_type = 'battery'
+
+ newbatteryservice = self._get_servicename_by_instance(battery_instance, service_type)
+
+ if newbatteryservice and newbatteryservice != oldservice:
+ if selectedbattery == 'default':
+ self.log_info('Getting battery values from systemcalc.')
+ if selectedbattery == 'nobattery':
+ self.log_info('Battery monitoring disabled! Stop evaluating related conditions')
+ self._battery_service = None
+ self._battery_prefix = None
+ self.log_info('Battery service we need (%s) found! Using it for generator start/stop' % batterymeasurement)
+ self._battery_service = newbatteryservice
+ self._battery_prefix = batteryprefix
+ elif not newbatteryservice and newbatteryservice != oldservice:
+ self.log_info('Error getting battery service!')
+ self._battery_service = newbatteryservice
+ self._battery_prefix = batteryprefix
+
+ # Get the default VE.Bus service
+ vebusservice = self._dbusmonitor.get_value('com.victronenergy.system', '/VebusService')
+ if vebusservice:
+ if self.multiservice != vebusservice:
+ self._vebusservice = vebusservice
+ self._acsystemservice = None
+ self._set_capabilities()
+ self.log_info('Vebus service (%s) found! Using it for generator start/stop' % vebusservice)
+ else:
+ acsystemservice = self._get_acsystem_service()
+ if acsystemservice:
+ if self.multiservice != acsystemservice:
+ self._acsystemservice = acsystemservice
+ self._vebusservice = None
+ self._set_capabilities()
+ self.log_info('AC system service (%s) found! Using it for generator start/stop' % acsystemservice)
+ else:
+ if self._vebusservice is not None:
+ self.log_info('Vebus service (%s) dissapeared! Stop evaluating related conditions' % self._vebusservice)
+ elif self._acsystemservice is not None:
+ self.log_info('AC system service (%s) dissapeared! Stop evaluating related conditions' % self._acsystemservice)
+ else:
+ self.log_info('Error getting Vebus or AC system service!')
+ self._vebusservice = None
+ self._acsystemservice = None
+
+ def _get_acsystem_service(self):
+ services = self._dbusmonitor.get_service_list()
+ for service in services:
+ if service.startswith('com.victronenergy.acsystem'):
+ if self._dbusmonitor.get_value(service, '/DeviceInstance') == 0:
+ return service
+ return None
+
+ def _get_servicename_by_instance(self, instance, service_type=None):
+ sv = None
+ services = self._dbusmonitor.get_service_list()
+
+ for service in services:
+ if service_type and service_type not in service:
+ continue
+
+ if services[service] == instance:
+ sv = service
+ break
+
+ return sv
+
+ def _get_monotonic_seconds(self):
+ return monotonic_time.monotonic_time().to_seconds_double()
+
+ def _start_generator(self, condition):
+ state = self._dbusservice['/State']
+ remote_running = self._get_remote_switch_state()
+
+ # This function will start the generator in the case generator not
+ # already running. When differs, the RunningByCondition is updated
+ running = state in (States.WARMUP, States.COOLDOWN, States.STOPPING, States.RUNNING)
+ if not (running and remote_running): # STOPPED, ERROR
+ # There is an option to skip warm-up for the inverteroverload condition.
+ if self._settings['warmuptime'] and not (condition == "inverteroverload" and self._settings['inverteroverloadskipwarmup'] == 1):
+ # Remove load while warming up
+ self._set_ignore_ac(True)
+ self._dbusservice['/State'] = States.WARMUP
+ else:
+ self._dbusservice['/State'] = States.RUNNING
+
+ self._update_remote_switch()
+ self._starttime = monotonic_time.monotonic_time().to_seconds_double()
+
+ self.log_info('Starting generator by %s condition' % condition)
+ else: # WARMUP, COOLDOWN, RUNNING, STOPPING
+ if state == States.WARMUP:
+ if monotonic_time.monotonic_time().to_seconds_double() - self._starttime > self._settings['warmuptime']:
+ self._set_ignore_ac(False) # Release load onto Generator
+ self._dbusservice['/State'] = States.RUNNING
+ elif state in (States.COOLDOWN, States.STOPPING):
+ # Start request during cool-down run, go back to RUNNING
+ self._set_ignore_ac(False) # Put load back onto Generator
+ self._dbusservice['/State'] = States.RUNNING
+
+ # Update the RunningByCondition
+ if self._dbusservice['/RunningByCondition'] != condition:
+ self.log_info('Generator previously running by %s condition is now running by %s condition'
+ % (self._dbusservice['/RunningByCondition'], condition))
+
+ self._dbusservice['/RunningByCondition'] = condition
+ self._dbusservice['/RunningByConditionCode'] = RunningConditions.lookup(condition)
+
+ def _stop_generator(self):
+ state = self._dbusservice['/State']
+ remote_running = self._get_remote_switch_state()
+ running = state in (States.WARMUP, States.COOLDOWN, States.STOPPING, States.RUNNING)
+
+ if running or remote_running:
+ if self._settings['cooldowntime'] > 0:
+ if state == States.RUNNING:
+ self._dbusservice['/State'] = States.COOLDOWN
+ self._stoptime = monotonic_time.monotonic_time().to_seconds_double()
+
+ # Remove load from Generator
+ self._set_ignore_ac(True)
+
+ return
+ elif state == States.COOLDOWN:
+ if monotonic_time.monotonic_time().to_seconds_double() - \
+ self._stoptime <= self._settings['cooldowntime']:
+ return # Don't stop engine yet
+
+ # When we arrive here, a stop command was given during warmup, the
+ # cooldown timer expired, or no cooldown was configured. Stop
+ # the engine, but if we're coming from cooldown, delay another
+ # while in the STOPPING state before reactivating AC-in.
+ if state == States.COOLDOWN:
+ self._dbusservice['/State'] = States.STOPPING
+ self._update_remote_switch() # Stop engine
+ return
+ elif state == States.STOPPING:
+ if monotonic_time.monotonic_time().to_seconds_double() - \
+ self._stoptime <= self._settings['cooldowntime'] + self._settings['generatorstoptime']:
+ return # Wait for engine stop
+
+ # All other possibilities are handled now. Cooldown is over or not
+ # configured and we waited for the generator to shut down.
+ self.log_info('Stopping generator that was running by %s condition' %
+ str(self._dbusservice['/RunningByCondition']))
+ self._dbusservice['/RunningByCondition'] = ''
+ self._dbusservice['/RunningByConditionCode'] = RunningConditions.Stopped
+ self._dbusservice['/State'] = States.STOPPED
+ self._update_remote_switch()
+ self._set_ignore_ac(False)
+ self._dbusservice['/ManualStartTimer'] = 0
+ self._manualstarttimer = 0
+ self._starttime = 0
+
+ @property
+ def _ac1_is_generator(self):
+ return self._dbusmonitor.get_value('com.victronenergy.settings',
+ '/Settings/SystemSetup/AcInput1') == 2
+
+ @property
+ def _ac2_is_generator(self):
+ return self._dbusmonitor.get_value('com.victronenergy.settings',
+ '/Settings/SystemSetup/AcInput2') == 2
+
+ def _set_ignore_ac(self, ignore):
+ # This is here so the Multi/Quattro can be told to disconnect AC-in,
+ # so that we can do warm-up and cool-down.
+ if self.multiservice is not None:
+ if self._ac1_is_generator:
+ self._dbusmonitor.set_value_async(self.multiservice, '/Ac/Control/IgnoreAcIn1', dbus.Int32(ignore, variant_level=1))
+ if self._ac2_is_generator:
+ self._dbusmonitor.set_value_async(self.multiservice, '/Ac/Control/IgnoreAcIn2', dbus.Int32(ignore, variant_level=1))
+
+ def _update_remote_switch(self):
+ # Engine should be started in these states
+ v = self._dbusservice['/State'] in (States.RUNNING, States.WARMUP, States.COOLDOWN)
+ self._set_remote_switch_state(dbus.Int32(v, variant_level=1))
+
+ def _running_by_digital_input(self, path, value):
+ return
+
+ def _generator_started(self):
+ if (not self._generator_running):
+ self._starttime_fb = monotonic_time.monotonic_time().to_seconds_double()
+ self._generator_running = True
+
+ def _generator_stopped(self):
+ if (self._generator_running):
+ self._generator_running = False
+ self._update_runtime(just_stopped=True)
+ self._dbusservice['/Runtime'] = 0
+ self._starttime_fb = 0
+ self._last_runtime_update = 0
+
+ def _get_remote_switch_state(self):
+ raise Exception('This function should be overridden')
+
+ def _set_remote_switch_state(self, value):
+ raise Exception('This function should be overridden')
+
+ # Check the remote status, for example errors
+ def _check_remote_status(self):
+ raise Exception('This function should be overridden')
+
+ def _remote_setup(self):
+ raise Exception('This function should be overridden')
+
+ def _create_dbus_monitor(self, *args, **kwargs):
+ raise Exception('This function should be overridden')
+
+ def _create_settings(self, *args, **kwargs):
+ raise Exception('This function should be overridden')
+
+ def _create_dbus_service(self):
+ return create_dbus_service(self._instance)
diff --git a/FileSets/PatchSource/startstop.py-v3.60~25.patch b/FileSets/PatchSource/startstop.py-v3.60~25.patch
new file mode 100644
index 00000000..872a82cb
--- /dev/null
+++ b/FileSets/PatchSource/startstop.py-v3.60~25.patch
@@ -0,0 +1,539 @@
+--- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/startstop.py-v3.60~25.orig 2025-02-25 09:42:59
++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/startstop.py-v3.60~25 2025-02-25 09:42:55
+@@ -1,6 +1,17 @@
+ #!/usr/bin/python -u
+ # -*- coding: utf-8 -*-
+
++#### GuiMods
++#### This file has been modified to allow the generator running state derived from the generator digital input
++#### Previous versions also used the genset AC input but this has been removed from this version with recent changes to stock code !!!!!
++#### If the incoming generator state changes, the manual start state is updated
++#### A switch in the generator settings menu controls whethter the incoming state affects manual start or time accumulaiton
++#### It is now possible to start the generator manually and have it stop automatically based on the preset conditions
++#### for automaitc start / stop
++#### warm-up and cool-down periods have been modified in order to work with an external transfer switch
++#### selecting grid or generator ahead of a MultiPlus input.
++#### Search for #### GuiMods to find changes
++
+ # Function
+ # dbus_generator monitors the dbus for batteries (com.victronenergy.battery.*) and
+ # vebus com.victronenergy.vebus.*
+@@ -272,6 +283,23 @@
+ class StartStop(object):
+ _driver = None
+ def __init__(self, instance):
++#### GuiMods #### TODO: check if any of these are needed
++ logging.info ("GuiMods version of startstop.py")
++ self._currentTime = self._get_monotonic_seconds()
++ self._last_update_mtime = 0
++ self._accumulatedRunTime = 0
++ self._lastIsRunning = False
++ self._externalOverrideDelay = 99
++ self._linkToExternalState = False
++#### GuiMods warm-up / cool-down
++ self._warmUpEndTime = 0
++ self._coolDownEndTime = 0
++ self._ac1isIgnored = False
++ self._ac2isIgnored = False
++ self._activeAcInIsIgnored = False
++ self._acInIsGenerator = False
++#### end GuiMods
++
+ self._dbusservice = None
+ self._settings = None
+ self._dbusmonitor = None
+@@ -302,9 +330,10 @@
+ self._timer_runnning = 0
+
+ # The installer left autostart disabled
+- self._autostart_last_time = self._get_monotonic_seconds()
+- self._remote_start_mode_last_time = self._get_monotonic_seconds()
++ self._autostart_last_time = self._currentTime
++ self._remote_start_mode_last_time = self._currentTime
+
++
+ # Manual battery service selection is deprecated in favour
+ # of getting the values directly from systemcalc, we keep
+ # manual selected services handling for compatibility reasons.
+@@ -440,6 +469,14 @@
+ # When this startstop instance controls a genset which reports operatinghours, make sure to synchronize with that.
+ self._useGensetHours = self._dbusmonitor.get_value(self._remoteservice, '/Engine/OperatingHours', None) is not None
+
++#### GuiMods
++ # generator input running state
++ # external override active
++ self._dbusservice.add_path('/ExternalOverride', value=None)
++ self._dbusservice['/ExternalOverride'] = False
++ self._ignoreAutoStartCondition = False
++
++
+ @property
+ def _is_running(self):
+ return self._generator_running
+@@ -578,13 +615,44 @@
+ def tick(self):
+ if not self._enabled:
+ return
++
++#### GuiMods warm-up / cool-down
++ self._currentTime = self._get_monotonic_seconds ()
++
+ self._check_remote_status()
++#### GuiMods
++ self._linkToExternalState = self._settings['linkManualStartToExternal'] == 1
++ self.syncManualRunToExternalState ()
++
+ self._evaluate_startstop_conditions()
+ self._evaluate_autostart_disabled_alarm()
+ self._detect_generator_at_input()
+ if self._dbusservice['/ServiceCounterReset'] == 1:
+ self._dbusservice['/ServiceCounterReset'] = 0
+
++#### GuiMods warm-up / cool-down
++
++ # shed load for active generator input in warm-up and cool-down
++ # note that external transfer switch might change the state of on generator
++ # so this needs to be checked and load adjusted every pass
++ # restore load for sources no longer in use or if state is not in warm-up/cool-down
++ # restoring load is delayed 1following end of cool-down
++ # to allow the generator to actually stop producing power
++ state = self._dbusservice['/State']
++ if state in (States.WARMUP, States.COOLDOWN, States.STOPPING):
++ self._set_ignore_ac (True)
++ else:
++ self._set_ignore_ac (False)
++
++ # update cool down end time while running and generator has the load
++ # this is done because acInIsGenerator may change by an external transfer switch
++ # and we want an accurate picture of the cool down end time
++ # based on the last time the generatot was loaded
++ if state == States.RUNNING and self._acInIsGenerator:
++ self._coolDownEndTime = self._currentTime + self._settings['cooldowntime']
++#### end GuiMods warm-up / cool-down
++
++
+ def _evaluate_startstop_conditions(self):
+ if self.get_error() != Errors.NONE:
+ # First evaluation after an error, log it
+@@ -617,10 +685,24 @@
+
+ self._update_runtime()
+
+- if self._evaluate_manual_start():
+- startbycondition = 'manual'
+- start = True
++#### GuiMods
++ # A negative /ManualStartTimer is used by the GUI to signal the generator should start now
++ # but stop when all auto stop conditions have been met
++ # so we skip manual start evaluation if this is the case
++ # and set a flag for use below to ignore auto start conditions
++ # the generator is actually started by the auto start/stop logic below
++ if self._dbusservice['/ManualStartTimer'] < 0 and self._dbusservice['/ManualStart'] == 1:
++ self._dbusservice['/ManualStartTimer'] = 0
++ self._dbusservice['/ManualStart'] = 0
++ self._ignoreAutoStartCondition = True
+
++ else:
++ self._ignoreAutoStartCondition = False
++ if self._evaluate_manual_start():
++ startbycondition = 'manual'
++ start = True
++#### end GuiMods
++
+ # Conditions will only be evaluated if the autostart functionality is enabled
+ if self._settings['autostart'] == 1:
+
+@@ -669,6 +751,12 @@
+ if running and self._settings['onlosscommunication'] == 2:
+ start = True
+ startbycondition = 'lossofcommunication'
++
++#### GuiMods
++ ## auto start disabled and generator is stopped - clear the 'reached' flags
++ elif self._dbusservice['/State'] == States.STOPPED:
++ for condition, data in self._condition_stack.items():
++ self._reset_condition(data)
+
+ if not start and self._errorstate:
+ self._stop_generator()
+@@ -676,12 +764,14 @@
+ if self._errorstate:
+ return
+
+- mtime = monotonic_time.monotonic_time().to_seconds_double()
+ if start:
+ self._start_generator(startbycondition)
+- elif (int(mtime - self._starttime) >= self._settings['minimumruntime'] * 60
+- or activecondition == 'manual'):
++#### GuiMods
++ # bypass the minimum run time check if External Override is active
++ elif (self._dbusservice['/Runtime'] >= self._settings['minimumruntime'] * 60
++ or activecondition == 'manual') or self._dbusservice['/ExternalOverride']:
+ self._stop_generator()
++#### end GuiMods
+
+ def _update_runtime(self, just_stopped=False):
+ # Update current and accumulated runtime.
+@@ -698,8 +788,8 @@
+ def _evaluate_autostart_disabled_alarm(self):
+
+ if self._settings['autostartdisabledalarm'] == 0:
+- self._autostart_last_time = self._get_monotonic_seconds()
+- self._remote_start_mode_last_time = self._get_monotonic_seconds()
++ self._autostart_last_time = self._currentTime
++ self._remote_start_mode_last_time = self._currentTime
+ if self._dbusservice['/Alarms/AutoStartDisabled'] != 0:
+ self._dbusservice['/Alarms/AutoStartDisabled'] = 0
+ if self._dbusservice['/Alarms/RemoteStartModeDisabled'] != 0:
+@@ -708,35 +798,37 @@
+
+ # GX auto start/stop alarm
+ if self._settings['autostart'] == 1:
+- self._autostart_last_time = self._get_monotonic_seconds()
++ self._autostart_last_time = self._currentTime
+ if self._dbusservice['/Alarms/AutoStartDisabled'] != 0:
+ self._dbusservice['/Alarms/AutoStartDisabled'] = 0
+ else:
+- timedisabled = self._get_monotonic_seconds() - self._autostart_last_time
++ timedisabled = self._currentTime - self._autostart_last_time
+ if timedisabled > AUTOSTART_DISABLED_ALARM_TIME and self._dbusservice['/Alarms/AutoStartDisabled'] != 2:
+ self.log_info("Autostart was left for more than %i seconds, triggering alarm." % int(timedisabled))
+ self._dbusservice['/Alarms/AutoStartDisabled'] = 2
+
+ # Genset remote start mode alarm
+ if self.get_error() != Errors.REMOTEDISABLED:
+- self._remote_start_mode_last_time = self._get_monotonic_seconds()
++ self._remote_start_mode_last_time = self._currentTime
+ if self._dbusservice['/Alarms/RemoteStartModeDisabled'] != 0:
+ self._dbusservice['/Alarms/RemoteStartModeDisabled'] = 0
+ else:
+- timedisabled = self._get_monotonic_seconds() - self._remote_start_mode_last_time
++ timedisabled = self._currentTime - self._remote_start_mode_last_time
+ if timedisabled > AUTOSTART_DISABLED_ALARM_TIME and self._dbusservice['/Alarms/RemoteStartModeDisabled'] != 2:
+ self.log_info("Autostart was left for more than %i seconds, triggering alarm." % int(timedisabled))
+ self._dbusservice['/Alarms/RemoteStartModeDisabled'] = 2
+
++#### GuiMods warm-up / cool-down - rewrote so acInIsGenerator is updated even if alarm is disabled
+ def _detect_generator_at_input(self):
+- state = self._dbusservice['/State']
++ self._acInIsGenerator = False # covers all conditions that result in a return
+
++ state = self._dbusservice['/State']
+ if state in [States.STOPPED, States.COOLDOWN, States.WARMUP]:
+ self._reset_power_input_timer()
+ return
+
+ if self._settings['nogeneratoratacinalarm'] == 0:
+- self._reset_power_input_timer()
++ self._reset_acpower_inverter_input()
+ return
+
+ if self.multiservice_type == 'vebus':
+@@ -757,23 +849,38 @@
+ # Not connected = 0, connected = 1
+ activein_connected = activein_state == 1
+
++#### GuiMods warm-up / cool-down
++ if self._settings['nogeneratoratacinalarm'] == 0:
++ processAlarm = False
++ self._reset_acpower_inverter_input()
++ else:
++ processAlarm = True
++
+ if generator_acsource and activein_connected:
+- if self._power_input_timer['unabletostart']:
++#### GuiMods warm-up / cool-down
++ self._acInIsGenerator = True
++#### GuiMods warm-up / cool-down
++ if processAlarm and self._power_input_timer['unabletostart']:
+ self.log_info('Generator detected at inverter AC input, alarm removed')
+- self._reset_power_input_timer()
+- elif self._power_input_timer['timeout'] < self.RETRIES_ON_ERROR:
+- self._power_input_timer['timeout'] += 1
+- elif not self._power_input_timer['unabletostart']:
+- self._power_input_timer['unabletostart'] = True
++ self._reset_power_inverter_input()
++#### GuiMods warm-up / cool-down
++ elif not processAlarm:
++ self._reset_power_inverter_input()
++ return
++ elif self._aower_inverter_input['timeout'] < self.RETRIES_ON_ERROR:
++ self._power_inverter_input['timeout'] += 1
++ elif not self._power_inverter_input['unabletostart']:
++ self._power_inverter_input['unabletostart'] = True
+ self._dbusservice['/Alarms/NoGeneratorAtAcIn'] = 2
+ self.log_info('Generator not detected at inverter AC input, triggering alarm')
++#### end GuiMods
+
+ def _reset_power_input_timer(self):
+- if self._power_input_timer['timeout'] != 0:
+- self._power_input_timer['timeout'] = 0
++ if self._acpower_inverter_input['timeout'] != 0:
++ self._acpower_inverter_input['timeout'] = 0
+
+- if self._power_input_timer['unabletostart'] != 0:
+- self._power_input_timer['unabletostart'] = False
++ if self._acpower_inverter_input['unabletostart'] != 0:
++ self._acpower_inverter_input['unabletostart'] = 0
+
+ self._dbusservice['/Alarms/NoGeneratorAtAcIn'] = 0
+
+@@ -851,8 +958,20 @@
+
+ # When the condition is already reached only the stop value can set it to False
+ start = condition['reached'] or (value >= startvalue if start_is_greater else value <= startvalue)
++ stop = value <= stopvalue if start_is_greater else value >= stopvalue
++
++#### GuiMods
+ stop = value <= stopvalue if start_is_greater else value >= stopvalue
++ # when starting manually and stopping based on auto stop values,
++ # start if stop condition is not satisfied
+
++ if self._ignoreAutoStartCondition:
++ start = not stop
++ else:
++ # When the condition is already reached only the stop value can set it to False
++ start = condition['reached'] or (value >= startvalue if start_is_greater else value <= startvalue)
++#### end GuiMods
++
+ # Timed conditions must start/stop after the condition has been reached for a minimum
+ # time.
+ if condition['timed']:
+@@ -885,7 +1004,8 @@
+ # If /ManualStartTimer has a value greater than zero will use it to set a stop timer.
+ # If no timer is set, the generator will not stop until the user stops it manually.
+ # Once started by manual start, each evaluation the timer is decreased
+- if self._dbusservice['/ManualStartTimer'] != 0:
++#### GuiMods - change test to > 0 from != 0 to allow for start now / auto stop
++ if self._dbusservice['/ManualStartTimer'] > 0:
+ self._manualstarttimer += time.time() if self._manualstarttimer == 0 else 0
+ self._dbusservice['/ManualStartTimer'] -= int(time.time()) - int(self._manualstarttimer)
+ self._manualstarttimer = time.time()
+@@ -1219,31 +1339,40 @@
+ running = state in (States.WARMUP, States.COOLDOWN, States.STOPPING, States.RUNNING)
+ if not (running and remote_running): # STOPPED, ERROR
+ # There is an option to skip warm-up for the inverteroverload condition.
+- if self._settings['warmuptime'] and not (condition == "inverteroverload" and self._settings['inverteroverloadskipwarmup'] == 1):
+- # Remove load while warming up
+- self._set_ignore_ac(True)
++#### GuiMods warm-up / cool-down
++ self.log_info('Starting generator by %s condition' % condition)
++ # if there is a warmup time specified, always go through warm-up state
++ # regardless of AC input in use
++ warmUpPeriod = self._settings['warmuptime']
++ if warmUpPeriod > 0:
++ self._warmUpEndTime = self._currentTime + warmUpPeriod
++ self.log_info ("starting warm-up")
+ self._dbusservice['/State'] = States.WARMUP
++ # no warm-up go directly to running
+ else:
+ self._dbusservice['/State'] = States.RUNNING
++ self._warmUpEndTime = 0
+
+- self._update_remote_switch()
+- self._starttime = monotonic_time.monotonic_time().to_seconds_double()
++ self._coolDownEndTime = 0
++ self._stoptime = 0
+
+- self.log_info('Starting generator by %s condition' % condition)
++ self._update_remote_switch()
+ else: # WARMUP, COOLDOWN, RUNNING, STOPPING
+- if state == States.WARMUP:
+- if monotonic_time.monotonic_time().to_seconds_double() - self._starttime > self._settings['warmuptime']:
+- self._set_ignore_ac(False) # Release load onto Generator
+- self._dbusservice['/State'] = States.RUNNING
+- elif state in (States.COOLDOWN, States.STOPPING):
++ if state in (States.COOLDOWN, States.STOPPING):
+ # Start request during cool-down run, go back to RUNNING
+- self._set_ignore_ac(False) # Put load back onto Generator
++ self.log_info ("aborting cool-down - returning to running")
+ self._dbusservice['/State'] = States.RUNNING
+
++ elif state == States.WARMUP:
++ if self._currentTime > self._warmUpEndTime:
++ self.log_info ("warm-up complete")
++ self._dbusservice['/State'] = States.RUNNING
++
+ # Update the RunningByCondition
+ if self._dbusservice['/RunningByCondition'] != condition:
+ self.log_info('Generator previously running by %s condition is now running by %s condition'
+ % (self._dbusservice['/RunningByCondition'], condition))
++#### end GuiMods warm-up / cool-down
+
+ self._dbusservice['/RunningByCondition'] = condition
+ self._dbusservice['/RunningByConditionCode'] = RunningConditions.lookup(condition)
+@@ -1254,46 +1383,47 @@
+ running = state in (States.WARMUP, States.COOLDOWN, States.STOPPING, States.RUNNING)
+
+ if running or remote_running:
+- if self._settings['cooldowntime'] > 0:
+- if state == States.RUNNING:
+- self._dbusservice['/State'] = States.COOLDOWN
+- self._stoptime = monotonic_time.monotonic_time().to_seconds_double()
++#### GuiMods warm-up / cool-down
++ if state == States.RUNNING:
++ state = States.COOLDOWN
++ if self._currentTime < self._coolDownEndTime:
++ self.log_info ("starting cool-down")
++ elif self._settings['cooldowntime'] != 0:
++ self.log_info ("skipping cool-down -- no AC load on generator")
+
+- # Remove load from Generator
+- self._set_ignore_ac(True)
++ # warm-up should also transition to stopping
++ # cool-down time will have expired since it's set to 0 when starting
++ # and there has not yet been a load on the generator
++ if state in (States.WARMUP, States.COOLDOWN):
++ # cool down complete
++ if self._currentTime > self._coolDownEndTime:
++ state = States.STOPPING
++ self.log_info('Stopping generator that was running by %s condition' %
++ str(self._dbusservice['/RunningByCondition']))
++ self._update_remote_switch() # Stop engine
++ self._stoptime = self._currentTime + self._settings['generatorstoptime']
++ if self._currentTime < self._stoptime:
++ self.log_info ("waiting for generator so stop")
++
++ if state == States.STOPPING:
++ # wait for stop period expired - finish up transition to STOPPED
++ if self._currentTime > self._stoptime:
++ if self._settings['generatorstoptime'] != 0:
++ self.log_info ("generator stop time reached - OK to reconnect AC")
++ state = States.STOPPED
++ self._update_remote_switch()
++ self._dbusservice['/RunningByCondition'] = ''
++ self._dbusservice['/RunningByConditionCode'] = RunningConditions.Stopped
++ self._update_accumulated_time()
++ self._starttime = 0
++ self._dbusservice['/Runtime'] = 0
++ self._dbusservice['/ManualStartTimer'] = 0
++ self._manualstarttimer = 0
++ self._last_runtime_update = 0
+
+- return
+- elif state == States.COOLDOWN:
+- if monotonic_time.monotonic_time().to_seconds_double() - \
+- self._stoptime <= self._settings['cooldowntime']:
+- return # Don't stop engine yet
++ self._dbusservice['/State'] = state
++#### end GuiMods warm-up / cool-down
+
+- # When we arrive here, a stop command was given during warmup, the
+- # cooldown timer expired, or no cooldown was configured. Stop
+- # the engine, but if we're coming from cooldown, delay another
+- # while in the STOPPING state before reactivating AC-in.
+- if state == States.COOLDOWN:
+- self._dbusservice['/State'] = States.STOPPING
+- self._update_remote_switch() # Stop engine
+- return
+- elif state == States.STOPPING:
+- if monotonic_time.monotonic_time().to_seconds_double() - \
+- self._stoptime <= self._settings['cooldowntime'] + self._settings['generatorstoptime']:
+- return # Wait for engine stop
+-
+- # All other possibilities are handled now. Cooldown is over or not
+- # configured and we waited for the generator to shut down.
+- self.log_info('Stopping generator that was running by %s condition' %
+- str(self._dbusservice['/RunningByCondition']))
+- self._dbusservice['/RunningByCondition'] = ''
+- self._dbusservice['/RunningByConditionCode'] = RunningConditions.Stopped
+- self._dbusservice['/State'] = States.STOPPED
+- self._update_remote_switch()
+- self._set_ignore_ac(False)
+- self._dbusservice['/ManualStartTimer'] = 0
+- self._manualstarttimer = 0
+- self._starttime = 0
+-
+ @property
+ def _ac1_is_generator(self):
+ return self._dbusmonitor.get_value('com.victronenergy.settings',
+@@ -1304,15 +1434,41 @@
+ return self._dbusmonitor.get_value('com.victronenergy.settings',
+ '/Settings/SystemSetup/AcInput2') == 2
+
++#### GuiMods warm-up / cool-down
++ # stock code does not handle changes in the input type
++ # which could happen with an external transfer switch
++ # doing things this way should handle it
++
+ def _set_ignore_ac(self, ignore):
+ # This is here so the Multi/Quattro can be told to disconnect AC-in,
+ # so that we can do warm-up and cool-down.
+- if self.multiservice is not None:
+- if self._ac1_is_generator:
+- self._dbusmonitor.set_value_async(self.multiservice, '/Ac/Control/IgnoreAcIn1', dbus.Int32(ignore, variant_level=1))
+- if self._ac2_is_generator:
+- self._dbusmonitor.set_value_async(self.multiservice, '/Ac/Control/IgnoreAcIn2', dbus.Int32(ignore, variant_level=1))
++ if self.multiservice is None:
++ return
++ self._activeAcInIsIgnored = ignore
++ ignore1 = False
++ ignore2 = False
++ if self._ac1_is_generator:
++ ignore1 = ignore
++ elif self._ac2_is_generator:
++ ignore2 = ignore
+
++ if ignore1 != self._ac1isIgnored:
++ if ignore1:
++ self.log_info ("shedding load - AC input 1")
++ else:
++ self.log_info ("restoring load - AC input 1")
++ self._dbusmonitor.set_value_async(self.multiservice, '/Ac/Control/IgnoreAcIn1', dbus.Int32(ignore1, variant_level=1))
++ self._ac1isIgnored = ignore1
++
++ if ignore2 != self._ac2isIgnored:
++ if ignore2:
++ self.log_info ("shedding load - AC input 2")
++ else:
++ self.log_info ("restoring load - AC input 2")
++ self._dbusmonitor.set_value_async(self.multiservice, '/Ac/Control/IgnoreAcIn2', dbus.Int32(ignore2, variant_level=1))
++ self._ac2isIgnored = ignore2
++#### end GuiMods warm-up / cool-down
++
+ def _update_remote_switch(self):
+ # Engine should be started in these states
+ v = self._dbusservice['/State'] in (States.RUNNING, States.WARMUP, States.COOLDOWN)
+@@ -1355,3 +1511,42 @@
+
+ def _create_dbus_service(self):
+ return create_dbus_service(self._instance)
++
++#### GuiMods
++# this function connects the generator digital input (if any)
++# to the generator /ManualStart
++#
++# if the generator digital input changes from stopped to running
++# AND no run conditions are active, a manual start is innitiated
++#
++# if the generator digital input changes from running to stopped
++# AND a manual start is active, a manual stop is innitiated
++#
++# /ExternalOverride is used by the GUI to alert the user when there is a conflict
++# between the running state reported by the generator and the expected state
++# /ExternalOverride is True if the states differ
++# activation is delayed 5 seconds to allow transitions to settle
++
++ def syncManualRunToExternalState (self):
++ internalRun = self._dbusservice['/State'] in (States.RUNNING, States.WARMUP, States.COOLDOWN)
++ externalRun = self._is_running
++ # forward input state changes to /ManualStart
++ if self._linkToExternalState and externalRun != self._lastIsRunning:
++ if externalRun and not internalRun:
++ self.log_info ("generator was started externally - syncing ManualStart state")
++ self._dbusservice['/ManualStart'] = 1
++ elif not externalRun and internalRun and self._dbusservice['/ManualStart'] == 1:
++ self.log_info ("generator was stopped externally - syncing ManualStart state")
++ self._dbusservice['/ManualStart'] = 0
++ self._lastIsRunning = externalRun
++
++ # update ExternalOverride
++ if externalRun != internalRun:
++ if self._externalOverrideDelay > 5:
++ self._dbusservice['/ExternalOverride'] = 1
++ else:
++ self._externalOverrideDelay += 1
++ else:
++ self._dbusservice['/ExternalOverride'] = 0
++ self._externalOverrideDelay = 0
++#### end GuiMods
diff --git a/FileSets/VersionIndependent/PageSettingsGuiModsGauges.qml b/FileSets/VersionIndependent/PageSettingsGuiModsGauges.qml
index 0c6edd3f..a2527fcd 100644
--- a/FileSets/VersionIndependent/PageSettingsGuiModsGauges.qml
+++ b/FileSets/VersionIndependent/PageSettingsGuiModsGauges.qml
@@ -9,7 +9,7 @@ MbPage {
title: qsTr("Gui Mods power gauges")
property string bindPrefixGuiMods: "com.victronenergy.settings/Settings/GuiMods"
- model: VisualItemModel
+ model: VisibleItemModel
{
MbSwitch
{
diff --git a/FileSets/v3.10/Battery.qml b/FileSets/v3.10/Battery.qml
deleted file mode 120000
index fafc7b36..00000000
--- a/FileSets/v3.10/Battery.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Battery.qml
\ No newline at end of file
diff --git a/FileSets/v3.10/MbEditBox.qml b/FileSets/v3.10/MbEditBox.qml
deleted file mode 120000
index 8381b7d5..00000000
--- a/FileSets/v3.10/MbEditBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~10/MbEditBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.10/MbEditBoxDateTime.qml b/FileSets/v3.10/MbEditBoxDateTime.qml
deleted file mode 120000
index 7fb351ea..00000000
--- a/FileSets/v3.10/MbEditBoxDateTime.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbEditBoxDateTime.qml
\ No newline at end of file
diff --git a/FileSets/v3.10/MbItem.qml b/FileSets/v3.10/MbItem.qml
deleted file mode 120000
index 048a313c..00000000
--- a/FileSets/v3.10/MbItem.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbItem.qml
\ No newline at end of file
diff --git a/FileSets/v3.10/MbItemDigitalInput.qml b/FileSets/v3.10/MbItemDigitalInput.qml
deleted file mode 120000
index 4404b520..00000000
--- a/FileSets/v3.10/MbItemDigitalInput.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbItemDigitalInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.10/MbSpinBox.qml b/FileSets/v3.10/MbSpinBox.qml
deleted file mode 120000
index bc7530c8..00000000
--- a/FileSets/v3.10/MbSpinBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbSpinBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.10/MbStyle.qml b/FileSets/v3.10/MbStyle.qml
deleted file mode 120000
index d1940446..00000000
--- a/FileSets/v3.10/MbStyle.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbStyle.qml
\ No newline at end of file
diff --git a/FileSets/v3.10/MbSubMenu.qml b/FileSets/v3.10/MbSubMenu.qml
deleted file mode 120000
index 35516a9a..00000000
--- a/FileSets/v3.10/MbSubMenu.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbSubMenu.qml
\ No newline at end of file
diff --git a/FileSets/v3.10/Multi.qml b/FileSets/v3.10/Multi.qml
deleted file mode 120000
index aa938460..00000000
--- a/FileSets/v3.10/Multi.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Multi.qml
\ No newline at end of file
diff --git a/FileSets/v3.10/OverviewBox.qml b/FileSets/v3.10/OverviewBox.qml
deleted file mode 120000
index 96aa4214..00000000
--- a/FileSets/v3.10/OverviewBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.10/OverviewConnection.qml b/FileSets/v3.10/OverviewConnection.qml
deleted file mode 120000
index d833fd05..00000000
--- a/FileSets/v3.10/OverviewConnection.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewConnection.qml
\ No newline at end of file
diff --git a/FileSets/v3.10/OverviewConnectionEnd.qml b/FileSets/v3.10/OverviewConnectionEnd.qml
deleted file mode 120000
index 50c4b471..00000000
--- a/FileSets/v3.10/OverviewConnectionEnd.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewConnectionEnd.qml
\ No newline at end of file
diff --git a/FileSets/v3.10/OverviewSolarCharger.qml b/FileSets/v3.10/OverviewSolarCharger.qml
deleted file mode 120000
index 7353cec9..00000000
--- a/FileSets/v3.10/OverviewSolarCharger.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewSolarCharger.qml
\ No newline at end of file
diff --git a/FileSets/v3.10/OverviewSolarInverter.qml b/FileSets/v3.10/OverviewSolarInverter.qml
deleted file mode 120000
index 4048703e..00000000
--- a/FileSets/v3.10/OverviewSolarInverter.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewSolarInverter.qml
\ No newline at end of file
diff --git a/FileSets/v3.10/OverviewTankDelegate.qml b/FileSets/v3.10/OverviewTankDelegate.qml
deleted file mode 120000
index faeb9aeb..00000000
--- a/FileSets/v3.10/OverviewTankDelegate.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewTankDelegate.qml
\ No newline at end of file
diff --git a/FileSets/v3.10/OverviewTanks.qml b/FileSets/v3.10/OverviewTanks.qml
deleted file mode 120000
index 645a324b..00000000
--- a/FileSets/v3.10/OverviewTanks.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewTanks.qml
\ No newline at end of file
diff --git a/FileSets/v3.10/PageDigitalInput.qml b/FileSets/v3.10/PageDigitalInput.qml
deleted file mode 120000
index 5b768f8f..00000000
--- a/FileSets/v3.10/PageDigitalInput.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/PageDigitalInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.10/PageMain.qml b/FileSets/v3.10/PageMain.qml
deleted file mode 120000
index 2f68fb1c..00000000
--- a/FileSets/v3.10/PageMain.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.14/PageMain.qml
\ No newline at end of file
diff --git a/FileSets/v3.10/PageSettingsDisplay.qml b/FileSets/v3.10/PageSettingsDisplay.qml
deleted file mode 120000
index 9860c031..00000000
--- a/FileSets/v3.10/PageSettingsDisplay.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.14/PageSettingsDisplay.qml
\ No newline at end of file
diff --git a/FileSets/v3.10/Tile.qml b/FileSets/v3.10/Tile.qml
deleted file mode 120000
index 0558cecf..00000000
--- a/FileSets/v3.10/Tile.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Tile.qml
\ No newline at end of file
diff --git a/FileSets/v3.10/TileText.qml b/FileSets/v3.10/TileText.qml
deleted file mode 120000
index 8d7c0778..00000000
--- a/FileSets/v3.10/TileText.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/TileText.qml
\ No newline at end of file
diff --git a/FileSets/v3.10/attributes.csv b/FileSets/v3.10/attributes.csv
deleted file mode 120000
index 61a61302..00000000
--- a/FileSets/v3.10/attributes.csv
+++ /dev/null
@@ -1 +0,0 @@
-../v3.14/attributes.csv
\ No newline at end of file
diff --git a/FileSets/v3.10/dbus_generator.py b/FileSets/v3.10/dbus_generator.py
deleted file mode 120000
index b22d702a..00000000
--- a/FileSets/v3.10/dbus_generator.py
+++ /dev/null
@@ -1 +0,0 @@
-../v3.14/dbus_generator.py
\ No newline at end of file
diff --git a/FileSets/v3.10/styles.css b/FileSets/v3.10/styles.css
deleted file mode 120000
index ef8557ec..00000000
--- a/FileSets/v3.10/styles.css
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/styles.css
\ No newline at end of file
diff --git a/FileSets/v3.11/Battery.qml b/FileSets/v3.11/Battery.qml
deleted file mode 120000
index fafc7b36..00000000
--- a/FileSets/v3.11/Battery.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Battery.qml
\ No newline at end of file
diff --git a/FileSets/v3.11/MbEditBox.qml b/FileSets/v3.11/MbEditBox.qml
deleted file mode 120000
index 8381b7d5..00000000
--- a/FileSets/v3.11/MbEditBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~10/MbEditBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.11/MbEditBoxDateTime.qml b/FileSets/v3.11/MbEditBoxDateTime.qml
deleted file mode 120000
index 7fb351ea..00000000
--- a/FileSets/v3.11/MbEditBoxDateTime.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbEditBoxDateTime.qml
\ No newline at end of file
diff --git a/FileSets/v3.11/MbItem.qml b/FileSets/v3.11/MbItem.qml
deleted file mode 120000
index 048a313c..00000000
--- a/FileSets/v3.11/MbItem.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbItem.qml
\ No newline at end of file
diff --git a/FileSets/v3.11/MbItemDigitalInput.qml b/FileSets/v3.11/MbItemDigitalInput.qml
deleted file mode 120000
index 4404b520..00000000
--- a/FileSets/v3.11/MbItemDigitalInput.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbItemDigitalInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.11/MbSpinBox.qml b/FileSets/v3.11/MbSpinBox.qml
deleted file mode 120000
index bc7530c8..00000000
--- a/FileSets/v3.11/MbSpinBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbSpinBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.11/MbStyle.qml b/FileSets/v3.11/MbStyle.qml
deleted file mode 120000
index d1940446..00000000
--- a/FileSets/v3.11/MbStyle.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbStyle.qml
\ No newline at end of file
diff --git a/FileSets/v3.11/MbSubMenu.qml b/FileSets/v3.11/MbSubMenu.qml
deleted file mode 120000
index 35516a9a..00000000
--- a/FileSets/v3.11/MbSubMenu.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbSubMenu.qml
\ No newline at end of file
diff --git a/FileSets/v3.11/Multi.qml b/FileSets/v3.11/Multi.qml
deleted file mode 120000
index aa938460..00000000
--- a/FileSets/v3.11/Multi.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Multi.qml
\ No newline at end of file
diff --git a/FileSets/v3.11/OverviewBox.qml b/FileSets/v3.11/OverviewBox.qml
deleted file mode 120000
index 96aa4214..00000000
--- a/FileSets/v3.11/OverviewBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.11/OverviewConnection.qml b/FileSets/v3.11/OverviewConnection.qml
deleted file mode 120000
index d833fd05..00000000
--- a/FileSets/v3.11/OverviewConnection.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewConnection.qml
\ No newline at end of file
diff --git a/FileSets/v3.11/OverviewConnectionEnd.qml b/FileSets/v3.11/OverviewConnectionEnd.qml
deleted file mode 120000
index 50c4b471..00000000
--- a/FileSets/v3.11/OverviewConnectionEnd.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewConnectionEnd.qml
\ No newline at end of file
diff --git a/FileSets/v3.11/OverviewSolarCharger.qml b/FileSets/v3.11/OverviewSolarCharger.qml
deleted file mode 120000
index 7353cec9..00000000
--- a/FileSets/v3.11/OverviewSolarCharger.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewSolarCharger.qml
\ No newline at end of file
diff --git a/FileSets/v3.11/OverviewSolarInverter.qml b/FileSets/v3.11/OverviewSolarInverter.qml
deleted file mode 120000
index 4048703e..00000000
--- a/FileSets/v3.11/OverviewSolarInverter.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewSolarInverter.qml
\ No newline at end of file
diff --git a/FileSets/v3.11/OverviewTankDelegate.qml b/FileSets/v3.11/OverviewTankDelegate.qml
deleted file mode 120000
index faeb9aeb..00000000
--- a/FileSets/v3.11/OverviewTankDelegate.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewTankDelegate.qml
\ No newline at end of file
diff --git a/FileSets/v3.11/OverviewTanks.qml b/FileSets/v3.11/OverviewTanks.qml
deleted file mode 120000
index 645a324b..00000000
--- a/FileSets/v3.11/OverviewTanks.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewTanks.qml
\ No newline at end of file
diff --git a/FileSets/v3.11/PageDigitalInput.qml b/FileSets/v3.11/PageDigitalInput.qml
deleted file mode 120000
index 5b768f8f..00000000
--- a/FileSets/v3.11/PageDigitalInput.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/PageDigitalInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.11/PageMain.qml b/FileSets/v3.11/PageMain.qml
deleted file mode 120000
index 2f68fb1c..00000000
--- a/FileSets/v3.11/PageMain.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.14/PageMain.qml
\ No newline at end of file
diff --git a/FileSets/v3.11/PageSettingsDisplay.qml b/FileSets/v3.11/PageSettingsDisplay.qml
deleted file mode 120000
index 9860c031..00000000
--- a/FileSets/v3.11/PageSettingsDisplay.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.14/PageSettingsDisplay.qml
\ No newline at end of file
diff --git a/FileSets/v3.11/Tile.qml b/FileSets/v3.11/Tile.qml
deleted file mode 120000
index 0558cecf..00000000
--- a/FileSets/v3.11/Tile.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Tile.qml
\ No newline at end of file
diff --git a/FileSets/v3.11/TileText.qml b/FileSets/v3.11/TileText.qml
deleted file mode 120000
index 8d7c0778..00000000
--- a/FileSets/v3.11/TileText.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/TileText.qml
\ No newline at end of file
diff --git a/FileSets/v3.11/attributes.csv b/FileSets/v3.11/attributes.csv
deleted file mode 120000
index 61a61302..00000000
--- a/FileSets/v3.11/attributes.csv
+++ /dev/null
@@ -1 +0,0 @@
-../v3.14/attributes.csv
\ No newline at end of file
diff --git a/FileSets/v3.11/dbus_generator.py b/FileSets/v3.11/dbus_generator.py
deleted file mode 120000
index b22d702a..00000000
--- a/FileSets/v3.11/dbus_generator.py
+++ /dev/null
@@ -1 +0,0 @@
-../v3.14/dbus_generator.py
\ No newline at end of file
diff --git a/FileSets/v3.11/dbus_systemcalc.py b/FileSets/v3.11/dbus_systemcalc.py
deleted file mode 120000
index fd94027f..00000000
--- a/FileSets/v3.11/dbus_systemcalc.py
+++ /dev/null
@@ -1 +0,0 @@
-../v3.12/dbus_systemcalc.py
\ No newline at end of file
diff --git a/FileSets/v3.11/main.qml b/FileSets/v3.11/main.qml
deleted file mode 120000
index 492167a2..00000000
--- a/FileSets/v3.11/main.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.22/main.qml
\ No newline at end of file
diff --git a/FileSets/v3.11/styles.css b/FileSets/v3.11/styles.css
deleted file mode 120000
index ef8557ec..00000000
--- a/FileSets/v3.11/styles.css
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/styles.css
\ No newline at end of file
diff --git a/FileSets/v3.12/Battery.qml b/FileSets/v3.12/Battery.qml
deleted file mode 120000
index fafc7b36..00000000
--- a/FileSets/v3.12/Battery.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Battery.qml
\ No newline at end of file
diff --git a/FileSets/v3.12/MbEditBox.qml b/FileSets/v3.12/MbEditBox.qml
deleted file mode 120000
index 8381b7d5..00000000
--- a/FileSets/v3.12/MbEditBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~10/MbEditBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.12/MbEditBoxDateTime.qml b/FileSets/v3.12/MbEditBoxDateTime.qml
deleted file mode 120000
index 7fb351ea..00000000
--- a/FileSets/v3.12/MbEditBoxDateTime.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbEditBoxDateTime.qml
\ No newline at end of file
diff --git a/FileSets/v3.12/MbItem.qml b/FileSets/v3.12/MbItem.qml
deleted file mode 120000
index 048a313c..00000000
--- a/FileSets/v3.12/MbItem.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbItem.qml
\ No newline at end of file
diff --git a/FileSets/v3.12/MbItemDigitalInput.qml b/FileSets/v3.12/MbItemDigitalInput.qml
deleted file mode 120000
index 4404b520..00000000
--- a/FileSets/v3.12/MbItemDigitalInput.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbItemDigitalInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.12/MbSpinBox.qml b/FileSets/v3.12/MbSpinBox.qml
deleted file mode 120000
index bc7530c8..00000000
--- a/FileSets/v3.12/MbSpinBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbSpinBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.12/MbStyle.qml b/FileSets/v3.12/MbStyle.qml
deleted file mode 120000
index d1940446..00000000
--- a/FileSets/v3.12/MbStyle.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbStyle.qml
\ No newline at end of file
diff --git a/FileSets/v3.12/MbSubMenu.qml b/FileSets/v3.12/MbSubMenu.qml
deleted file mode 120000
index 35516a9a..00000000
--- a/FileSets/v3.12/MbSubMenu.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbSubMenu.qml
\ No newline at end of file
diff --git a/FileSets/v3.12/Multi.qml b/FileSets/v3.12/Multi.qml
deleted file mode 120000
index aa938460..00000000
--- a/FileSets/v3.12/Multi.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Multi.qml
\ No newline at end of file
diff --git a/FileSets/v3.12/OverviewBox.qml b/FileSets/v3.12/OverviewBox.qml
deleted file mode 120000
index 96aa4214..00000000
--- a/FileSets/v3.12/OverviewBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.12/OverviewConnection.qml b/FileSets/v3.12/OverviewConnection.qml
deleted file mode 120000
index d833fd05..00000000
--- a/FileSets/v3.12/OverviewConnection.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewConnection.qml
\ No newline at end of file
diff --git a/FileSets/v3.12/OverviewConnectionEnd.qml b/FileSets/v3.12/OverviewConnectionEnd.qml
deleted file mode 120000
index 50c4b471..00000000
--- a/FileSets/v3.12/OverviewConnectionEnd.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewConnectionEnd.qml
\ No newline at end of file
diff --git a/FileSets/v3.12/OverviewSolarCharger.qml b/FileSets/v3.12/OverviewSolarCharger.qml
deleted file mode 120000
index 7353cec9..00000000
--- a/FileSets/v3.12/OverviewSolarCharger.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewSolarCharger.qml
\ No newline at end of file
diff --git a/FileSets/v3.12/OverviewSolarInverter.qml b/FileSets/v3.12/OverviewSolarInverter.qml
deleted file mode 120000
index 4048703e..00000000
--- a/FileSets/v3.12/OverviewSolarInverter.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewSolarInverter.qml
\ No newline at end of file
diff --git a/FileSets/v3.12/OverviewTankDelegate.qml b/FileSets/v3.12/OverviewTankDelegate.qml
deleted file mode 120000
index faeb9aeb..00000000
--- a/FileSets/v3.12/OverviewTankDelegate.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewTankDelegate.qml
\ No newline at end of file
diff --git a/FileSets/v3.12/OverviewTanks.qml b/FileSets/v3.12/OverviewTanks.qml
deleted file mode 120000
index 645a324b..00000000
--- a/FileSets/v3.12/OverviewTanks.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewTanks.qml
\ No newline at end of file
diff --git a/FileSets/v3.12/PageDigitalInput.qml b/FileSets/v3.12/PageDigitalInput.qml
deleted file mode 120000
index 5b768f8f..00000000
--- a/FileSets/v3.12/PageDigitalInput.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/PageDigitalInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.12/PageMain.qml b/FileSets/v3.12/PageMain.qml
deleted file mode 120000
index 2f68fb1c..00000000
--- a/FileSets/v3.12/PageMain.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.14/PageMain.qml
\ No newline at end of file
diff --git a/FileSets/v3.12/PageSettingsDisplay.qml b/FileSets/v3.12/PageSettingsDisplay.qml
deleted file mode 120000
index 9860c031..00000000
--- a/FileSets/v3.12/PageSettingsDisplay.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.14/PageSettingsDisplay.qml
\ No newline at end of file
diff --git a/FileSets/v3.12/Tile.qml b/FileSets/v3.12/Tile.qml
deleted file mode 120000
index 0558cecf..00000000
--- a/FileSets/v3.12/Tile.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Tile.qml
\ No newline at end of file
diff --git a/FileSets/v3.12/TileText.qml b/FileSets/v3.12/TileText.qml
deleted file mode 120000
index 8d7c0778..00000000
--- a/FileSets/v3.12/TileText.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/TileText.qml
\ No newline at end of file
diff --git a/FileSets/v3.12/attributes.csv b/FileSets/v3.12/attributes.csv
deleted file mode 120000
index 61a61302..00000000
--- a/FileSets/v3.12/attributes.csv
+++ /dev/null
@@ -1 +0,0 @@
-../v3.14/attributes.csv
\ No newline at end of file
diff --git a/FileSets/v3.12/dbus_generator.py b/FileSets/v3.12/dbus_generator.py
deleted file mode 120000
index b22d702a..00000000
--- a/FileSets/v3.12/dbus_generator.py
+++ /dev/null
@@ -1 +0,0 @@
-../v3.14/dbus_generator.py
\ No newline at end of file
diff --git a/FileSets/v3.12/main.qml b/FileSets/v3.12/main.qml
deleted file mode 120000
index 492167a2..00000000
--- a/FileSets/v3.12/main.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.22/main.qml
\ No newline at end of file
diff --git a/FileSets/v3.12/styles.css b/FileSets/v3.12/styles.css
deleted file mode 120000
index ef8557ec..00000000
--- a/FileSets/v3.12/styles.css
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/styles.css
\ No newline at end of file
diff --git a/FileSets/v3.13/Battery.qml b/FileSets/v3.13/Battery.qml
deleted file mode 120000
index fafc7b36..00000000
--- a/FileSets/v3.13/Battery.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Battery.qml
\ No newline at end of file
diff --git a/FileSets/v3.13/MbEditBox.qml b/FileSets/v3.13/MbEditBox.qml
deleted file mode 120000
index 8381b7d5..00000000
--- a/FileSets/v3.13/MbEditBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~10/MbEditBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.13/MbEditBoxDateTime.qml b/FileSets/v3.13/MbEditBoxDateTime.qml
deleted file mode 120000
index 7fb351ea..00000000
--- a/FileSets/v3.13/MbEditBoxDateTime.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbEditBoxDateTime.qml
\ No newline at end of file
diff --git a/FileSets/v3.13/MbItem.qml b/FileSets/v3.13/MbItem.qml
deleted file mode 120000
index 048a313c..00000000
--- a/FileSets/v3.13/MbItem.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbItem.qml
\ No newline at end of file
diff --git a/FileSets/v3.13/MbItemDigitalInput.qml b/FileSets/v3.13/MbItemDigitalInput.qml
deleted file mode 120000
index 4404b520..00000000
--- a/FileSets/v3.13/MbItemDigitalInput.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbItemDigitalInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.13/MbSpinBox.qml b/FileSets/v3.13/MbSpinBox.qml
deleted file mode 120000
index bc7530c8..00000000
--- a/FileSets/v3.13/MbSpinBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbSpinBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.13/MbStyle.qml b/FileSets/v3.13/MbStyle.qml
deleted file mode 120000
index d1940446..00000000
--- a/FileSets/v3.13/MbStyle.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbStyle.qml
\ No newline at end of file
diff --git a/FileSets/v3.13/MbSubMenu.qml b/FileSets/v3.13/MbSubMenu.qml
deleted file mode 120000
index 35516a9a..00000000
--- a/FileSets/v3.13/MbSubMenu.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbSubMenu.qml
\ No newline at end of file
diff --git a/FileSets/v3.13/Multi.qml b/FileSets/v3.13/Multi.qml
deleted file mode 120000
index aa938460..00000000
--- a/FileSets/v3.13/Multi.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Multi.qml
\ No newline at end of file
diff --git a/FileSets/v3.13/OverviewBox.qml b/FileSets/v3.13/OverviewBox.qml
deleted file mode 120000
index 96aa4214..00000000
--- a/FileSets/v3.13/OverviewBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.13/OverviewConnection.qml b/FileSets/v3.13/OverviewConnection.qml
deleted file mode 120000
index d833fd05..00000000
--- a/FileSets/v3.13/OverviewConnection.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewConnection.qml
\ No newline at end of file
diff --git a/FileSets/v3.13/OverviewConnectionEnd.qml b/FileSets/v3.13/OverviewConnectionEnd.qml
deleted file mode 120000
index 50c4b471..00000000
--- a/FileSets/v3.13/OverviewConnectionEnd.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewConnectionEnd.qml
\ No newline at end of file
diff --git a/FileSets/v3.13/OverviewSolarCharger.qml b/FileSets/v3.13/OverviewSolarCharger.qml
deleted file mode 120000
index 7353cec9..00000000
--- a/FileSets/v3.13/OverviewSolarCharger.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewSolarCharger.qml
\ No newline at end of file
diff --git a/FileSets/v3.13/OverviewSolarInverter.qml b/FileSets/v3.13/OverviewSolarInverter.qml
deleted file mode 120000
index 4048703e..00000000
--- a/FileSets/v3.13/OverviewSolarInverter.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewSolarInverter.qml
\ No newline at end of file
diff --git a/FileSets/v3.13/OverviewTankDelegate.qml b/FileSets/v3.13/OverviewTankDelegate.qml
deleted file mode 120000
index faeb9aeb..00000000
--- a/FileSets/v3.13/OverviewTankDelegate.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewTankDelegate.qml
\ No newline at end of file
diff --git a/FileSets/v3.13/OverviewTanks.qml b/FileSets/v3.13/OverviewTanks.qml
deleted file mode 120000
index 645a324b..00000000
--- a/FileSets/v3.13/OverviewTanks.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewTanks.qml
\ No newline at end of file
diff --git a/FileSets/v3.13/PageDigitalInput.qml b/FileSets/v3.13/PageDigitalInput.qml
deleted file mode 120000
index 5b768f8f..00000000
--- a/FileSets/v3.13/PageDigitalInput.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/PageDigitalInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.13/PageMain.qml b/FileSets/v3.13/PageMain.qml
deleted file mode 120000
index 2f68fb1c..00000000
--- a/FileSets/v3.13/PageMain.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.14/PageMain.qml
\ No newline at end of file
diff --git a/FileSets/v3.13/PageSettingsDisplay.qml b/FileSets/v3.13/PageSettingsDisplay.qml
deleted file mode 120000
index 9860c031..00000000
--- a/FileSets/v3.13/PageSettingsDisplay.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.14/PageSettingsDisplay.qml
\ No newline at end of file
diff --git a/FileSets/v3.13/Tile.qml b/FileSets/v3.13/Tile.qml
deleted file mode 120000
index 0558cecf..00000000
--- a/FileSets/v3.13/Tile.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Tile.qml
\ No newline at end of file
diff --git a/FileSets/v3.13/TileText.qml b/FileSets/v3.13/TileText.qml
deleted file mode 120000
index 8d7c0778..00000000
--- a/FileSets/v3.13/TileText.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/TileText.qml
\ No newline at end of file
diff --git a/FileSets/v3.13/attributes.csv b/FileSets/v3.13/attributes.csv
deleted file mode 120000
index 61a61302..00000000
--- a/FileSets/v3.13/attributes.csv
+++ /dev/null
@@ -1 +0,0 @@
-../v3.14/attributes.csv
\ No newline at end of file
diff --git a/FileSets/v3.13/dbus_generator.py b/FileSets/v3.13/dbus_generator.py
deleted file mode 120000
index b22d702a..00000000
--- a/FileSets/v3.13/dbus_generator.py
+++ /dev/null
@@ -1 +0,0 @@
-../v3.14/dbus_generator.py
\ No newline at end of file
diff --git a/FileSets/v3.13/dbus_systemcalc.py b/FileSets/v3.13/dbus_systemcalc.py
deleted file mode 120000
index 7595dd75..00000000
--- a/FileSets/v3.13/dbus_systemcalc.py
+++ /dev/null
@@ -1 +0,0 @@
-../v3.14/dbus_systemcalc.py
\ No newline at end of file
diff --git a/FileSets/v3.13/main.qml b/FileSets/v3.13/main.qml
deleted file mode 120000
index 492167a2..00000000
--- a/FileSets/v3.13/main.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.22/main.qml
\ No newline at end of file
diff --git a/FileSets/v3.13/styles.css b/FileSets/v3.13/styles.css
deleted file mode 120000
index ef8557ec..00000000
--- a/FileSets/v3.13/styles.css
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/styles.css
\ No newline at end of file
diff --git a/FileSets/v3.14/Battery.qml b/FileSets/v3.14/Battery.qml
deleted file mode 120000
index fafc7b36..00000000
--- a/FileSets/v3.14/Battery.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Battery.qml
\ No newline at end of file
diff --git a/FileSets/v3.14/MbEditBox.qml b/FileSets/v3.14/MbEditBox.qml
deleted file mode 120000
index 8381b7d5..00000000
--- a/FileSets/v3.14/MbEditBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~10/MbEditBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.14/MbEditBoxDateTime.qml b/FileSets/v3.14/MbEditBoxDateTime.qml
deleted file mode 120000
index 7fb351ea..00000000
--- a/FileSets/v3.14/MbEditBoxDateTime.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbEditBoxDateTime.qml
\ No newline at end of file
diff --git a/FileSets/v3.14/MbItem.qml b/FileSets/v3.14/MbItem.qml
deleted file mode 120000
index 048a313c..00000000
--- a/FileSets/v3.14/MbItem.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbItem.qml
\ No newline at end of file
diff --git a/FileSets/v3.14/MbItemDigitalInput.qml b/FileSets/v3.14/MbItemDigitalInput.qml
deleted file mode 120000
index 4404b520..00000000
--- a/FileSets/v3.14/MbItemDigitalInput.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbItemDigitalInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.14/MbSpinBox.qml b/FileSets/v3.14/MbSpinBox.qml
deleted file mode 120000
index bc7530c8..00000000
--- a/FileSets/v3.14/MbSpinBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbSpinBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.14/MbStyle.qml b/FileSets/v3.14/MbStyle.qml
deleted file mode 120000
index d1940446..00000000
--- a/FileSets/v3.14/MbStyle.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbStyle.qml
\ No newline at end of file
diff --git a/FileSets/v3.14/MbSubMenu.qml b/FileSets/v3.14/MbSubMenu.qml
deleted file mode 120000
index 35516a9a..00000000
--- a/FileSets/v3.14/MbSubMenu.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbSubMenu.qml
\ No newline at end of file
diff --git a/FileSets/v3.14/Multi.qml b/FileSets/v3.14/Multi.qml
deleted file mode 120000
index aa938460..00000000
--- a/FileSets/v3.14/Multi.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Multi.qml
\ No newline at end of file
diff --git a/FileSets/v3.14/OverviewBox.qml b/FileSets/v3.14/OverviewBox.qml
deleted file mode 120000
index 96aa4214..00000000
--- a/FileSets/v3.14/OverviewBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.14/OverviewConnection.qml b/FileSets/v3.14/OverviewConnection.qml
deleted file mode 120000
index d833fd05..00000000
--- a/FileSets/v3.14/OverviewConnection.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewConnection.qml
\ No newline at end of file
diff --git a/FileSets/v3.14/OverviewConnectionEnd.qml b/FileSets/v3.14/OverviewConnectionEnd.qml
deleted file mode 120000
index 50c4b471..00000000
--- a/FileSets/v3.14/OverviewConnectionEnd.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewConnectionEnd.qml
\ No newline at end of file
diff --git a/FileSets/v3.14/OverviewSolarCharger.qml b/FileSets/v3.14/OverviewSolarCharger.qml
deleted file mode 120000
index 7353cec9..00000000
--- a/FileSets/v3.14/OverviewSolarCharger.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewSolarCharger.qml
\ No newline at end of file
diff --git a/FileSets/v3.14/OverviewSolarInverter.qml b/FileSets/v3.14/OverviewSolarInverter.qml
deleted file mode 120000
index 4048703e..00000000
--- a/FileSets/v3.14/OverviewSolarInverter.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewSolarInverter.qml
\ No newline at end of file
diff --git a/FileSets/v3.14/OverviewTankDelegate.qml b/FileSets/v3.14/OverviewTankDelegate.qml
deleted file mode 120000
index faeb9aeb..00000000
--- a/FileSets/v3.14/OverviewTankDelegate.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewTankDelegate.qml
\ No newline at end of file
diff --git a/FileSets/v3.14/OverviewTanks.qml b/FileSets/v3.14/OverviewTanks.qml
deleted file mode 120000
index 645a324b..00000000
--- a/FileSets/v3.14/OverviewTanks.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewTanks.qml
\ No newline at end of file
diff --git a/FileSets/v3.14/PageDigitalInput.qml b/FileSets/v3.14/PageDigitalInput.qml
deleted file mode 120000
index 5b768f8f..00000000
--- a/FileSets/v3.14/PageDigitalInput.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/PageDigitalInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.14/Tile.qml b/FileSets/v3.14/Tile.qml
deleted file mode 120000
index 0558cecf..00000000
--- a/FileSets/v3.14/Tile.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Tile.qml
\ No newline at end of file
diff --git a/FileSets/v3.14/TileText.qml b/FileSets/v3.14/TileText.qml
deleted file mode 120000
index 8d7c0778..00000000
--- a/FileSets/v3.14/TileText.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/TileText.qml
\ No newline at end of file
diff --git a/FileSets/v3.14/main.qml b/FileSets/v3.14/main.qml
deleted file mode 120000
index 492167a2..00000000
--- a/FileSets/v3.14/main.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.22/main.qml
\ No newline at end of file
diff --git a/FileSets/v3.14/styles.css b/FileSets/v3.14/styles.css
deleted file mode 120000
index ef8557ec..00000000
--- a/FileSets/v3.14/styles.css
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/styles.css
\ No newline at end of file
diff --git a/FileSets/v3.20/Battery.qml b/FileSets/v3.20/Battery.qml
deleted file mode 120000
index fafc7b36..00000000
--- a/FileSets/v3.20/Battery.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Battery.qml
\ No newline at end of file
diff --git a/FileSets/v3.20/MbEditBox.qml b/FileSets/v3.20/MbEditBox.qml
deleted file mode 120000
index 8381b7d5..00000000
--- a/FileSets/v3.20/MbEditBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~10/MbEditBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.20/MbEditBoxDateTime.qml b/FileSets/v3.20/MbEditBoxDateTime.qml
deleted file mode 120000
index 7fb351ea..00000000
--- a/FileSets/v3.20/MbEditBoxDateTime.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbEditBoxDateTime.qml
\ No newline at end of file
diff --git a/FileSets/v3.20/MbItem.qml b/FileSets/v3.20/MbItem.qml
deleted file mode 120000
index 048a313c..00000000
--- a/FileSets/v3.20/MbItem.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbItem.qml
\ No newline at end of file
diff --git a/FileSets/v3.20/MbItemDigitalInput.qml b/FileSets/v3.20/MbItemDigitalInput.qml
deleted file mode 120000
index 4404b520..00000000
--- a/FileSets/v3.20/MbItemDigitalInput.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbItemDigitalInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.20/MbSpinBox.qml b/FileSets/v3.20/MbSpinBox.qml
deleted file mode 120000
index bc7530c8..00000000
--- a/FileSets/v3.20/MbSpinBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbSpinBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.20/MbStyle.qml b/FileSets/v3.20/MbStyle.qml
deleted file mode 120000
index d1940446..00000000
--- a/FileSets/v3.20/MbStyle.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbStyle.qml
\ No newline at end of file
diff --git a/FileSets/v3.20/MbSubMenu.qml b/FileSets/v3.20/MbSubMenu.qml
deleted file mode 120000
index 35516a9a..00000000
--- a/FileSets/v3.20/MbSubMenu.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbSubMenu.qml
\ No newline at end of file
diff --git a/FileSets/v3.20/Multi.qml b/FileSets/v3.20/Multi.qml
deleted file mode 120000
index aa938460..00000000
--- a/FileSets/v3.20/Multi.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Multi.qml
\ No newline at end of file
diff --git a/FileSets/v3.20/OverviewBox.qml b/FileSets/v3.20/OverviewBox.qml
deleted file mode 120000
index 96aa4214..00000000
--- a/FileSets/v3.20/OverviewBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.20/OverviewConnection.qml b/FileSets/v3.20/OverviewConnection.qml
deleted file mode 120000
index d833fd05..00000000
--- a/FileSets/v3.20/OverviewConnection.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewConnection.qml
\ No newline at end of file
diff --git a/FileSets/v3.20/OverviewConnectionEnd.qml b/FileSets/v3.20/OverviewConnectionEnd.qml
deleted file mode 120000
index 50c4b471..00000000
--- a/FileSets/v3.20/OverviewConnectionEnd.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewConnectionEnd.qml
\ No newline at end of file
diff --git a/FileSets/v3.20/OverviewSolarCharger.qml b/FileSets/v3.20/OverviewSolarCharger.qml
deleted file mode 120000
index 7353cec9..00000000
--- a/FileSets/v3.20/OverviewSolarCharger.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewSolarCharger.qml
\ No newline at end of file
diff --git a/FileSets/v3.20/OverviewSolarInverter.qml b/FileSets/v3.20/OverviewSolarInverter.qml
deleted file mode 120000
index 4048703e..00000000
--- a/FileSets/v3.20/OverviewSolarInverter.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewSolarInverter.qml
\ No newline at end of file
diff --git a/FileSets/v3.20/OverviewTankDelegate.qml b/FileSets/v3.20/OverviewTankDelegate.qml
deleted file mode 120000
index faeb9aeb..00000000
--- a/FileSets/v3.20/OverviewTankDelegate.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewTankDelegate.qml
\ No newline at end of file
diff --git a/FileSets/v3.20/OverviewTanks.qml b/FileSets/v3.20/OverviewTanks.qml
deleted file mode 120000
index 645a324b..00000000
--- a/FileSets/v3.20/OverviewTanks.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewTanks.qml
\ No newline at end of file
diff --git a/FileSets/v3.20/PageDigitalInput.qml b/FileSets/v3.20/PageDigitalInput.qml
deleted file mode 120000
index 5b768f8f..00000000
--- a/FileSets/v3.20/PageDigitalInput.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/PageDigitalInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.20/PageMain.qml b/FileSets/v3.20/PageMain.qml
deleted file mode 120000
index 4839c5d8..00000000
--- a/FileSets/v3.20/PageMain.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.22/PageMain.qml
\ No newline at end of file
diff --git a/FileSets/v3.20/PageSettingsDisplay.qml b/FileSets/v3.20/PageSettingsDisplay.qml
deleted file mode 120000
index a3d107e3..00000000
--- a/FileSets/v3.20/PageSettingsDisplay.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.22/PageSettingsDisplay.qml
\ No newline at end of file
diff --git a/FileSets/v3.20/Tile.qml b/FileSets/v3.20/Tile.qml
deleted file mode 120000
index 0558cecf..00000000
--- a/FileSets/v3.20/Tile.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Tile.qml
\ No newline at end of file
diff --git a/FileSets/v3.20/TileText.qml b/FileSets/v3.20/TileText.qml
deleted file mode 120000
index 8d7c0778..00000000
--- a/FileSets/v3.20/TileText.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/TileText.qml
\ No newline at end of file
diff --git a/FileSets/v3.20/attributes.csv b/FileSets/v3.20/attributes.csv
deleted file mode 120000
index 2e8b8b10..00000000
--- a/FileSets/v3.20/attributes.csv
+++ /dev/null
@@ -1 +0,0 @@
-../v3.22/attributes.csv
\ No newline at end of file
diff --git a/FileSets/v3.20/dbus_generator.py b/FileSets/v3.20/dbus_generator.py
deleted file mode 120000
index fc553c91..00000000
--- a/FileSets/v3.20/dbus_generator.py
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40/dbus_generator.py
\ No newline at end of file
diff --git a/FileSets/v3.20/dbus_systemcalc.py b/FileSets/v3.20/dbus_systemcalc.py
deleted file mode 120000
index c4df2742..00000000
--- a/FileSets/v3.20/dbus_systemcalc.py
+++ /dev/null
@@ -1 +0,0 @@
-../v3.22/dbus_systemcalc.py
\ No newline at end of file
diff --git a/FileSets/v3.20/main.qml b/FileSets/v3.20/main.qml
deleted file mode 120000
index 492167a2..00000000
--- a/FileSets/v3.20/main.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.22/main.qml
\ No newline at end of file
diff --git a/FileSets/v3.20/styles.css b/FileSets/v3.20/styles.css
deleted file mode 120000
index ef8557ec..00000000
--- a/FileSets/v3.20/styles.css
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/styles.css
\ No newline at end of file
diff --git a/FileSets/v3.21/Battery.qml b/FileSets/v3.21/Battery.qml
deleted file mode 120000
index fafc7b36..00000000
--- a/FileSets/v3.21/Battery.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Battery.qml
\ No newline at end of file
diff --git a/FileSets/v3.21/MbEditBox.qml b/FileSets/v3.21/MbEditBox.qml
deleted file mode 120000
index 8381b7d5..00000000
--- a/FileSets/v3.21/MbEditBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~10/MbEditBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.21/MbEditBoxDateTime.qml b/FileSets/v3.21/MbEditBoxDateTime.qml
deleted file mode 120000
index 7fb351ea..00000000
--- a/FileSets/v3.21/MbEditBoxDateTime.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbEditBoxDateTime.qml
\ No newline at end of file
diff --git a/FileSets/v3.21/MbItem.qml b/FileSets/v3.21/MbItem.qml
deleted file mode 120000
index 048a313c..00000000
--- a/FileSets/v3.21/MbItem.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbItem.qml
\ No newline at end of file
diff --git a/FileSets/v3.21/MbItemDigitalInput.qml b/FileSets/v3.21/MbItemDigitalInput.qml
deleted file mode 120000
index 4404b520..00000000
--- a/FileSets/v3.21/MbItemDigitalInput.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbItemDigitalInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.21/MbSpinBox.qml b/FileSets/v3.21/MbSpinBox.qml
deleted file mode 120000
index bc7530c8..00000000
--- a/FileSets/v3.21/MbSpinBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbSpinBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.21/MbStyle.qml b/FileSets/v3.21/MbStyle.qml
deleted file mode 120000
index d1940446..00000000
--- a/FileSets/v3.21/MbStyle.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbStyle.qml
\ No newline at end of file
diff --git a/FileSets/v3.21/MbSubMenu.qml b/FileSets/v3.21/MbSubMenu.qml
deleted file mode 120000
index 35516a9a..00000000
--- a/FileSets/v3.21/MbSubMenu.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbSubMenu.qml
\ No newline at end of file
diff --git a/FileSets/v3.21/Multi.qml b/FileSets/v3.21/Multi.qml
deleted file mode 120000
index aa938460..00000000
--- a/FileSets/v3.21/Multi.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Multi.qml
\ No newline at end of file
diff --git a/FileSets/v3.21/OverviewBox.qml b/FileSets/v3.21/OverviewBox.qml
deleted file mode 120000
index 96aa4214..00000000
--- a/FileSets/v3.21/OverviewBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.21/OverviewConnection.qml b/FileSets/v3.21/OverviewConnection.qml
deleted file mode 120000
index d833fd05..00000000
--- a/FileSets/v3.21/OverviewConnection.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewConnection.qml
\ No newline at end of file
diff --git a/FileSets/v3.21/OverviewConnectionEnd.qml b/FileSets/v3.21/OverviewConnectionEnd.qml
deleted file mode 120000
index 50c4b471..00000000
--- a/FileSets/v3.21/OverviewConnectionEnd.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewConnectionEnd.qml
\ No newline at end of file
diff --git a/FileSets/v3.21/OverviewSolarCharger.qml b/FileSets/v3.21/OverviewSolarCharger.qml
deleted file mode 120000
index 7353cec9..00000000
--- a/FileSets/v3.21/OverviewSolarCharger.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewSolarCharger.qml
\ No newline at end of file
diff --git a/FileSets/v3.21/OverviewSolarInverter.qml b/FileSets/v3.21/OverviewSolarInverter.qml
deleted file mode 120000
index 4048703e..00000000
--- a/FileSets/v3.21/OverviewSolarInverter.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewSolarInverter.qml
\ No newline at end of file
diff --git a/FileSets/v3.21/OverviewTankDelegate.qml b/FileSets/v3.21/OverviewTankDelegate.qml
deleted file mode 120000
index faeb9aeb..00000000
--- a/FileSets/v3.21/OverviewTankDelegate.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewTankDelegate.qml
\ No newline at end of file
diff --git a/FileSets/v3.21/OverviewTanks.qml b/FileSets/v3.21/OverviewTanks.qml
deleted file mode 120000
index 645a324b..00000000
--- a/FileSets/v3.21/OverviewTanks.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewTanks.qml
\ No newline at end of file
diff --git a/FileSets/v3.21/PageDigitalInput.qml b/FileSets/v3.21/PageDigitalInput.qml
deleted file mode 120000
index 5b768f8f..00000000
--- a/FileSets/v3.21/PageDigitalInput.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/PageDigitalInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.21/PageMain.qml b/FileSets/v3.21/PageMain.qml
deleted file mode 120000
index 4839c5d8..00000000
--- a/FileSets/v3.21/PageMain.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.22/PageMain.qml
\ No newline at end of file
diff --git a/FileSets/v3.21/PageSettingsDisplay.qml b/FileSets/v3.21/PageSettingsDisplay.qml
deleted file mode 120000
index a3d107e3..00000000
--- a/FileSets/v3.21/PageSettingsDisplay.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.22/PageSettingsDisplay.qml
\ No newline at end of file
diff --git a/FileSets/v3.21/Tile.qml b/FileSets/v3.21/Tile.qml
deleted file mode 120000
index 0558cecf..00000000
--- a/FileSets/v3.21/Tile.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Tile.qml
\ No newline at end of file
diff --git a/FileSets/v3.21/TileText.qml b/FileSets/v3.21/TileText.qml
deleted file mode 120000
index 8d7c0778..00000000
--- a/FileSets/v3.21/TileText.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/TileText.qml
\ No newline at end of file
diff --git a/FileSets/v3.21/attributes.csv b/FileSets/v3.21/attributes.csv
deleted file mode 120000
index 2e8b8b10..00000000
--- a/FileSets/v3.21/attributes.csv
+++ /dev/null
@@ -1 +0,0 @@
-../v3.22/attributes.csv
\ No newline at end of file
diff --git a/FileSets/v3.21/dbus_generator.py b/FileSets/v3.21/dbus_generator.py
deleted file mode 120000
index fc553c91..00000000
--- a/FileSets/v3.21/dbus_generator.py
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40/dbus_generator.py
\ No newline at end of file
diff --git a/FileSets/v3.21/dbus_systemcalc.py b/FileSets/v3.21/dbus_systemcalc.py
deleted file mode 120000
index c4df2742..00000000
--- a/FileSets/v3.21/dbus_systemcalc.py
+++ /dev/null
@@ -1 +0,0 @@
-../v3.22/dbus_systemcalc.py
\ No newline at end of file
diff --git a/FileSets/v3.21/main.qml b/FileSets/v3.21/main.qml
deleted file mode 120000
index 492167a2..00000000
--- a/FileSets/v3.21/main.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.22/main.qml
\ No newline at end of file
diff --git a/FileSets/v3.21/styles.css b/FileSets/v3.21/styles.css
deleted file mode 120000
index ef8557ec..00000000
--- a/FileSets/v3.21/styles.css
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/styles.css
\ No newline at end of file
diff --git a/FileSets/v3.22/Battery.qml b/FileSets/v3.22/Battery.qml
deleted file mode 120000
index fafc7b36..00000000
--- a/FileSets/v3.22/Battery.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Battery.qml
\ No newline at end of file
diff --git a/FileSets/v3.22/MbEditBox.qml b/FileSets/v3.22/MbEditBox.qml
deleted file mode 120000
index 8381b7d5..00000000
--- a/FileSets/v3.22/MbEditBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~10/MbEditBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.22/MbEditBoxDateTime.qml b/FileSets/v3.22/MbEditBoxDateTime.qml
deleted file mode 120000
index 7fb351ea..00000000
--- a/FileSets/v3.22/MbEditBoxDateTime.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbEditBoxDateTime.qml
\ No newline at end of file
diff --git a/FileSets/v3.22/MbItem.qml b/FileSets/v3.22/MbItem.qml
deleted file mode 120000
index 048a313c..00000000
--- a/FileSets/v3.22/MbItem.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbItem.qml
\ No newline at end of file
diff --git a/FileSets/v3.22/MbItemDigitalInput.qml b/FileSets/v3.22/MbItemDigitalInput.qml
deleted file mode 120000
index 4404b520..00000000
--- a/FileSets/v3.22/MbItemDigitalInput.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbItemDigitalInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.22/MbSpinBox.qml b/FileSets/v3.22/MbSpinBox.qml
deleted file mode 120000
index bc7530c8..00000000
--- a/FileSets/v3.22/MbSpinBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbSpinBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.22/MbStyle.qml b/FileSets/v3.22/MbStyle.qml
deleted file mode 120000
index d1940446..00000000
--- a/FileSets/v3.22/MbStyle.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbStyle.qml
\ No newline at end of file
diff --git a/FileSets/v3.22/MbSubMenu.qml b/FileSets/v3.22/MbSubMenu.qml
deleted file mode 120000
index 35516a9a..00000000
--- a/FileSets/v3.22/MbSubMenu.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbSubMenu.qml
\ No newline at end of file
diff --git a/FileSets/v3.22/Multi.qml b/FileSets/v3.22/Multi.qml
deleted file mode 120000
index aa938460..00000000
--- a/FileSets/v3.22/Multi.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Multi.qml
\ No newline at end of file
diff --git a/FileSets/v3.22/OverviewBox.qml b/FileSets/v3.22/OverviewBox.qml
deleted file mode 120000
index 96aa4214..00000000
--- a/FileSets/v3.22/OverviewBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.22/OverviewConnection.qml b/FileSets/v3.22/OverviewConnection.qml
deleted file mode 120000
index d833fd05..00000000
--- a/FileSets/v3.22/OverviewConnection.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewConnection.qml
\ No newline at end of file
diff --git a/FileSets/v3.22/OverviewConnectionEnd.qml b/FileSets/v3.22/OverviewConnectionEnd.qml
deleted file mode 120000
index 50c4b471..00000000
--- a/FileSets/v3.22/OverviewConnectionEnd.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewConnectionEnd.qml
\ No newline at end of file
diff --git a/FileSets/v3.22/OverviewSolarCharger.qml b/FileSets/v3.22/OverviewSolarCharger.qml
deleted file mode 120000
index 7353cec9..00000000
--- a/FileSets/v3.22/OverviewSolarCharger.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewSolarCharger.qml
\ No newline at end of file
diff --git a/FileSets/v3.22/OverviewSolarInverter.qml b/FileSets/v3.22/OverviewSolarInverter.qml
deleted file mode 120000
index 4048703e..00000000
--- a/FileSets/v3.22/OverviewSolarInverter.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewSolarInverter.qml
\ No newline at end of file
diff --git a/FileSets/v3.22/OverviewTankDelegate.qml b/FileSets/v3.22/OverviewTankDelegate.qml
deleted file mode 120000
index faeb9aeb..00000000
--- a/FileSets/v3.22/OverviewTankDelegate.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewTankDelegate.qml
\ No newline at end of file
diff --git a/FileSets/v3.22/OverviewTanks.qml b/FileSets/v3.22/OverviewTanks.qml
deleted file mode 120000
index 645a324b..00000000
--- a/FileSets/v3.22/OverviewTanks.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewTanks.qml
\ No newline at end of file
diff --git a/FileSets/v3.22/PageDigitalInput.qml b/FileSets/v3.22/PageDigitalInput.qml
deleted file mode 120000
index 5b768f8f..00000000
--- a/FileSets/v3.22/PageDigitalInput.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/PageDigitalInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.22/Tile.qml b/FileSets/v3.22/Tile.qml
deleted file mode 120000
index 0558cecf..00000000
--- a/FileSets/v3.22/Tile.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Tile.qml
\ No newline at end of file
diff --git a/FileSets/v3.22/TileText.qml b/FileSets/v3.22/TileText.qml
deleted file mode 120000
index 8d7c0778..00000000
--- a/FileSets/v3.22/TileText.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/TileText.qml
\ No newline at end of file
diff --git a/FileSets/v3.22/dbus_generator.py b/FileSets/v3.22/dbus_generator.py
deleted file mode 120000
index fc553c91..00000000
--- a/FileSets/v3.22/dbus_generator.py
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40/dbus_generator.py
\ No newline at end of file
diff --git a/FileSets/v3.22/styles.css b/FileSets/v3.22/styles.css
deleted file mode 120000
index ef8557ec..00000000
--- a/FileSets/v3.22/styles.css
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/styles.css
\ No newline at end of file
diff --git a/FileSets/v3.30/Battery.qml b/FileSets/v3.30/Battery.qml
deleted file mode 120000
index fafc7b36..00000000
--- a/FileSets/v3.30/Battery.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Battery.qml
\ No newline at end of file
diff --git a/FileSets/v3.30/MbEditBox.qml b/FileSets/v3.30/MbEditBox.qml
deleted file mode 120000
index 8381b7d5..00000000
--- a/FileSets/v3.30/MbEditBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~10/MbEditBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.30/MbEditBoxDateTime.qml b/FileSets/v3.30/MbEditBoxDateTime.qml
deleted file mode 120000
index 7fb351ea..00000000
--- a/FileSets/v3.30/MbEditBoxDateTime.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbEditBoxDateTime.qml
\ No newline at end of file
diff --git a/FileSets/v3.30/MbItem.qml b/FileSets/v3.30/MbItem.qml
deleted file mode 120000
index 048a313c..00000000
--- a/FileSets/v3.30/MbItem.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbItem.qml
\ No newline at end of file
diff --git a/FileSets/v3.30/MbItemDigitalInput.qml b/FileSets/v3.30/MbItemDigitalInput.qml
deleted file mode 120000
index 4404b520..00000000
--- a/FileSets/v3.30/MbItemDigitalInput.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbItemDigitalInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.30/MbSpinBox.qml b/FileSets/v3.30/MbSpinBox.qml
deleted file mode 120000
index bc7530c8..00000000
--- a/FileSets/v3.30/MbSpinBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbSpinBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.30/MbStyle.qml b/FileSets/v3.30/MbStyle.qml
deleted file mode 120000
index d1940446..00000000
--- a/FileSets/v3.30/MbStyle.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbStyle.qml
\ No newline at end of file
diff --git a/FileSets/v3.30/MbSubMenu.qml b/FileSets/v3.30/MbSubMenu.qml
deleted file mode 120000
index 35516a9a..00000000
--- a/FileSets/v3.30/MbSubMenu.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbSubMenu.qml
\ No newline at end of file
diff --git a/FileSets/v3.30/Multi.qml b/FileSets/v3.30/Multi.qml
deleted file mode 120000
index aa938460..00000000
--- a/FileSets/v3.30/Multi.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Multi.qml
\ No newline at end of file
diff --git a/FileSets/v3.30/OverviewBox.qml b/FileSets/v3.30/OverviewBox.qml
deleted file mode 120000
index 96aa4214..00000000
--- a/FileSets/v3.30/OverviewBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.30/OverviewConnection.qml b/FileSets/v3.30/OverviewConnection.qml
deleted file mode 120000
index d833fd05..00000000
--- a/FileSets/v3.30/OverviewConnection.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewConnection.qml
\ No newline at end of file
diff --git a/FileSets/v3.30/OverviewConnectionEnd.qml b/FileSets/v3.30/OverviewConnectionEnd.qml
deleted file mode 120000
index 50c4b471..00000000
--- a/FileSets/v3.30/OverviewConnectionEnd.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewConnectionEnd.qml
\ No newline at end of file
diff --git a/FileSets/v3.30/OverviewSolarCharger.qml b/FileSets/v3.30/OverviewSolarCharger.qml
deleted file mode 120000
index 7353cec9..00000000
--- a/FileSets/v3.30/OverviewSolarCharger.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewSolarCharger.qml
\ No newline at end of file
diff --git a/FileSets/v3.30/OverviewSolarInverter.qml b/FileSets/v3.30/OverviewSolarInverter.qml
deleted file mode 120000
index 4048703e..00000000
--- a/FileSets/v3.30/OverviewSolarInverter.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewSolarInverter.qml
\ No newline at end of file
diff --git a/FileSets/v3.30/OverviewTankDelegate.qml b/FileSets/v3.30/OverviewTankDelegate.qml
deleted file mode 120000
index faeb9aeb..00000000
--- a/FileSets/v3.30/OverviewTankDelegate.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewTankDelegate.qml
\ No newline at end of file
diff --git a/FileSets/v3.30/OverviewTanks.qml b/FileSets/v3.30/OverviewTanks.qml
deleted file mode 120000
index 645a324b..00000000
--- a/FileSets/v3.30/OverviewTanks.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewTanks.qml
\ No newline at end of file
diff --git a/FileSets/v3.30/PageDigitalInput.qml b/FileSets/v3.30/PageDigitalInput.qml
deleted file mode 120000
index 5b768f8f..00000000
--- a/FileSets/v3.30/PageDigitalInput.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/PageDigitalInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.30/PageMain.qml b/FileSets/v3.30/PageMain.qml
deleted file mode 120000
index 33fe83b0..00000000
--- a/FileSets/v3.30/PageMain.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~13/PageMain.qml
\ No newline at end of file
diff --git a/FileSets/v3.30/PageSettingsDisplay.qml b/FileSets/v3.30/PageSettingsDisplay.qml
deleted file mode 120000
index fd80cffd..00000000
--- a/FileSets/v3.30/PageSettingsDisplay.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.34/PageSettingsDisplay.qml
\ No newline at end of file
diff --git a/FileSets/v3.30/Tile.qml b/FileSets/v3.30/Tile.qml
deleted file mode 120000
index 0558cecf..00000000
--- a/FileSets/v3.30/Tile.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Tile.qml
\ No newline at end of file
diff --git a/FileSets/v3.30/TileText.qml b/FileSets/v3.30/TileText.qml
deleted file mode 120000
index 8d7c0778..00000000
--- a/FileSets/v3.30/TileText.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/TileText.qml
\ No newline at end of file
diff --git a/FileSets/v3.30/attributes.csv b/FileSets/v3.30/attributes.csv
deleted file mode 120000
index e226e13c..00000000
--- a/FileSets/v3.30/attributes.csv
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~6/attributes.csv
\ No newline at end of file
diff --git a/FileSets/v3.30/dbus_generator.py b/FileSets/v3.30/dbus_generator.py
deleted file mode 120000
index fc553c91..00000000
--- a/FileSets/v3.30/dbus_generator.py
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40/dbus_generator.py
\ No newline at end of file
diff --git a/FileSets/v3.30/dbus_systemcalc.py b/FileSets/v3.30/dbus_systemcalc.py
deleted file mode 120000
index 1a9e3b3a..00000000
--- a/FileSets/v3.30/dbus_systemcalc.py
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~1/dbus_systemcalc.py
\ No newline at end of file
diff --git a/FileSets/v3.30/main.qml b/FileSets/v3.30/main.qml
deleted file mode 120000
index 916fdfbf..00000000
--- a/FileSets/v3.30/main.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~13/main.qml
\ No newline at end of file
diff --git a/FileSets/v3.30/styles.css b/FileSets/v3.30/styles.css
deleted file mode 120000
index ef8557ec..00000000
--- a/FileSets/v3.30/styles.css
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/styles.css
\ No newline at end of file
diff --git a/FileSets/v3.31/Battery.qml b/FileSets/v3.31/Battery.qml
deleted file mode 120000
index fafc7b36..00000000
--- a/FileSets/v3.31/Battery.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Battery.qml
\ No newline at end of file
diff --git a/FileSets/v3.31/MbEditBox.qml b/FileSets/v3.31/MbEditBox.qml
deleted file mode 120000
index 8381b7d5..00000000
--- a/FileSets/v3.31/MbEditBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~10/MbEditBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.31/MbEditBoxDateTime.qml b/FileSets/v3.31/MbEditBoxDateTime.qml
deleted file mode 120000
index 7fb351ea..00000000
--- a/FileSets/v3.31/MbEditBoxDateTime.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbEditBoxDateTime.qml
\ No newline at end of file
diff --git a/FileSets/v3.31/MbItem.qml b/FileSets/v3.31/MbItem.qml
deleted file mode 120000
index 048a313c..00000000
--- a/FileSets/v3.31/MbItem.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbItem.qml
\ No newline at end of file
diff --git a/FileSets/v3.31/MbItemDigitalInput.qml b/FileSets/v3.31/MbItemDigitalInput.qml
deleted file mode 120000
index 4404b520..00000000
--- a/FileSets/v3.31/MbItemDigitalInput.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbItemDigitalInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.31/MbSpinBox.qml b/FileSets/v3.31/MbSpinBox.qml
deleted file mode 120000
index bc7530c8..00000000
--- a/FileSets/v3.31/MbSpinBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbSpinBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.31/MbStyle.qml b/FileSets/v3.31/MbStyle.qml
deleted file mode 120000
index d1940446..00000000
--- a/FileSets/v3.31/MbStyle.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbStyle.qml
\ No newline at end of file
diff --git a/FileSets/v3.31/MbSubMenu.qml b/FileSets/v3.31/MbSubMenu.qml
deleted file mode 120000
index 35516a9a..00000000
--- a/FileSets/v3.31/MbSubMenu.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbSubMenu.qml
\ No newline at end of file
diff --git a/FileSets/v3.31/Multi.qml b/FileSets/v3.31/Multi.qml
deleted file mode 120000
index aa938460..00000000
--- a/FileSets/v3.31/Multi.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Multi.qml
\ No newline at end of file
diff --git a/FileSets/v3.31/OverviewBox.qml b/FileSets/v3.31/OverviewBox.qml
deleted file mode 120000
index 96aa4214..00000000
--- a/FileSets/v3.31/OverviewBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.31/OverviewConnection.qml b/FileSets/v3.31/OverviewConnection.qml
deleted file mode 120000
index d833fd05..00000000
--- a/FileSets/v3.31/OverviewConnection.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewConnection.qml
\ No newline at end of file
diff --git a/FileSets/v3.31/OverviewConnectionEnd.qml b/FileSets/v3.31/OverviewConnectionEnd.qml
deleted file mode 120000
index 50c4b471..00000000
--- a/FileSets/v3.31/OverviewConnectionEnd.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewConnectionEnd.qml
\ No newline at end of file
diff --git a/FileSets/v3.31/OverviewSolarCharger.qml b/FileSets/v3.31/OverviewSolarCharger.qml
deleted file mode 120000
index 7353cec9..00000000
--- a/FileSets/v3.31/OverviewSolarCharger.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewSolarCharger.qml
\ No newline at end of file
diff --git a/FileSets/v3.31/OverviewSolarInverter.qml b/FileSets/v3.31/OverviewSolarInverter.qml
deleted file mode 120000
index 4048703e..00000000
--- a/FileSets/v3.31/OverviewSolarInverter.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewSolarInverter.qml
\ No newline at end of file
diff --git a/FileSets/v3.31/OverviewTankDelegate.qml b/FileSets/v3.31/OverviewTankDelegate.qml
deleted file mode 120000
index faeb9aeb..00000000
--- a/FileSets/v3.31/OverviewTankDelegate.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewTankDelegate.qml
\ No newline at end of file
diff --git a/FileSets/v3.31/OverviewTanks.qml b/FileSets/v3.31/OverviewTanks.qml
deleted file mode 120000
index 645a324b..00000000
--- a/FileSets/v3.31/OverviewTanks.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewTanks.qml
\ No newline at end of file
diff --git a/FileSets/v3.31/PageDigitalInput.qml b/FileSets/v3.31/PageDigitalInput.qml
deleted file mode 120000
index 5b768f8f..00000000
--- a/FileSets/v3.31/PageDigitalInput.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/PageDigitalInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.31/PageMain.qml b/FileSets/v3.31/PageMain.qml
deleted file mode 120000
index 33fe83b0..00000000
--- a/FileSets/v3.31/PageMain.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~13/PageMain.qml
\ No newline at end of file
diff --git a/FileSets/v3.31/PageSettingsDisplay.qml b/FileSets/v3.31/PageSettingsDisplay.qml
deleted file mode 120000
index fd80cffd..00000000
--- a/FileSets/v3.31/PageSettingsDisplay.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.34/PageSettingsDisplay.qml
\ No newline at end of file
diff --git a/FileSets/v3.31/Tile.qml b/FileSets/v3.31/Tile.qml
deleted file mode 120000
index 0558cecf..00000000
--- a/FileSets/v3.31/Tile.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Tile.qml
\ No newline at end of file
diff --git a/FileSets/v3.31/TileText.qml b/FileSets/v3.31/TileText.qml
deleted file mode 120000
index 8d7c0778..00000000
--- a/FileSets/v3.31/TileText.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/TileText.qml
\ No newline at end of file
diff --git a/FileSets/v3.31/attributes.csv b/FileSets/v3.31/attributes.csv
deleted file mode 120000
index e226e13c..00000000
--- a/FileSets/v3.31/attributes.csv
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~6/attributes.csv
\ No newline at end of file
diff --git a/FileSets/v3.31/dbus_generator.py b/FileSets/v3.31/dbus_generator.py
deleted file mode 120000
index fc553c91..00000000
--- a/FileSets/v3.31/dbus_generator.py
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40/dbus_generator.py
\ No newline at end of file
diff --git a/FileSets/v3.31/dbus_systemcalc.py b/FileSets/v3.31/dbus_systemcalc.py
deleted file mode 120000
index 1a9e3b3a..00000000
--- a/FileSets/v3.31/dbus_systemcalc.py
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~1/dbus_systemcalc.py
\ No newline at end of file
diff --git a/FileSets/v3.31/main.qml b/FileSets/v3.31/main.qml
deleted file mode 120000
index 916fdfbf..00000000
--- a/FileSets/v3.31/main.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~13/main.qml
\ No newline at end of file
diff --git a/FileSets/v3.31/styles.css b/FileSets/v3.31/styles.css
deleted file mode 120000
index ef8557ec..00000000
--- a/FileSets/v3.31/styles.css
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/styles.css
\ No newline at end of file
diff --git a/FileSets/v3.33/Battery.qml b/FileSets/v3.33/Battery.qml
deleted file mode 120000
index fafc7b36..00000000
--- a/FileSets/v3.33/Battery.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Battery.qml
\ No newline at end of file
diff --git a/FileSets/v3.33/MbEditBox.qml b/FileSets/v3.33/MbEditBox.qml
deleted file mode 120000
index 8381b7d5..00000000
--- a/FileSets/v3.33/MbEditBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~10/MbEditBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.33/MbEditBoxDateTime.qml b/FileSets/v3.33/MbEditBoxDateTime.qml
deleted file mode 120000
index 7fb351ea..00000000
--- a/FileSets/v3.33/MbEditBoxDateTime.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbEditBoxDateTime.qml
\ No newline at end of file
diff --git a/FileSets/v3.33/MbItem.qml b/FileSets/v3.33/MbItem.qml
deleted file mode 120000
index 048a313c..00000000
--- a/FileSets/v3.33/MbItem.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbItem.qml
\ No newline at end of file
diff --git a/FileSets/v3.33/MbItemDigitalInput.qml b/FileSets/v3.33/MbItemDigitalInput.qml
deleted file mode 120000
index 4404b520..00000000
--- a/FileSets/v3.33/MbItemDigitalInput.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbItemDigitalInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.33/MbSpinBox.qml b/FileSets/v3.33/MbSpinBox.qml
deleted file mode 120000
index bc7530c8..00000000
--- a/FileSets/v3.33/MbSpinBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbSpinBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.33/MbStyle.qml b/FileSets/v3.33/MbStyle.qml
deleted file mode 120000
index d1940446..00000000
--- a/FileSets/v3.33/MbStyle.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbStyle.qml
\ No newline at end of file
diff --git a/FileSets/v3.33/MbSubMenu.qml b/FileSets/v3.33/MbSubMenu.qml
deleted file mode 120000
index 35516a9a..00000000
--- a/FileSets/v3.33/MbSubMenu.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbSubMenu.qml
\ No newline at end of file
diff --git a/FileSets/v3.33/Multi.qml b/FileSets/v3.33/Multi.qml
deleted file mode 120000
index aa938460..00000000
--- a/FileSets/v3.33/Multi.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Multi.qml
\ No newline at end of file
diff --git a/FileSets/v3.33/OverviewBox.qml b/FileSets/v3.33/OverviewBox.qml
deleted file mode 120000
index 96aa4214..00000000
--- a/FileSets/v3.33/OverviewBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.33/OverviewConnection.qml b/FileSets/v3.33/OverviewConnection.qml
deleted file mode 120000
index d833fd05..00000000
--- a/FileSets/v3.33/OverviewConnection.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewConnection.qml
\ No newline at end of file
diff --git a/FileSets/v3.33/OverviewConnectionEnd.qml b/FileSets/v3.33/OverviewConnectionEnd.qml
deleted file mode 120000
index 50c4b471..00000000
--- a/FileSets/v3.33/OverviewConnectionEnd.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewConnectionEnd.qml
\ No newline at end of file
diff --git a/FileSets/v3.33/OverviewSolarCharger.qml b/FileSets/v3.33/OverviewSolarCharger.qml
deleted file mode 120000
index 7353cec9..00000000
--- a/FileSets/v3.33/OverviewSolarCharger.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewSolarCharger.qml
\ No newline at end of file
diff --git a/FileSets/v3.33/OverviewSolarInverter.qml b/FileSets/v3.33/OverviewSolarInverter.qml
deleted file mode 120000
index 4048703e..00000000
--- a/FileSets/v3.33/OverviewSolarInverter.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewSolarInverter.qml
\ No newline at end of file
diff --git a/FileSets/v3.33/OverviewTankDelegate.qml b/FileSets/v3.33/OverviewTankDelegate.qml
deleted file mode 120000
index faeb9aeb..00000000
--- a/FileSets/v3.33/OverviewTankDelegate.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewTankDelegate.qml
\ No newline at end of file
diff --git a/FileSets/v3.33/OverviewTanks.qml b/FileSets/v3.33/OverviewTanks.qml
deleted file mode 120000
index 645a324b..00000000
--- a/FileSets/v3.33/OverviewTanks.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewTanks.qml
\ No newline at end of file
diff --git a/FileSets/v3.33/PageDigitalInput.qml b/FileSets/v3.33/PageDigitalInput.qml
deleted file mode 120000
index 5b768f8f..00000000
--- a/FileSets/v3.33/PageDigitalInput.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/PageDigitalInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.33/PageMain.qml b/FileSets/v3.33/PageMain.qml
deleted file mode 120000
index 33fe83b0..00000000
--- a/FileSets/v3.33/PageMain.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~13/PageMain.qml
\ No newline at end of file
diff --git a/FileSets/v3.33/PageSettingsDisplay.qml b/FileSets/v3.33/PageSettingsDisplay.qml
deleted file mode 120000
index fd80cffd..00000000
--- a/FileSets/v3.33/PageSettingsDisplay.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.34/PageSettingsDisplay.qml
\ No newline at end of file
diff --git a/FileSets/v3.33/Tile.qml b/FileSets/v3.33/Tile.qml
deleted file mode 120000
index 0558cecf..00000000
--- a/FileSets/v3.33/Tile.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Tile.qml
\ No newline at end of file
diff --git a/FileSets/v3.33/TileText.qml b/FileSets/v3.33/TileText.qml
deleted file mode 120000
index 8d7c0778..00000000
--- a/FileSets/v3.33/TileText.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/TileText.qml
\ No newline at end of file
diff --git a/FileSets/v3.33/attributes.csv b/FileSets/v3.33/attributes.csv
deleted file mode 120000
index e226e13c..00000000
--- a/FileSets/v3.33/attributes.csv
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~6/attributes.csv
\ No newline at end of file
diff --git a/FileSets/v3.33/dbus_generator.py b/FileSets/v3.33/dbus_generator.py
deleted file mode 120000
index fc553c91..00000000
--- a/FileSets/v3.33/dbus_generator.py
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40/dbus_generator.py
\ No newline at end of file
diff --git a/FileSets/v3.33/dbus_systemcalc.py b/FileSets/v3.33/dbus_systemcalc.py
deleted file mode 120000
index 1a9e3b3a..00000000
--- a/FileSets/v3.33/dbus_systemcalc.py
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~1/dbus_systemcalc.py
\ No newline at end of file
diff --git a/FileSets/v3.33/main.qml b/FileSets/v3.33/main.qml
deleted file mode 120000
index 916fdfbf..00000000
--- a/FileSets/v3.33/main.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~13/main.qml
\ No newline at end of file
diff --git a/FileSets/v3.33/styles.css b/FileSets/v3.33/styles.css
deleted file mode 120000
index ef8557ec..00000000
--- a/FileSets/v3.33/styles.css
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/styles.css
\ No newline at end of file
diff --git a/FileSets/v3.34/Battery.qml b/FileSets/v3.34/Battery.qml
deleted file mode 120000
index fafc7b36..00000000
--- a/FileSets/v3.34/Battery.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Battery.qml
\ No newline at end of file
diff --git a/FileSets/v3.34/MbEditBox.qml b/FileSets/v3.34/MbEditBox.qml
deleted file mode 120000
index 8381b7d5..00000000
--- a/FileSets/v3.34/MbEditBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~10/MbEditBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.34/MbEditBoxDateTime.qml b/FileSets/v3.34/MbEditBoxDateTime.qml
deleted file mode 120000
index 7fb351ea..00000000
--- a/FileSets/v3.34/MbEditBoxDateTime.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbEditBoxDateTime.qml
\ No newline at end of file
diff --git a/FileSets/v3.34/MbItem.qml b/FileSets/v3.34/MbItem.qml
deleted file mode 120000
index 048a313c..00000000
--- a/FileSets/v3.34/MbItem.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbItem.qml
\ No newline at end of file
diff --git a/FileSets/v3.34/MbItemDigitalInput.qml b/FileSets/v3.34/MbItemDigitalInput.qml
deleted file mode 120000
index 4404b520..00000000
--- a/FileSets/v3.34/MbItemDigitalInput.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbItemDigitalInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.34/MbSpinBox.qml b/FileSets/v3.34/MbSpinBox.qml
deleted file mode 120000
index bc7530c8..00000000
--- a/FileSets/v3.34/MbSpinBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbSpinBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.34/MbStyle.qml b/FileSets/v3.34/MbStyle.qml
deleted file mode 120000
index d1940446..00000000
--- a/FileSets/v3.34/MbStyle.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbStyle.qml
\ No newline at end of file
diff --git a/FileSets/v3.34/MbSubMenu.qml b/FileSets/v3.34/MbSubMenu.qml
deleted file mode 120000
index 35516a9a..00000000
--- a/FileSets/v3.34/MbSubMenu.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/MbSubMenu.qml
\ No newline at end of file
diff --git a/FileSets/v3.34/Multi.qml b/FileSets/v3.34/Multi.qml
deleted file mode 120000
index aa938460..00000000
--- a/FileSets/v3.34/Multi.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Multi.qml
\ No newline at end of file
diff --git a/FileSets/v3.34/OverviewBox.qml b/FileSets/v3.34/OverviewBox.qml
deleted file mode 120000
index 96aa4214..00000000
--- a/FileSets/v3.34/OverviewBox.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewBox.qml
\ No newline at end of file
diff --git a/FileSets/v3.34/OverviewConnection.qml b/FileSets/v3.34/OverviewConnection.qml
deleted file mode 120000
index d833fd05..00000000
--- a/FileSets/v3.34/OverviewConnection.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewConnection.qml
\ No newline at end of file
diff --git a/FileSets/v3.34/OverviewConnectionEnd.qml b/FileSets/v3.34/OverviewConnectionEnd.qml
deleted file mode 120000
index 50c4b471..00000000
--- a/FileSets/v3.34/OverviewConnectionEnd.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewConnectionEnd.qml
\ No newline at end of file
diff --git a/FileSets/v3.34/OverviewSolarCharger.qml b/FileSets/v3.34/OverviewSolarCharger.qml
deleted file mode 120000
index 7353cec9..00000000
--- a/FileSets/v3.34/OverviewSolarCharger.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewSolarCharger.qml
\ No newline at end of file
diff --git a/FileSets/v3.34/OverviewSolarInverter.qml b/FileSets/v3.34/OverviewSolarInverter.qml
deleted file mode 120000
index 4048703e..00000000
--- a/FileSets/v3.34/OverviewSolarInverter.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewSolarInverter.qml
\ No newline at end of file
diff --git a/FileSets/v3.34/OverviewTankDelegate.qml b/FileSets/v3.34/OverviewTankDelegate.qml
deleted file mode 120000
index faeb9aeb..00000000
--- a/FileSets/v3.34/OverviewTankDelegate.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewTankDelegate.qml
\ No newline at end of file
diff --git a/FileSets/v3.34/OverviewTanks.qml b/FileSets/v3.34/OverviewTanks.qml
deleted file mode 120000
index 645a324b..00000000
--- a/FileSets/v3.34/OverviewTanks.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/OverviewTanks.qml
\ No newline at end of file
diff --git a/FileSets/v3.34/PageDigitalInput.qml b/FileSets/v3.34/PageDigitalInput.qml
deleted file mode 120000
index 5b768f8f..00000000
--- a/FileSets/v3.34/PageDigitalInput.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/PageDigitalInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.34/PageMain.qml b/FileSets/v3.34/PageMain.qml
deleted file mode 120000
index 33fe83b0..00000000
--- a/FileSets/v3.34/PageMain.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~13/PageMain.qml
\ No newline at end of file
diff --git a/FileSets/v3.34/Tile.qml b/FileSets/v3.34/Tile.qml
deleted file mode 120000
index 0558cecf..00000000
--- a/FileSets/v3.34/Tile.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/Tile.qml
\ No newline at end of file
diff --git a/FileSets/v3.34/TileText.qml b/FileSets/v3.34/TileText.qml
deleted file mode 120000
index 8d7c0778..00000000
--- a/FileSets/v3.34/TileText.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/TileText.qml
\ No newline at end of file
diff --git a/FileSets/v3.34/dbus_generator.py b/FileSets/v3.34/dbus_generator.py
deleted file mode 120000
index fc553c91..00000000
--- a/FileSets/v3.34/dbus_generator.py
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40/dbus_generator.py
\ No newline at end of file
diff --git a/FileSets/v3.34/dbus_systemcalc.py b/FileSets/v3.34/dbus_systemcalc.py
deleted file mode 120000
index 1a9e3b3a..00000000
--- a/FileSets/v3.34/dbus_systemcalc.py
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~1/dbus_systemcalc.py
\ No newline at end of file
diff --git a/FileSets/v3.34/main.qml b/FileSets/v3.34/main.qml
deleted file mode 120000
index 916fdfbf..00000000
--- a/FileSets/v3.34/main.qml
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~13/main.qml
\ No newline at end of file
diff --git a/FileSets/v3.34/styles.css b/FileSets/v3.34/styles.css
deleted file mode 120000
index ef8557ec..00000000
--- a/FileSets/v3.34/styles.css
+++ /dev/null
@@ -1 +0,0 @@
-../v3.40~35/styles.css
\ No newline at end of file
diff --git a/FileSets/v3.50/TileDigIn.qml b/FileSets/v3.50/TileDigIn.qml
index 3ea17c12..a0872570 120000
--- a/FileSets/v3.50/TileDigIn.qml
+++ b/FileSets/v3.50/TileDigIn.qml
@@ -1 +1 @@
-../v3.60~17/TileDigIn.qml
\ No newline at end of file
+../v3.60~21/TileDigIn.qml
\ No newline at end of file
diff --git a/FileSets/v3.51/TileDigIn.qml b/FileSets/v3.51/TileDigIn.qml
index 3ea17c12..a0872570 120000
--- a/FileSets/v3.51/TileDigIn.qml
+++ b/FileSets/v3.51/TileDigIn.qml
@@ -1 +1 @@
-../v3.60~17/TileDigIn.qml
\ No newline at end of file
+../v3.60~21/TileDigIn.qml
\ No newline at end of file
diff --git a/FileSets/v3.52/TileDigIn.qml b/FileSets/v3.52/TileDigIn.qml
index 3ea17c12..a0872570 120000
--- a/FileSets/v3.52/TileDigIn.qml
+++ b/FileSets/v3.52/TileDigIn.qml
@@ -1 +1 @@
-../v3.60~17/TileDigIn.qml
\ No newline at end of file
+../v3.60~21/TileDigIn.qml
\ No newline at end of file
diff --git a/FileSets/v3.53/TileDigIn.qml b/FileSets/v3.53/TileDigIn.qml
index 3ea17c12..a0872570 120000
--- a/FileSets/v3.53/TileDigIn.qml
+++ b/FileSets/v3.53/TileDigIn.qml
@@ -1 +1 @@
-../v3.60~17/TileDigIn.qml
\ No newline at end of file
+../v3.60~21/TileDigIn.qml
\ No newline at end of file
diff --git a/FileSets/v3.54/COMPLETE b/FileSets/v3.54/COMPLETE
new file mode 100644
index 00000000..e69de29b
diff --git a/FileSets/v3.54/DetailAcInput.qml b/FileSets/v3.54/DetailAcInput.qml
new file mode 120000
index 00000000..3fa965c3
--- /dev/null
+++ b/FileSets/v3.54/DetailAcInput.qml
@@ -0,0 +1 @@
+../v3.60~17/DetailAcInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.54/DetailInverter.qml b/FileSets/v3.54/DetailInverter.qml
new file mode 120000
index 00000000..88520fef
--- /dev/null
+++ b/FileSets/v3.54/DetailInverter.qml
@@ -0,0 +1 @@
+../v3.60~17/DetailInverter.qml
\ No newline at end of file
diff --git a/FileSets/v3.54/DetailLoadsCombined.qml b/FileSets/v3.54/DetailLoadsCombined.qml
new file mode 120000
index 00000000..2a0760fc
--- /dev/null
+++ b/FileSets/v3.54/DetailLoadsCombined.qml
@@ -0,0 +1 @@
+../v3.60~17/DetailLoadsCombined.qml
\ No newline at end of file
diff --git a/FileSets/v3.54/DetailLoadsOnInput.qml b/FileSets/v3.54/DetailLoadsOnInput.qml
new file mode 120000
index 00000000..bdd9ee0c
--- /dev/null
+++ b/FileSets/v3.54/DetailLoadsOnInput.qml
@@ -0,0 +1 @@
+../v3.60~17/DetailLoadsOnInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.54/DetailLoadsOnOutput.qml b/FileSets/v3.54/DetailLoadsOnOutput.qml
new file mode 120000
index 00000000..c3185d28
--- /dev/null
+++ b/FileSets/v3.54/DetailLoadsOnOutput.qml
@@ -0,0 +1 @@
+../v3.60~17/DetailLoadsOnOutput.qml
\ No newline at end of file
diff --git a/FileSets/v3.54/LINKS_ONLY b/FileSets/v3.54/LINKS_ONLY
new file mode 100644
index 00000000..e69de29b
diff --git a/FileSets/v3.54/OverviewAcValuesEnhanced.qml b/FileSets/v3.54/OverviewAcValuesEnhanced.qml
new file mode 120000
index 00000000..c2d9566e
--- /dev/null
+++ b/FileSets/v3.54/OverviewAcValuesEnhanced.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewAcValuesEnhanced.qml
\ No newline at end of file
diff --git a/FileSets/v3.54/OverviewFlowComplex.qml b/FileSets/v3.54/OverviewFlowComplex.qml
new file mode 120000
index 00000000..1ce6dc7b
--- /dev/null
+++ b/FileSets/v3.54/OverviewFlowComplex.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewFlowComplex.qml
\ No newline at end of file
diff --git a/FileSets/v3.54/OverviewGeneratorEnhanced.qml b/FileSets/v3.54/OverviewGeneratorEnhanced.qml
new file mode 120000
index 00000000..376fd466
--- /dev/null
+++ b/FileSets/v3.54/OverviewGeneratorEnhanced.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewGeneratorEnhanced.qml
\ No newline at end of file
diff --git a/FileSets/v3.54/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.54/OverviewGeneratorRelayEnhanced.qml
new file mode 120000
index 00000000..361a181b
--- /dev/null
+++ b/FileSets/v3.54/OverviewGeneratorRelayEnhanced.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewGeneratorRelayEnhanced.qml
\ No newline at end of file
diff --git a/FileSets/v3.54/OverviewHubEnhanced.qml b/FileSets/v3.54/OverviewHubEnhanced.qml
new file mode 120000
index 00000000..ed64252b
--- /dev/null
+++ b/FileSets/v3.54/OverviewHubEnhanced.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewHubEnhanced.qml
\ No newline at end of file
diff --git a/FileSets/v3.54/OverviewMobileEnhanced.qml b/FileSets/v3.54/OverviewMobileEnhanced.qml
new file mode 120000
index 00000000..b82b1af4
--- /dev/null
+++ b/FileSets/v3.54/OverviewMobileEnhanced.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewMobileEnhanced.qml
\ No newline at end of file
diff --git a/FileSets/v3.54/OverviewTanksTempsDigInputs.qml b/FileSets/v3.54/OverviewTanksTempsDigInputs.qml
new file mode 120000
index 00000000..6706a905
--- /dev/null
+++ b/FileSets/v3.54/OverviewTanksTempsDigInputs.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewTanksTempsDigInputs.qml
\ No newline at end of file
diff --git a/FileSets/v3.54/PageGenerator.qml.USE_ORIGINAL b/FileSets/v3.54/PageGenerator.qml.USE_ORIGINAL
new file mode 100644
index 00000000..e69de29b
diff --git a/FileSets/v3.54/PageSettingsGuiMods.qml b/FileSets/v3.54/PageSettingsGuiMods.qml
new file mode 120000
index 00000000..2f1127c6
--- /dev/null
+++ b/FileSets/v3.54/PageSettingsGuiMods.qml
@@ -0,0 +1 @@
+../v3.60~17/PageSettingsGuiMods.qml
\ No newline at end of file
diff --git a/FileSets/v3.54/PowerGauge.qml b/FileSets/v3.54/PowerGauge.qml
new file mode 120000
index 00000000..6d3a152c
--- /dev/null
+++ b/FileSets/v3.54/PowerGauge.qml
@@ -0,0 +1 @@
+../v3.60~17/PowerGauge.qml
\ No newline at end of file
diff --git a/FileSets/v3.54/TileDigIn.qml b/FileSets/v3.54/TileDigIn.qml
new file mode 120000
index 00000000..a0872570
--- /dev/null
+++ b/FileSets/v3.54/TileDigIn.qml
@@ -0,0 +1 @@
+../v3.60~21/TileDigIn.qml
\ No newline at end of file
diff --git a/FileSets/v3.54/TileRelay.qml b/FileSets/v3.54/TileRelay.qml
new file mode 120000
index 00000000..74deeb6d
--- /dev/null
+++ b/FileSets/v3.54/TileRelay.qml
@@ -0,0 +1 @@
+../v3.60~17/TileRelay.qml
\ No newline at end of file
diff --git a/FileSets/v3.54~1/COMPLETE b/FileSets/v3.54~1/COMPLETE
new file mode 100644
index 00000000..e69de29b
diff --git a/FileSets/v3.54~1/DetailAcInput.qml b/FileSets/v3.54~1/DetailAcInput.qml
new file mode 120000
index 00000000..3fa965c3
--- /dev/null
+++ b/FileSets/v3.54~1/DetailAcInput.qml
@@ -0,0 +1 @@
+../v3.60~17/DetailAcInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.54~1/DetailInverter.qml b/FileSets/v3.54~1/DetailInverter.qml
new file mode 120000
index 00000000..88520fef
--- /dev/null
+++ b/FileSets/v3.54~1/DetailInverter.qml
@@ -0,0 +1 @@
+../v3.60~17/DetailInverter.qml
\ No newline at end of file
diff --git a/FileSets/v3.54~1/DetailLoadsCombined.qml b/FileSets/v3.54~1/DetailLoadsCombined.qml
new file mode 120000
index 00000000..2a0760fc
--- /dev/null
+++ b/FileSets/v3.54~1/DetailLoadsCombined.qml
@@ -0,0 +1 @@
+../v3.60~17/DetailLoadsCombined.qml
\ No newline at end of file
diff --git a/FileSets/v3.54~1/DetailLoadsOnInput.qml b/FileSets/v3.54~1/DetailLoadsOnInput.qml
new file mode 120000
index 00000000..bdd9ee0c
--- /dev/null
+++ b/FileSets/v3.54~1/DetailLoadsOnInput.qml
@@ -0,0 +1 @@
+../v3.60~17/DetailLoadsOnInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.54~1/DetailLoadsOnOutput.qml b/FileSets/v3.54~1/DetailLoadsOnOutput.qml
new file mode 120000
index 00000000..c3185d28
--- /dev/null
+++ b/FileSets/v3.54~1/DetailLoadsOnOutput.qml
@@ -0,0 +1 @@
+../v3.60~17/DetailLoadsOnOutput.qml
\ No newline at end of file
diff --git a/FileSets/v3.54~1/LINKS_ONLY b/FileSets/v3.54~1/LINKS_ONLY
new file mode 100644
index 00000000..e69de29b
diff --git a/FileSets/v3.54~1/OverviewAcValuesEnhanced.qml b/FileSets/v3.54~1/OverviewAcValuesEnhanced.qml
new file mode 120000
index 00000000..c2d9566e
--- /dev/null
+++ b/FileSets/v3.54~1/OverviewAcValuesEnhanced.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewAcValuesEnhanced.qml
\ No newline at end of file
diff --git a/FileSets/v3.54~1/OverviewFlowComplex.qml b/FileSets/v3.54~1/OverviewFlowComplex.qml
new file mode 120000
index 00000000..1ce6dc7b
--- /dev/null
+++ b/FileSets/v3.54~1/OverviewFlowComplex.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewFlowComplex.qml
\ No newline at end of file
diff --git a/FileSets/v3.54~1/OverviewGeneratorEnhanced.qml b/FileSets/v3.54~1/OverviewGeneratorEnhanced.qml
new file mode 120000
index 00000000..376fd466
--- /dev/null
+++ b/FileSets/v3.54~1/OverviewGeneratorEnhanced.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewGeneratorEnhanced.qml
\ No newline at end of file
diff --git a/FileSets/v3.54~1/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.54~1/OverviewGeneratorRelayEnhanced.qml
new file mode 120000
index 00000000..361a181b
--- /dev/null
+++ b/FileSets/v3.54~1/OverviewGeneratorRelayEnhanced.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewGeneratorRelayEnhanced.qml
\ No newline at end of file
diff --git a/FileSets/v3.54~1/OverviewHubEnhanced.qml b/FileSets/v3.54~1/OverviewHubEnhanced.qml
new file mode 120000
index 00000000..ed64252b
--- /dev/null
+++ b/FileSets/v3.54~1/OverviewHubEnhanced.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewHubEnhanced.qml
\ No newline at end of file
diff --git a/FileSets/v3.54~1/OverviewMobileEnhanced.qml b/FileSets/v3.54~1/OverviewMobileEnhanced.qml
new file mode 120000
index 00000000..b82b1af4
--- /dev/null
+++ b/FileSets/v3.54~1/OverviewMobileEnhanced.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewMobileEnhanced.qml
\ No newline at end of file
diff --git a/FileSets/v3.54~1/OverviewTanksTempsDigInputs.qml b/FileSets/v3.54~1/OverviewTanksTempsDigInputs.qml
new file mode 120000
index 00000000..6706a905
--- /dev/null
+++ b/FileSets/v3.54~1/OverviewTanksTempsDigInputs.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewTanksTempsDigInputs.qml
\ No newline at end of file
diff --git a/FileSets/v3.54~1/PageGenerator.qml.USE_ORIGINAL b/FileSets/v3.54~1/PageGenerator.qml.USE_ORIGINAL
new file mode 100644
index 00000000..e69de29b
diff --git a/FileSets/v3.54~1/PageSettingsGuiMods.qml b/FileSets/v3.54~1/PageSettingsGuiMods.qml
new file mode 120000
index 00000000..2f1127c6
--- /dev/null
+++ b/FileSets/v3.54~1/PageSettingsGuiMods.qml
@@ -0,0 +1 @@
+../v3.60~17/PageSettingsGuiMods.qml
\ No newline at end of file
diff --git a/FileSets/v3.54~1/PowerGauge.qml b/FileSets/v3.54~1/PowerGauge.qml
new file mode 120000
index 00000000..6d3a152c
--- /dev/null
+++ b/FileSets/v3.54~1/PowerGauge.qml
@@ -0,0 +1 @@
+../v3.60~17/PowerGauge.qml
\ No newline at end of file
diff --git a/FileSets/v3.54~1/TileDigIn.qml b/FileSets/v3.54~1/TileDigIn.qml
new file mode 120000
index 00000000..a0872570
--- /dev/null
+++ b/FileSets/v3.54~1/TileDigIn.qml
@@ -0,0 +1 @@
+../v3.60~21/TileDigIn.qml
\ No newline at end of file
diff --git a/FileSets/v3.54~1/TileRelay.qml b/FileSets/v3.54~1/TileRelay.qml
new file mode 120000
index 00000000..74deeb6d
--- /dev/null
+++ b/FileSets/v3.54~1/TileRelay.qml
@@ -0,0 +1 @@
+../v3.60~17/TileRelay.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~15/COMPLETE b/FileSets/v3.60~15/COMPLETE
new file mode 100644
index 00000000..e69de29b
diff --git a/FileSets/v3.60~15/DetailAcInput.qml b/FileSets/v3.60~15/DetailAcInput.qml
new file mode 120000
index 00000000..3fa965c3
--- /dev/null
+++ b/FileSets/v3.60~15/DetailAcInput.qml
@@ -0,0 +1 @@
+../v3.60~17/DetailAcInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~15/DetailInverter.qml b/FileSets/v3.60~15/DetailInverter.qml
new file mode 120000
index 00000000..88520fef
--- /dev/null
+++ b/FileSets/v3.60~15/DetailInverter.qml
@@ -0,0 +1 @@
+../v3.60~17/DetailInverter.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~15/DetailLoadsCombined.qml b/FileSets/v3.60~15/DetailLoadsCombined.qml
new file mode 120000
index 00000000..2a0760fc
--- /dev/null
+++ b/FileSets/v3.60~15/DetailLoadsCombined.qml
@@ -0,0 +1 @@
+../v3.60~17/DetailLoadsCombined.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~15/DetailLoadsOnInput.qml b/FileSets/v3.60~15/DetailLoadsOnInput.qml
new file mode 120000
index 00000000..bdd9ee0c
--- /dev/null
+++ b/FileSets/v3.60~15/DetailLoadsOnInput.qml
@@ -0,0 +1 @@
+../v3.60~17/DetailLoadsOnInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~15/DetailLoadsOnOutput.qml b/FileSets/v3.60~15/DetailLoadsOnOutput.qml
new file mode 120000
index 00000000..c3185d28
--- /dev/null
+++ b/FileSets/v3.60~15/DetailLoadsOnOutput.qml
@@ -0,0 +1 @@
+../v3.60~17/DetailLoadsOnOutput.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~15/LINKS_ONLY b/FileSets/v3.60~15/LINKS_ONLY
new file mode 100644
index 00000000..e69de29b
diff --git a/FileSets/v3.60~15/OverviewAcValuesEnhanced.qml b/FileSets/v3.60~15/OverviewAcValuesEnhanced.qml
new file mode 120000
index 00000000..c2d9566e
--- /dev/null
+++ b/FileSets/v3.60~15/OverviewAcValuesEnhanced.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewAcValuesEnhanced.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~15/OverviewFlowComplex.qml b/FileSets/v3.60~15/OverviewFlowComplex.qml
new file mode 120000
index 00000000..1ce6dc7b
--- /dev/null
+++ b/FileSets/v3.60~15/OverviewFlowComplex.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewFlowComplex.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~15/OverviewGeneratorEnhanced.qml b/FileSets/v3.60~15/OverviewGeneratorEnhanced.qml
new file mode 120000
index 00000000..376fd466
--- /dev/null
+++ b/FileSets/v3.60~15/OverviewGeneratorEnhanced.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewGeneratorEnhanced.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~15/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.60~15/OverviewGeneratorRelayEnhanced.qml
new file mode 120000
index 00000000..361a181b
--- /dev/null
+++ b/FileSets/v3.60~15/OverviewGeneratorRelayEnhanced.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewGeneratorRelayEnhanced.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~15/OverviewHubEnhanced.qml b/FileSets/v3.60~15/OverviewHubEnhanced.qml
new file mode 120000
index 00000000..ed64252b
--- /dev/null
+++ b/FileSets/v3.60~15/OverviewHubEnhanced.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewHubEnhanced.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~15/OverviewMobileEnhanced.qml b/FileSets/v3.60~15/OverviewMobileEnhanced.qml
new file mode 120000
index 00000000..b82b1af4
--- /dev/null
+++ b/FileSets/v3.60~15/OverviewMobileEnhanced.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewMobileEnhanced.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~15/OverviewTanksTempsDigInputs.qml b/FileSets/v3.60~15/OverviewTanksTempsDigInputs.qml
new file mode 120000
index 00000000..6706a905
--- /dev/null
+++ b/FileSets/v3.60~15/OverviewTanksTempsDigInputs.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewTanksTempsDigInputs.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~15/PageGenerator.qml.USE_ORIGINAL b/FileSets/v3.60~15/PageGenerator.qml.USE_ORIGINAL
new file mode 100644
index 00000000..e69de29b
diff --git a/FileSets/v3.60~15/PageSettingsGuiMods.qml b/FileSets/v3.60~15/PageSettingsGuiMods.qml
new file mode 120000
index 00000000..2f1127c6
--- /dev/null
+++ b/FileSets/v3.60~15/PageSettingsGuiMods.qml
@@ -0,0 +1 @@
+../v3.60~17/PageSettingsGuiMods.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~15/PowerGauge.qml b/FileSets/v3.60~15/PowerGauge.qml
new file mode 120000
index 00000000..6d3a152c
--- /dev/null
+++ b/FileSets/v3.60~15/PowerGauge.qml
@@ -0,0 +1 @@
+../v3.60~17/PowerGauge.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~15/TileDigIn.qml b/FileSets/v3.60~15/TileDigIn.qml
new file mode 120000
index 00000000..a0872570
--- /dev/null
+++ b/FileSets/v3.60~15/TileDigIn.qml
@@ -0,0 +1 @@
+../v3.60~21/TileDigIn.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~15/TileRelay.qml b/FileSets/v3.60~15/TileRelay.qml
new file mode 120000
index 00000000..74deeb6d
--- /dev/null
+++ b/FileSets/v3.60~15/TileRelay.qml
@@ -0,0 +1 @@
+../v3.60~17/TileRelay.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~16/COMPLETE b/FileSets/v3.60~16/COMPLETE
new file mode 100644
index 00000000..e69de29b
diff --git a/FileSets/v3.60~16/DetailAcInput.qml b/FileSets/v3.60~16/DetailAcInput.qml
new file mode 120000
index 00000000..3fa965c3
--- /dev/null
+++ b/FileSets/v3.60~16/DetailAcInput.qml
@@ -0,0 +1 @@
+../v3.60~17/DetailAcInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~16/DetailInverter.qml b/FileSets/v3.60~16/DetailInverter.qml
new file mode 120000
index 00000000..88520fef
--- /dev/null
+++ b/FileSets/v3.60~16/DetailInverter.qml
@@ -0,0 +1 @@
+../v3.60~17/DetailInverter.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~16/DetailLoadsCombined.qml b/FileSets/v3.60~16/DetailLoadsCombined.qml
new file mode 120000
index 00000000..2a0760fc
--- /dev/null
+++ b/FileSets/v3.60~16/DetailLoadsCombined.qml
@@ -0,0 +1 @@
+../v3.60~17/DetailLoadsCombined.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~16/DetailLoadsOnInput.qml b/FileSets/v3.60~16/DetailLoadsOnInput.qml
new file mode 120000
index 00000000..bdd9ee0c
--- /dev/null
+++ b/FileSets/v3.60~16/DetailLoadsOnInput.qml
@@ -0,0 +1 @@
+../v3.60~17/DetailLoadsOnInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~16/DetailLoadsOnOutput.qml b/FileSets/v3.60~16/DetailLoadsOnOutput.qml
new file mode 120000
index 00000000..c3185d28
--- /dev/null
+++ b/FileSets/v3.60~16/DetailLoadsOnOutput.qml
@@ -0,0 +1 @@
+../v3.60~17/DetailLoadsOnOutput.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~16/LINKS_ONLY b/FileSets/v3.60~16/LINKS_ONLY
new file mode 100644
index 00000000..e69de29b
diff --git a/FileSets/v3.60~16/OverviewAcValuesEnhanced.qml b/FileSets/v3.60~16/OverviewAcValuesEnhanced.qml
new file mode 120000
index 00000000..c2d9566e
--- /dev/null
+++ b/FileSets/v3.60~16/OverviewAcValuesEnhanced.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewAcValuesEnhanced.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~16/OverviewFlowComplex.qml b/FileSets/v3.60~16/OverviewFlowComplex.qml
new file mode 120000
index 00000000..1ce6dc7b
--- /dev/null
+++ b/FileSets/v3.60~16/OverviewFlowComplex.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewFlowComplex.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~16/OverviewGeneratorEnhanced.qml b/FileSets/v3.60~16/OverviewGeneratorEnhanced.qml
new file mode 120000
index 00000000..376fd466
--- /dev/null
+++ b/FileSets/v3.60~16/OverviewGeneratorEnhanced.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewGeneratorEnhanced.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~16/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.60~16/OverviewGeneratorRelayEnhanced.qml
new file mode 120000
index 00000000..361a181b
--- /dev/null
+++ b/FileSets/v3.60~16/OverviewGeneratorRelayEnhanced.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewGeneratorRelayEnhanced.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~16/OverviewHubEnhanced.qml b/FileSets/v3.60~16/OverviewHubEnhanced.qml
new file mode 120000
index 00000000..ed64252b
--- /dev/null
+++ b/FileSets/v3.60~16/OverviewHubEnhanced.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewHubEnhanced.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~16/OverviewMobileEnhanced.qml b/FileSets/v3.60~16/OverviewMobileEnhanced.qml
new file mode 120000
index 00000000..b82b1af4
--- /dev/null
+++ b/FileSets/v3.60~16/OverviewMobileEnhanced.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewMobileEnhanced.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~16/OverviewTanksTempsDigInputs.qml b/FileSets/v3.60~16/OverviewTanksTempsDigInputs.qml
new file mode 120000
index 00000000..6706a905
--- /dev/null
+++ b/FileSets/v3.60~16/OverviewTanksTempsDigInputs.qml
@@ -0,0 +1 @@
+../v3.60~17/OverviewTanksTempsDigInputs.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~16/PageGenerator.qml.USE_ORIGINAL b/FileSets/v3.60~16/PageGenerator.qml.USE_ORIGINAL
new file mode 100644
index 00000000..e69de29b
diff --git a/FileSets/v3.60~16/PageSettingsGuiMods.qml b/FileSets/v3.60~16/PageSettingsGuiMods.qml
new file mode 120000
index 00000000..2f1127c6
--- /dev/null
+++ b/FileSets/v3.60~16/PageSettingsGuiMods.qml
@@ -0,0 +1 @@
+../v3.60~17/PageSettingsGuiMods.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~16/PowerGauge.qml b/FileSets/v3.60~16/PowerGauge.qml
new file mode 120000
index 00000000..6d3a152c
--- /dev/null
+++ b/FileSets/v3.60~16/PowerGauge.qml
@@ -0,0 +1 @@
+../v3.60~17/PowerGauge.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~16/TileDigIn.qml b/FileSets/v3.60~16/TileDigIn.qml
new file mode 120000
index 00000000..a0872570
--- /dev/null
+++ b/FileSets/v3.60~16/TileDigIn.qml
@@ -0,0 +1 @@
+../v3.60~21/TileDigIn.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~16/TileRelay.qml b/FileSets/v3.60~16/TileRelay.qml
new file mode 120000
index 00000000..74deeb6d
--- /dev/null
+++ b/FileSets/v3.60~16/TileRelay.qml
@@ -0,0 +1 @@
+../v3.60~17/TileRelay.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~17/TileDigIn.qml b/FileSets/v3.60~17/TileDigIn.qml
deleted file mode 100644
index 492b8b2d..00000000
--- a/FileSets/v3.60~17/TileDigIn.qml
+++ /dev/null
@@ -1,133 +0,0 @@
-// New for GuiMods to display digital inputs
-// based on TileTank.qml
-
-import QtQuick 1.1
-import "utils.js" as Utils
-import "tanksensor.js" as TankSensor
-
-Tile {
- id: root
-
- property string bindPrefix: serviceName
- property VBusItem nameItem: VBusItem { bind: Utils.path(bindPrefix, "/CustomName") }
- property VBusItem deviceItem: VBusItem { bind: Utils.path(bindPrefix, "/DeviceInstance") }
- property VBusItem aggregateItem: VBusItem { bind: Utils.path(bindPrefix, "/Aggregate") }
- property string digInName: nameItem.valid && nameItem.value != "" ? nameItem.value : getType (type)
- property VBusItem typeItem: VBusItem { bind: Utils.path(bindPrefix, "/Type") }
- property VBusItem stateItem: VBusItem { bind: Utils.path(bindPrefix, "/State") }
- property bool isPulseCounter: aggregateItem.valid
- // pulse counter doesn't have /Type so fill it in here
- property int type: isPulseCounter ? 1 : typeItem.valid ? typeItem.value : 0
-
- property variant bkgdColors: [ "#b3b3b3", "#4aa3df", "#1abc9c", "#F39C12", "#95a5a6", "#95a5a6","#dcc6e0", "#f1a9a0", "#7f8c8d", "#ebbc3a" ]
- property color bkgdColor: type > 0 && type < 10 ? bkgdColors [type] : "#b3b3b3"
- property variant units: ["m3", "L", "gal", "gal"]
-
-
- function getType(type)
- {
- switch (type)
- {
- case 0:
- return qsTr("Disabled")
- case 1:
- return qsTr("Pulse meter")
- case 2:
- return qsTr("Door alarm")
- case 3:
- return qsTr("Bilge pump")
- case 4:
- return qsTr("Bilge alarm")
- case 5:
- return qsTr("Burglar alarm")
- case 6:
- return qsTr("Smoke alarm")
- case 7:
- return qsTr("Fire alarm")
- case 8:
- return qsTr("CO2 alarm")
- case 9:
- return qsTr("Generator")
- case 10:
- return qsTr("Generic I/O")
-//// added for ExtTransferSwitch package
- case 11:
- return qsTr("Touch enable")
- case 12:
- return qsTr("Transfer switch")
- default:
- return "Unknown"
- }
- }
-
- function getState(st)
- {
- switch (st)
- {
- case 0:
- return qsTr("Low")
- case 1:
- return qsTr("High")
- case 2:
- return qsTr("Off")
- case 3:
- return qsTr("On")
- case 4:
- return qsTr("No")
- case 5:
- return qsTr("Yes")
- case 6:
- return qsTr("Open")
- case 7:
- return qsTr("Closed")
- case 8:
- return qsTr("Ok")
- case 9:
- return qsTr("Alarm")
- case 10:
- return qsTr("Running")
- case 11:
- return qsTr("Stopped")
-//// added for ExtTransferSwitch package
- case 12:
- return qsTr("On Generator")
- case 13:
- return qsTr("On Grid")
- default:
- return qsTr("Unknown")
- }
-
- }
-
- title: digInName + " (In " + (deviceItem.valid ? (deviceItem.value.toString ()) : "?") + ")"
-
- color: bkgdColor
-
- VBusItem
- {
- id: unitItem
- bind: Utils.path("com.victronenergy.settings/Settings/System/VolumeUnit")
- }
-
- values: Item
- {
- width: root.width - 10
- height: 12
- TileText
- {
- width: root.width
- text:
- {
- if (isPulseCounter)
- return aggregateItem.value.toString() + (unitItem.valid ? units[unitItem.value] : "??")
- else
- return stateItem.valid ? getState (stateItem.value) : "??"
- }
- horizontalAlignment: Text.AlignHCenter
- anchors
- {
- horizontalCenter: parent.horizontalCenter
- }
- }
- }
-}
diff --git a/FileSets/v3.60~17/TileDigIn.qml b/FileSets/v3.60~17/TileDigIn.qml
new file mode 120000
index 00000000..a0872570
--- /dev/null
+++ b/FileSets/v3.60~17/TileDigIn.qml
@@ -0,0 +1 @@
+../v3.60~21/TileDigIn.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~21/COMPLETE b/FileSets/v3.60~21/COMPLETE
new file mode 100644
index 00000000..e69de29b
diff --git a/FileSets/v3.60~21/DetailAcInput.qml b/FileSets/v3.60~21/DetailAcInput.qml
new file mode 120000
index 00000000..9e3016f7
--- /dev/null
+++ b/FileSets/v3.60~21/DetailAcInput.qml
@@ -0,0 +1 @@
+../v3.60~25/DetailAcInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~21/DetailInverter.qml b/FileSets/v3.60~21/DetailInverter.qml
new file mode 120000
index 00000000..b702a885
--- /dev/null
+++ b/FileSets/v3.60~21/DetailInverter.qml
@@ -0,0 +1 @@
+../v3.60~25/DetailInverter.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~21/DetailLoadsCombined.qml b/FileSets/v3.60~21/DetailLoadsCombined.qml
new file mode 120000
index 00000000..e5f3e68e
--- /dev/null
+++ b/FileSets/v3.60~21/DetailLoadsCombined.qml
@@ -0,0 +1 @@
+../v3.60~25/DetailLoadsCombined.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~21/DetailLoadsOnInput.qml b/FileSets/v3.60~21/DetailLoadsOnInput.qml
new file mode 120000
index 00000000..11f4c173
--- /dev/null
+++ b/FileSets/v3.60~21/DetailLoadsOnInput.qml
@@ -0,0 +1 @@
+../v3.60~25/DetailLoadsOnInput.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~21/DetailLoadsOnOutput.qml b/FileSets/v3.60~21/DetailLoadsOnOutput.qml
new file mode 120000
index 00000000..54ab28e5
--- /dev/null
+++ b/FileSets/v3.60~21/DetailLoadsOnOutput.qml
@@ -0,0 +1 @@
+../v3.60~25/DetailLoadsOnOutput.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~21/LINKS_ONLY b/FileSets/v3.60~21/LINKS_ONLY
new file mode 100644
index 00000000..e69de29b
diff --git a/FileSets/v3.60~21/OverviewAcValuesEnhanced.qml b/FileSets/v3.60~21/OverviewAcValuesEnhanced.qml
new file mode 120000
index 00000000..09f7989b
--- /dev/null
+++ b/FileSets/v3.60~21/OverviewAcValuesEnhanced.qml
@@ -0,0 +1 @@
+../v3.60~25/OverviewAcValuesEnhanced.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~21/OverviewFlowComplex.qml b/FileSets/v3.60~21/OverviewFlowComplex.qml
new file mode 120000
index 00000000..fb316922
--- /dev/null
+++ b/FileSets/v3.60~21/OverviewFlowComplex.qml
@@ -0,0 +1 @@
+../v3.60~25/OverviewFlowComplex.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~21/OverviewGeneratorEnhanced.qml b/FileSets/v3.60~21/OverviewGeneratorEnhanced.qml
new file mode 120000
index 00000000..3a94fc62
--- /dev/null
+++ b/FileSets/v3.60~21/OverviewGeneratorEnhanced.qml
@@ -0,0 +1 @@
+../v3.60~25/OverviewGeneratorEnhanced.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~21/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.60~21/OverviewGeneratorRelayEnhanced.qml
new file mode 120000
index 00000000..c592dd97
--- /dev/null
+++ b/FileSets/v3.60~21/OverviewGeneratorRelayEnhanced.qml
@@ -0,0 +1 @@
+../v3.60~25/OverviewGeneratorRelayEnhanced.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~21/OverviewHubEnhanced.qml b/FileSets/v3.60~21/OverviewHubEnhanced.qml
new file mode 120000
index 00000000..5134a4c8
--- /dev/null
+++ b/FileSets/v3.60~21/OverviewHubEnhanced.qml
@@ -0,0 +1 @@
+../v3.60~25/OverviewHubEnhanced.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~21/OverviewMobileEnhanced.qml b/FileSets/v3.60~21/OverviewMobileEnhanced.qml
new file mode 120000
index 00000000..633340cc
--- /dev/null
+++ b/FileSets/v3.60~21/OverviewMobileEnhanced.qml
@@ -0,0 +1 @@
+../v3.60~25/OverviewMobileEnhanced.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~21/OverviewTanksTempsDigInputs.qml b/FileSets/v3.60~21/OverviewTanksTempsDigInputs.qml
new file mode 120000
index 00000000..81f97bbb
--- /dev/null
+++ b/FileSets/v3.60~21/OverviewTanksTempsDigInputs.qml
@@ -0,0 +1 @@
+../v3.60~25/OverviewTanksTempsDigInputs.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~21/PageGenerator.qml.USE_ORIGINAL b/FileSets/v3.60~21/PageGenerator.qml.USE_ORIGINAL
new file mode 100644
index 00000000..e69de29b
diff --git a/FileSets/v3.60~21/PageGenerator.qml.orig b/FileSets/v3.60~21/PageGenerator.qml.orig
new file mode 100644
index 00000000..3dd70448
--- /dev/null
+++ b/FileSets/v3.60~21/PageGenerator.qml.orig
@@ -0,0 +1,96 @@
+import QtQuick 2
+import "utils.js" as Utils
+
+MbPage {
+ id: root
+ title: qsTr("Generator start/stop")
+ property string settingsBindPrefix
+ property string startStopBindPrefix
+ property bool showRunTime: true
+ property alias startStopModel: _startStopModel
+ property VBusItem activeCondition: VBusItem { bind: Utils.path(startStopBindPrefix, "/RunningByCondition") }
+ property VBusItem generatorState: VBusItem { bind: Utils.path(startStopBindPrefix, "/State") }
+ property VBusItem runningTime: VBusItem { bind: Utils.path(startStopBindPrefix, "/Runtime") }
+
+ FnGeneratorStates {
+ id: genState
+ }
+
+ model: startStopModel
+
+ function formatError(text, value)
+ {
+ return "#" + value.toString() + " " + text
+ }
+
+ VisibleItemModel {
+ id: _startStopModel
+
+ MbSwitch {
+ name: qsTr("Auto start functionality")
+ bind: Utils.path(startStopBindPrefix, "/AutoStartEnabled")
+ show: startStopBindPrefix === "com.victronenergy.generator.startstop0"
+ }
+
+ MbSubMenu {
+ description: qsTr("Manual start")
+ show: startStopBindPrefix === "com.victronenergy.generator.startstop0"
+ subpage:
+ Component {
+ PageGeneratorManualStart {
+ startStopBindPrefix: root.startStopBindPrefix
+ }
+ }
+ }
+
+ MbItemValue {
+ description: qsTr("Current run time")
+ item.text: runningTime.valid ? Utils.secondsToNoSecsString(runningTime.value) : "0"
+ show: generatorState.value >= 1 && generatorState.value <= 3 // Running, Warm-up, Cool-down
+ }
+
+ MbItemValue {
+ description: qsTr("State")
+ show: startStopBindPrefix === "com.victronenergy.generator.startstop0"
+ item.text: activeCondition.valid ? genState.getState(generatorState.value, activeCondition.value) : '---'
+ }
+
+ MbItemOptions {
+ id: _gensetStatus
+ description: qsTr("Error")
+ bind: Utils.path(startStopBindPrefix, "/Error")
+ readonly: true
+ show: valid && startStopBindPrefix === "com.victronenergy.generator.startstop0"
+ possibleValues: [
+ MbOption { description: qsTr("No error"); value: 0 },
+ MbOption { description: formatError(qsTr("Remote switch control disabled"), 1); value: 1 },
+ MbOption { description: formatError(qsTr("Generator in fault condition"), 2); value: 2 },
+ MbOption { description: formatError(qsTr("Generator not detected at AC input"), 3); value: 3 }
+ ]
+ }
+
+ MbSubMenu {
+ id: conditions
+ description: qsTr("Settings")
+ subpage: Component {
+ PageSettingsGenerator {
+ settingsBindPrefix: root.settingsBindPrefix
+ startStopBindPrefix: root.startStopBindPrefix
+ }
+ }
+ }
+
+ MbSubMenu {
+ id: runtimePage
+ description: qsTr("Run time and service")
+ subpage:
+ Component {
+ PageGeneratorRuntimeService {
+ title: qsTr("Run time and service")
+ settingsBindPrefix: root.settingsBindPrefix
+ startStopBindPrefix: root.startStopBindPrefix
+ }
+ }
+ }
+ }
+}
diff --git a/FileSets/v3.60~21/PageSettingsGuiMods.qml b/FileSets/v3.60~21/PageSettingsGuiMods.qml
new file mode 120000
index 00000000..751bba70
--- /dev/null
+++ b/FileSets/v3.60~21/PageSettingsGuiMods.qml
@@ -0,0 +1 @@
+../v3.60~25/PageSettingsGuiMods.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~21/PowerGauge.qml b/FileSets/v3.60~21/PowerGauge.qml
new file mode 120000
index 00000000..d11eedb9
--- /dev/null
+++ b/FileSets/v3.60~21/PowerGauge.qml
@@ -0,0 +1 @@
+../v3.60~25/PowerGauge.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~21/TileDigIn.qml b/FileSets/v3.60~21/TileDigIn.qml
new file mode 120000
index 00000000..66003f49
--- /dev/null
+++ b/FileSets/v3.60~21/TileDigIn.qml
@@ -0,0 +1 @@
+../v3.60~25/TileDigIn.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~21/TileRelay.orig b/FileSets/v3.60~21/TileRelay.orig
new file mode 100644
index 00000000..6efa32bf
--- /dev/null
+++ b/FileSets/v3.60~21/TileRelay.orig
@@ -0,0 +1,170 @@
+import QtQuick 2
+import "utils.js" as Utils
+
+Item {
+ id: root
+
+ property string systemPrefix: "com.victronenergy.system"
+ property string vebusPrefix: _vebusService.valid ? _vebusService.value : ""
+
+ property variant battery: _battery
+ property alias dcSystem: _dcSystem
+ property alias pvCharger: _pvCharger
+ property alias pvOnAcIn1: _pvOnAcIn1
+ property alias pvOnAcIn2: _pvOnAcIn2
+ property alias pvOnAcOut: _pvOnAcOut
+ property alias inverterChargerDc: _inverterChargerDc
+ property alias acLoad: _acLoad
+ property alias acInLoad: _acInLoad
+ property alias acOutLoad: _acOutLoad
+ property alias grid: _grid
+ property alias genset: _genset
+ property alias acInput: _activein
+ property VBusItem systemType: VBusItem { bind: Utils.path(systemPrefix, "/SystemType") }
+ property bool hasGridMeter: _hasGridMeter.valid
+ property variant acSource: _acSource.value
+ property VBusItem preferRenewableEnergy: VBusItem { bind: Utils.path(vebusPrefix, "/Dc/0/PreferRenewableEnergy") }
+ property VBusItem remoteGeneratorSelected: VBusItem { bind: Utils.path(vebusPrefix, "/Ac/State/RemoteGeneratorSelected") }
+
+ property alias pvOnGrid: _pvOnAcIn2
+
+ property int batteryStateIdle: 0
+ property int batteryStateCharging: 1
+ property int batteryStateDischarging: 2
+
+ property int acSourceNotAvailable: 0
+ property int acSourceGrid: 1
+ property int acSourceGenset: 2
+ property int acSourceShore: 3 // same as grid
+
+ property alias pvInvertersProductIds: _pvInvertersProductIds
+ property alias batteryProductId: _batteryProductId
+
+ VBusItem {
+ id: _pvInvertersProductIds
+ bind: Utils.path(systemPrefix, "/PvInvertersProductIds")
+ }
+
+ VBusItem {
+ id: _batteryProductId
+ bind: Utils.path(systemPrefix, "/Dc/Battery/ProductId")
+ }
+
+ VBusItem {
+ id: _vebusService
+ bind: Utils.path(systemPrefix, "/VebusService")
+ }
+
+ QtObject {
+ id: _pvCharger
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Pv/Power"); unit: "W"}
+ }
+
+ ObjectAcConnection {
+ id: _pvOnAcOut
+ bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnOutput")
+ }
+
+ ObjectAcConnection {
+ id: _pvOnAcIn1
+ bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGenset")
+ }
+
+ ObjectAcConnection {
+ id: _pvOnAcIn2
+ bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGrid")
+ }
+
+ ObjectAcConnection {
+ id: _genset
+ splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0
+ bindPrefix: Utils.path(systemPrefix, "/Ac/Genset")
+ }
+
+ VBusItem {
+ id: _acSource
+ bind: Utils.path(systemPrefix, "/Ac/ActiveIn/Source")
+ }
+
+ VBusItem {
+ id: _hasGridMeter
+ bind: Utils.path(systemPrefix, "/Ac/Grid/DeviceType")
+ }
+
+ /*
+ * Single Multis that can be split-phase reports NrOfPhases of 2
+ * When L2 is disconnected from the input the output L1 and L2
+ * are shorted. This item indicates if L2 is passed through
+ * from AC-in to AC-out.
+ * 1: L2 is being passed through from AC-in to AC-out.
+ * 0: L1 and L2 are shorted together.
+ * invalid: The unit is configured in such way that its L2 output is not used.
+ */
+
+ VBusItem {
+ id: _splitPhaseL2Passthru
+ bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2Passthru")
+ }
+
+ VBusItem {
+ id: _l2L1OutSummed
+ bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2L1OutSummed")
+ }
+
+
+ ObjectAcConnection {
+ id: _grid
+ splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0
+ bindPrefix: Utils.path(systemPrefix, "/Ac/Grid")
+ }
+
+ ObjectAcConnection {
+ id: _activein
+ bindPrefix: Utils.path(systemPrefix, "/Ac/ActiveIn")
+ }
+
+ ObjectAcConnection {
+ id: _acLoad
+ l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0)
+ isAcOutput: true
+ bindPrefix: Utils.path(systemPrefix, "/Ac/Consumption")
+ }
+
+ ObjectAcConnection {
+ id: _acOutLoad
+ l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0)
+ isAcOutput: true
+ bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnOutput")
+ }
+
+ ObjectAcConnection {
+ id: _acInLoad
+ splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0
+ bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnInput")
+ }
+
+ ObjectAcConnection {
+ id: _acUnknown
+ }
+
+ QtObject {
+ id: _inverterChargerDc
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/InverterCharger/Power"); unit: "W"}
+ }
+
+ QtObject {
+ id: _battery
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Power"); unit: "W"}
+ property VBusItem voltage: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Voltage"); unit: "V"}
+ property VBusItem current: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Current"); unit: "A"}
+ property VBusItem soc: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Soc"); unit: "%"}
+
+ // Get the battery charge state, see batteryState properties
+ property VBusItem state: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/State")}
+ }
+
+ QtObject {
+ id: _dcSystem
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/System/Power"); unit: "W"}
+ }
+}
diff --git a/FileSets/v3.60~21/TileRelay.qml b/FileSets/v3.60~21/TileRelay.qml
new file mode 120000
index 00000000..ca5e8d2d
--- /dev/null
+++ b/FileSets/v3.60~21/TileRelay.qml
@@ -0,0 +1 @@
+../v3.60~25/TileRelay.qml
\ No newline at end of file
diff --git a/FileSets/v3.60~25/COMPLETE b/FileSets/v3.60~25/COMPLETE
new file mode 100644
index 00000000..e69de29b
diff --git a/FileSets/v3.60~25/DetailAcInput.qml b/FileSets/v3.60~25/DetailAcInput.qml
new file mode 100644
index 00000000..2ef58a00
--- /dev/null
+++ b/FileSets/v3.60~25/DetailAcInput.qml
@@ -0,0 +1,606 @@
+////// detail page for setting input current limit
+////// and displaying AC input details
+////// pushed from Flow overview
+
+import QtQuick 2
+import "utils.js" as Utils
+import com.victron.velib 1.0
+import "enhancedFormat.js" as EnhFmt
+
+MbPage {
+ id: root
+
+ title: "AC Input detail"
+
+ property variant sys: theSystem
+ property string systemPrefix: "com.victronenergy.system"
+ property string settingsPrefix: "com.victronenergy.settings"
+
+ property int fontPixelSize: 18
+ property color buttonColor: "#979797"
+ property color pressedColor: "#d3d3d3"
+ property color backgroundColor: "#b3b3b3"
+
+ property int buttonHeight: 40
+
+ property int dataColumns: 4
+ property int rowTitleWidth: 100
+ property int totalDataWidth: 340 - rowTitleWidth
+ property int tableColumnWidth: totalDataWidth / dataColumns
+
+ property int legColumnWidth: phaseCount <= 1 ? tableColumnWidth * 3 : tableColumnWidth * 3 / phaseCount
+
+ property int phaseCount: sys.acInput.phaseCount.valid ? sys.acInput.phaseCount.value : 0
+
+ VBusItem { id: vebusServiceItem; bind: Utils.path(systemPrefix, "/VebusService") }
+ property string inverterService: vebusServiceItem.valid ? vebusServiceItem.value : ""
+ property bool splitPhasePassthruDisabled: sys.acInput.splitPhaseL2PassthruDisabled
+
+ property real actualCurrentLimit: 0
+ property real newCurrentLimit: 0
+
+ VBusItem { id: acLimitPreset1Item; bind: Utils.path(settingsPrefix, "/Settings/GuiMods/AcCurrentLimit/Preset1") }
+ VBusItem { id: acLimitPreset2Item; bind: Utils.path(settingsPrefix, "/Settings/GuiMods/AcCurrentLimit/Preset2") }
+ VBusItem { id: acLimitPreset3Item; bind: Utils.path(settingsPrefix, "/Settings/GuiMods/AcCurrentLimit/Preset3") }
+ VBusItem { id: acLimitPreset4Item; bind: Utils.path(settingsPrefix, "/Settings/GuiMods/AcCurrentLimit/Preset4") }
+ property real acLimitPreset1: acLimitPreset1Item.valid ? acLimitPreset1Item.value : 0
+ property real acLimitPreset2: acLimitPreset2Item.valid ? acLimitPreset2Item.value : 0
+ property real acLimitPreset3: acLimitPreset3Item.valid ? acLimitPreset3Item.value : 0
+ property real acLimitPreset4: acLimitPreset4Item.valid ? acLimitPreset4Item.value : 0
+
+ property bool currentLimitIsAdjustable: currentLimitIsAdjustableItem.valid && currentLimitIsAdjustableItem.value == 1 && currentLimitItem.valid
+
+ Component.onCompleted: { getActualCurrent () }
+
+ VBusItem
+ {
+ id: currentLimitIsAdjustableItem
+ bind: Utils.path(inverterService, "/Ac/ActiveIn/CurrentLimitIsAdjustable")
+ onValueChanged: getActualCurrent ()
+ onValidChanged: getActualCurrent ()
+ }
+ VBusItem
+ {
+ id: currentLimitItem
+ bind: Utils.path(inverterService, "/Ac/ActiveIn/CurrentLimit")
+ onValueChanged: getActualCurrent ()
+ onValidChanged: getActualCurrent ()
+ }
+ VBusItem { id: activeInputItem; bind: Utils.path(inverterService, "/Ac/ActiveIn/ActiveInput") }
+ VBusItem { id: numberOfAcInputs; bind: Utils.path(inverterService, "/Ac/In/NumberOfAcInputs") }
+ VBusItem { id: activeSourceItem; bind: Utils.path(systemPrefix, "/Ac/ActiveIn/Source") }
+ VBusItem { id: acIn1sourceItem; bind: Utils.path(settingsPrefix, "/Settings/SystemSetup/AcInput1") }
+ VBusItem { id: acIn2sourceItem; bind: Utils.path(settingsPrefix, "/Settings/SystemSetup/AcInput2") }
+ property int activeSource: activeSourceItem.valid ? activeSourceItem.value : 0
+ property int acIn1source: acIn1sourceItem.valid ? acIn1sourceItem.value : 0
+ property int acIn2source: acIn2sourceItem.valid ? acIn2sourceItem.value : 0
+ property int activeInput: activeInputItem.valid && activeInputItem.value == 1 ? 2 : 1
+ property bool hasTwoInputs: numberOfAcInputs.valid && numberOfAcInputs.value == 2
+
+ property variant acSourceName: [qsTr("---"), qsTr("Grid"), qsTr("Generator"), qsTr("Shore")]
+
+ // background
+ Rectangle
+ {
+ anchors
+ {
+ fill: parent
+ }
+ color: root.backgroundColor
+ }
+
+ Row
+ {
+ spacing: 5
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.verticalCenter: parent.verticalCenter
+ width: parent.width - 6
+ Column
+ {
+ spacing: 2
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Total Power") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: tableColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.acInput.power, "W")
+ }
+ PowerGauge
+ {
+ id: gauge
+ width: totalDataWidth - tableColumnWidth
+ height: 15
+ connection: sys.acInput
+ useInputCurrentLimit: true
+ maxForwardPowerParameter: "" // handled internally - uses input current limit and AC input voltage
+ maxReversePowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/MaxFeedInPower"
+ }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth + tableColumnWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Active Source") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: totalDataWidth - tableColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text:
+ {
+ if (activeSource == 240)
+ return quTr ("no input")
+ else if (hasTwoInputs)
+ return acSourceName[activeSource] + " (AC in " + activeInput + ")"
+ else
+ return acSourceName[activeSource]
+ }
+ }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: "L1" }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: "L2"; visible: phaseCount >= 2 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: "L3"; visible: phaseCount >= 3 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: tableColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: qsTr ("Freq") }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Power") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.acInput.powerL1, "W") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: splitPhasePassthruDisabled ? "< < <" : EnhFmt.formatVBusItem (sys.acInput.powerL2, "W"); visible: phaseCount >= 2 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.acInput.powerL3, "W"); visible: phaseCount >= 3 }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Voltage / Freq") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.acInput.voltageL1, "V") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: splitPhasePassthruDisabled ? "< < <" : EnhFmt.formatVBusItem (sys.acInput.voltageL2, "V"); visible: phaseCount >= 2 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.acInput.voltageL3, "V"); visible: phaseCount >= 3 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: tableColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.acInput.frequency, "Hz") }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Current") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.acInput.currentL1, "A") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: splitPhasePassthruDisabled ? "< < <" : EnhFmt.formatVBusItem (sys.acInput.currentL2, "A"); visible: phaseCount >= 2 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.acInput.currentL3, "A"); visible: phaseCount >= 3 }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("CurrentLimit") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: totalDataWidth; horizontalAlignment: Text.AlignHCenter
+ text:
+ {
+ var newText
+ if (newCurrentLimit != actualCurrentLimit)
+ newText = qsTr(" New ") + newCurrentLimit.toFixed (1) + " A"
+ else
+ newText = ""
+ return currentLimitItem.valid ? currentLimitItem.value.toFixed (1) + " A" + newText: "--" }
+ }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth + totalDataWidth; horizontalAlignment: Text.AlignHCenter
+ text: "L2 values included in L1"
+ visible: splitPhasePassthruDisabled }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth + totalDataWidth; horizontalAlignment: Text.AlignHCenter
+ text: qsTr("Avaliable Sources") }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: "L1" }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: "L2"; visible: phaseCount >= 2 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: "L3"; visible: phaseCount >= 3 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: tableColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: qsTr ("Freq") }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: 20; horizontalAlignment: Text.AlignHCenter
+ text: activeSource == 1 || activeSource == 3 ? ">" : "" }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth - 20; horizontalAlignment: Text.AlignRight
+ text:
+ {
+ if (acIn1source == 3 || acIn2source == 3)
+ return acSourceName[3]
+ else
+ return acSourceName[1]
+ }
+ }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.grid.voltageL1, "V") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: splitPhasePassthruDisabled ? "< < <" : EnhFmt.formatVBusItem (sys.grid.voltageL2, "V"); visible: phaseCount >= 2 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.grid.voltageL3, "V"); visible: phaseCount >= 3 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: tableColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.grid.frequency, "Hz") }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: 20; horizontalAlignment: Text.AlignHCenter
+ text: activeSource == 2 ? ">" : "" }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth - 20; horizontalAlignment: Text.AlignRight
+ text: acSourceName[2] }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.genset.voltageL1, "V") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: splitPhasePassthruDisabled ? "< < <" : EnhFmt.formatVBusItem (sys.genset.voltageL2, "V"); visible: phaseCount >= 2 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.genset.voltageL3, "V"); visible: phaseCount >= 3 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: tableColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.genset.frequency, "Hz") }
+ }
+ }
+ Column
+ {
+ id: currentButtonColumn
+ width: 128
+ spacing: 4
+
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: currentButtonColumn.width; horizontalAlignment: Text.AlignHCenter
+ text: qsTr("Current Limit") }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: currentButtonColumn.width; horizontalAlignment: Text.AlignHCenter
+ text: qsTr("is not adjustable")}
+ visible: !currentLimitIsAdjustable
+ }
+ Row
+ {
+ visible: currentLimitIsAdjustable
+ width: (parent.width / 2) - 2
+ spacing: 4
+ DetailButton
+ {
+ id: preset1button
+ baseColor: newCurrentLimit === acLimitPreset1 ? "black" : root.buttonColor
+ pressedColor: root.pressedColor
+ opacity: acLimitPreset1 === 0 ? 0.001 : 1
+ height: 40
+ width: parent.width
+ onClicked: setNewValue (acLimitPreset1)
+ enabled: acLimitPreset1 === 0 ? false : true
+ content: TileText
+ {
+ text: qsTr(acLimitPreset1 + " A"); font.bold: true;
+ color: "white"
+ }
+ }
+ DetailButton
+ {
+ id: preset2button
+ baseColor: newCurrentLimit === acLimitPreset2 ? "black" : root.buttonColor
+ pressedColor: root.pressedColor
+ opacity: acLimitPreset2 === 0 ? 0.001 : 1
+ height: 40
+ width: parent.width
+ onClicked: setNewValue (acLimitPreset2)
+ enabled: acLimitPreset2 === 0 ? false : true
+ content: TileText
+ {
+ text: qsTr(acLimitPreset2 + " A"); font.bold: true;
+ color: "white"
+ }
+ }
+ }
+ Row
+ {
+ visible: currentLimitIsAdjustable
+ width: (parent.width / 2) - 2
+ spacing: 4
+ DetailButton
+ {
+ id: preset3button
+ baseColor: newCurrentLimit === acLimitPreset3 ? "black" : root.buttonColor
+ pressedColor: root.pressedColor
+ opacity: acLimitPreset3 === 0 ? 0.001 : 1
+ height: 40
+ width: parent.width
+ onClicked: setNewValue (acLimitPreset3)
+ enabled: acLimitPreset3 === 0 ? false : true
+ content: TileText
+ {
+ text: qsTr(acLimitPreset3 + " A"); font.bold: true;
+ color: "white"
+ }
+ }
+ DetailButton
+ {
+ visible: currentLimitIsAdjustable
+ id: preset4button
+ baseColor: newCurrentLimit === acLimitPreset4 ? "black" : root.buttonColor
+ pressedColor: root.pressedColor
+ opacity: acLimitPreset4 === 0 ? 0.001 : 1
+ height: 40
+ width: parent.width
+ onClicked: setNewValue (acLimitPreset4)
+ enabled: acLimitPreset4 === 0 ? false : true
+ content: TileText
+ {
+ text: qsTr(acLimitPreset4 + " A"); font.bold: true;
+ color: "white"
+ }
+ }
+ }
+ Row
+ {
+ visible: currentLimitIsAdjustable
+ width: (parent.width / 2) - 2
+ spacing: 4
+ DetailButton
+ {
+ id: trimMinus
+ baseColor: root.buttonColor
+ pressedColor: root.pressedColor
+ height: 40
+ width: parent.width
+ enablePressAndHold: true
+ onClicked: trimNewValue (-1)
+ enabled: newCurrentLimit === acLimitPreset4 ? false : true
+ content: TileText
+ {
+ text: qsTr("-1 A"); font.bold: true;
+ color: "white"
+ }
+ }
+ DetailButton
+ {
+ id: trimPlus
+ baseColor: root.buttonColor
+ pressedColor: root.pressedColor
+ height: 40
+ width: parent.width
+ enablePressAndHold: true
+ onClicked: trimNewValue (+1)
+ content: TileText
+ {
+ text: qsTr("+1 A"); font.bold: true;
+ color: "white"
+ }
+ }
+ }
+ Row
+ {
+ visible: currentLimitIsAdjustable
+ width: parent.width
+ spacing: 4
+ DetailButton
+ {
+ id: acceptButton
+ baseColor: root.buttonColor
+ pressedColor: root.pressedColor
+ height: 40
+ width: parent.width
+ onClicked: accept()
+ content: TileText { text: qsTr ("Accept New");
+ font.bold: true; color: newCurrentLimit !== actualCurrentLimit ? "white" : "#d9d9d9" }
+ }
+ }
+ }
+ }
+
+ function setNewValue (newValue)
+ {
+ if (currentLimitIsAdjustable)
+ newCurrentLimit = newValue
+ }
+
+ function trimNewValue (trimValue)
+ {
+ if (!currentLimitIsAdjustable)
+ return
+
+ newCurrentLimit += trimValue
+ if (newCurrentLimit < 0)
+ newCurrentLimit = 0
+ }
+
+ function cancel ()
+ {
+ newCurrentLimit = actualCurrentLimit
+ pageStack.pop()
+ }
+
+ function accept ()
+ {
+ if (currentLimitIsAdjustable)
+ {
+ currentLimitItem.setValue (newCurrentLimit)
+ pageStack.pop() // return to main screen after changing input current limit
+ }
+ }
+
+ function getActualCurrent ()
+ {
+ actualCurrentLimit = currentLimitItem.valid ? currentLimitItem.value : 0
+ newCurrentLimit = actualCurrentLimit
+ }
+
+ // When new service is found check if is a tank sensor
+ Connections
+ {
+ target: DBusServices
+ onDbusServiceFound: addService(service)
+ }
+
+ //// hard key handler
+ // used to press buttons when touch isn't available
+ // UP and DOWN buttons cycle through the list of buttons
+ // "space" button is used to simulate a button press
+ // button must be highlighted so that other uses of "space"
+ // will still occur
+
+ // list of buttons to be accessed via hard buttons
+ property variant buttonList:
+ [
+ preset1button, preset2button, preset3button, preset4button, trimMinus, trimPlus, acceptButton
+ ]
+
+ property int buttonIndex: 0
+
+ Timer
+ {
+ id: targetTimer
+ interval: 5000
+ repeat: false
+ running: false
+ onTriggered: { clearHighlight () }
+ }
+
+ Keys.forwardTo: [keyHandler]
+
+ Item
+ {
+ id: keyHandler
+ Keys.onDownPressed:
+ {
+ nextTarget (+1)
+ event.accepted = true
+ }
+
+ Keys.onUpPressed:
+ {
+ nextTarget (-1)
+ event.accepted = true
+ }
+ }
+
+ function nextTarget (increment)
+ {
+ // make one pass through all possible targets to find an enabled one
+ // if found, that's the new selectedTarget,
+ // if not selectedTarget does not change
+ var newIndex = buttonIndex
+ for (var i = 0; i < buttonList.length; i++)
+ {
+ // just restore highlight if not visible
+ if ( ! targetTimer.running && buttonList[newIndex].visible)
+ {
+ setActiveButton (buttonIndex)
+ return
+ }
+ newIndex += increment
+ if (newIndex >= buttonList.length)
+ newIndex = 0
+ else if (newIndex < 0)
+ newIndex = buttonList.length - 1
+ if (buttonList[newIndex].visible)
+ {
+ setActiveButton (newIndex)
+ break
+ }
+ }
+ }
+
+ // Keys.onSpacePressed doesn't work - stolen by pageHandler
+ // so build a custom page handler so "space" can be used to press a button
+ pageToolbarHandler: detailToolbarHandler
+ ToolbarHandlerPages
+ {
+ id: detailToolbarHandler
+ isDefault: true
+ function centerAction()
+ {
+ acceptSpaceButton ()
+ }
+ }
+
+ function acceptSpaceButton ()
+ {
+ if (targetTimer.running)
+ {
+ buttonList[buttonIndex].clicked ()
+ }
+ }
+
+ function setActiveButton (newIndex)
+ {
+ buttonIndex = newIndex
+ for (var i = 0; i < buttonList.length; i++)
+ if (i == newIndex)
+ buttonList[i].highlight = true
+ else
+ buttonList[i].highlight = false
+ targetTimer.restart ()
+ }
+
+ function clearHighlight ()
+ {
+ for (var i = 0; i < buttonList.length; i++)
+ buttonList[i].highlight = false
+ }
+}
diff --git a/FileSets/v3.60~25/DetailAcInput.qml.orig b/FileSets/v3.60~25/DetailAcInput.qml.orig
new file mode 100644
index 00000000..6efa32bf
--- /dev/null
+++ b/FileSets/v3.60~25/DetailAcInput.qml.orig
@@ -0,0 +1,170 @@
+import QtQuick 2
+import "utils.js" as Utils
+
+Item {
+ id: root
+
+ property string systemPrefix: "com.victronenergy.system"
+ property string vebusPrefix: _vebusService.valid ? _vebusService.value : ""
+
+ property variant battery: _battery
+ property alias dcSystem: _dcSystem
+ property alias pvCharger: _pvCharger
+ property alias pvOnAcIn1: _pvOnAcIn1
+ property alias pvOnAcIn2: _pvOnAcIn2
+ property alias pvOnAcOut: _pvOnAcOut
+ property alias inverterChargerDc: _inverterChargerDc
+ property alias acLoad: _acLoad
+ property alias acInLoad: _acInLoad
+ property alias acOutLoad: _acOutLoad
+ property alias grid: _grid
+ property alias genset: _genset
+ property alias acInput: _activein
+ property VBusItem systemType: VBusItem { bind: Utils.path(systemPrefix, "/SystemType") }
+ property bool hasGridMeter: _hasGridMeter.valid
+ property variant acSource: _acSource.value
+ property VBusItem preferRenewableEnergy: VBusItem { bind: Utils.path(vebusPrefix, "/Dc/0/PreferRenewableEnergy") }
+ property VBusItem remoteGeneratorSelected: VBusItem { bind: Utils.path(vebusPrefix, "/Ac/State/RemoteGeneratorSelected") }
+
+ property alias pvOnGrid: _pvOnAcIn2
+
+ property int batteryStateIdle: 0
+ property int batteryStateCharging: 1
+ property int batteryStateDischarging: 2
+
+ property int acSourceNotAvailable: 0
+ property int acSourceGrid: 1
+ property int acSourceGenset: 2
+ property int acSourceShore: 3 // same as grid
+
+ property alias pvInvertersProductIds: _pvInvertersProductIds
+ property alias batteryProductId: _batteryProductId
+
+ VBusItem {
+ id: _pvInvertersProductIds
+ bind: Utils.path(systemPrefix, "/PvInvertersProductIds")
+ }
+
+ VBusItem {
+ id: _batteryProductId
+ bind: Utils.path(systemPrefix, "/Dc/Battery/ProductId")
+ }
+
+ VBusItem {
+ id: _vebusService
+ bind: Utils.path(systemPrefix, "/VebusService")
+ }
+
+ QtObject {
+ id: _pvCharger
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Pv/Power"); unit: "W"}
+ }
+
+ ObjectAcConnection {
+ id: _pvOnAcOut
+ bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnOutput")
+ }
+
+ ObjectAcConnection {
+ id: _pvOnAcIn1
+ bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGenset")
+ }
+
+ ObjectAcConnection {
+ id: _pvOnAcIn2
+ bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGrid")
+ }
+
+ ObjectAcConnection {
+ id: _genset
+ splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0
+ bindPrefix: Utils.path(systemPrefix, "/Ac/Genset")
+ }
+
+ VBusItem {
+ id: _acSource
+ bind: Utils.path(systemPrefix, "/Ac/ActiveIn/Source")
+ }
+
+ VBusItem {
+ id: _hasGridMeter
+ bind: Utils.path(systemPrefix, "/Ac/Grid/DeviceType")
+ }
+
+ /*
+ * Single Multis that can be split-phase reports NrOfPhases of 2
+ * When L2 is disconnected from the input the output L1 and L2
+ * are shorted. This item indicates if L2 is passed through
+ * from AC-in to AC-out.
+ * 1: L2 is being passed through from AC-in to AC-out.
+ * 0: L1 and L2 are shorted together.
+ * invalid: The unit is configured in such way that its L2 output is not used.
+ */
+
+ VBusItem {
+ id: _splitPhaseL2Passthru
+ bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2Passthru")
+ }
+
+ VBusItem {
+ id: _l2L1OutSummed
+ bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2L1OutSummed")
+ }
+
+
+ ObjectAcConnection {
+ id: _grid
+ splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0
+ bindPrefix: Utils.path(systemPrefix, "/Ac/Grid")
+ }
+
+ ObjectAcConnection {
+ id: _activein
+ bindPrefix: Utils.path(systemPrefix, "/Ac/ActiveIn")
+ }
+
+ ObjectAcConnection {
+ id: _acLoad
+ l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0)
+ isAcOutput: true
+ bindPrefix: Utils.path(systemPrefix, "/Ac/Consumption")
+ }
+
+ ObjectAcConnection {
+ id: _acOutLoad
+ l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0)
+ isAcOutput: true
+ bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnOutput")
+ }
+
+ ObjectAcConnection {
+ id: _acInLoad
+ splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0
+ bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnInput")
+ }
+
+ ObjectAcConnection {
+ id: _acUnknown
+ }
+
+ QtObject {
+ id: _inverterChargerDc
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/InverterCharger/Power"); unit: "W"}
+ }
+
+ QtObject {
+ id: _battery
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Power"); unit: "W"}
+ property VBusItem voltage: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Voltage"); unit: "V"}
+ property VBusItem current: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Current"); unit: "A"}
+ property VBusItem soc: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Soc"); unit: "%"}
+
+ // Get the battery charge state, see batteryState properties
+ property VBusItem state: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/State")}
+ }
+
+ QtObject {
+ id: _dcSystem
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/System/Power"); unit: "W"}
+ }
+}
diff --git a/FileSets/v3.60~25/DetailInverter.qml b/FileSets/v3.60~25/DetailInverter.qml
new file mode 100644
index 00000000..923761b3
--- /dev/null
+++ b/FileSets/v3.60~25/DetailInverter.qml
@@ -0,0 +1,650 @@
+////// detail page for setting inverter mode
+////// and displaying inverter details
+////// pushed from Flow overview
+
+import QtQuick 2
+import "utils.js" as Utils
+import com.victron.velib 1.0
+import "enhancedFormat.js" as EnhFmt
+
+MbPage {
+ id: root
+
+ title: "Inverter detail"
+
+ property variant sys: theSystem
+ property string systemPrefix: "com.victronenergy.system"
+
+ property int fontPixelSize: 18
+ property color buttonColor: "#979797"
+ property color pressedColor: "#d3d3d3"
+ property color backgroundColor: "#b3b3b3"
+
+ property int inverterMode: inverterModeItem.valid ? inverterModeItem.value : 0
+ property bool editable: inverterService != "" && inverterModeItem.valid
+
+ property int buttonHeight: 40
+ property int buttonWidth: 72
+ property int buttonAreaWidth: buttonWidth * 2 + 4
+
+ property int rowTitleWidth: 132
+ property int dataColumns: 3
+ property int totalDataWidth: root.width - rowTitleWidth - buttonAreaWidth - 12
+ property int tableColumnWidth: totalDataWidth / dataColumns
+ property int legColumnWidth: phaseCount <= 1 ? totalDataWidth : totalDataWidth / phaseCount
+
+ property int numberOfMultis: 0
+ property int numberOfInverters: 0
+ property string inverterService: ""
+ property bool isInverter: numberOfMultis === 0 && numberOfInverters === 1
+
+ Component.onCompleted: { discoverServices(); highlightMode () }
+
+ property bool showChargePriority: numberOfMultis > 0 && sys.preferRenewableEnergy.valid
+ property bool preferRenewableEnergy: showChargePriority && sys.preferRenewableEnergy.value == 1
+ property bool autoReturnToRenewable: sys.remoteGeneratorSelected.valid
+ property bool acInIsGenerator: sys.acSource == 2
+
+ VBusItem
+ {
+ id: inverterModeItem
+ bind: Utils.path(inverterService, "/Mode")
+ onValidChanged: highlightMode ()
+ onValueChanged: highlightMode ()
+ }
+ property VBusItem systemState: VBusItem { bind: Utils.path(systemPrefix, "/SystemState/State") }
+ SystemState
+ {
+ id: vebusState
+ bind: systemState.valid ? Utils.path(systemPrefix, "/SystemState/State") : Utils.path(inverterService, "/State")
+ }
+ VBusItem { id: pInL1; bind: Utils.path(inverterService, "/Ac/ActiveIn/L1/P") }
+ VBusItem { id: pInL2; bind: Utils.path(inverterService, "/Ac/ActiveIn/L2/P") }
+ VBusItem { id: pInL3; bind: Utils.path(inverterService, "/Ac/ActiveIn/L2/P") }
+ VBusItem { id: vInL1; bind: Utils.path(inverterService, "/Ac/ActiveIn/L1/V") }
+ VBusItem { id: vInL2; bind: Utils.path(inverterService, "/Ac/ActiveIn/L2/V") }
+ VBusItem { id: vInL3; bind: Utils.path(inverterService, "/Ac/ActiveIn/L3/V") }
+ VBusItem { id: iInL1; bind: Utils.path(inverterService, "/Ac/ActiveIn/L1/I") }
+ VBusItem { id: iInL2; bind: Utils.path(inverterService, "/Ac/ActiveIn/L2/I") }
+ VBusItem { id: iInL3; bind: Utils.path(inverterService, "/Ac/ActiveIn/L3/I") }
+ VBusItem { id: pOutL1; bind: Utils.path(inverterService, "/Ac/Out/L1/P") }
+ VBusItem { id: pOutL2; bind: Utils.path(inverterService, "/Ac/Out/L2/P") }
+ VBusItem { id: pOutL3; bind: Utils.path(inverterService, "/Ac/Out/L3/P") }
+ VBusItem { id: vOutL1; bind: Utils.path(inverterService, "/Ac/Out/L1/V") }
+ VBusItem { id: vOutL2; bind: Utils.path(inverterService, "/Ac/Out/L2/V") }
+ VBusItem { id: vOutL3; bind: Utils.path(inverterService, "/Ac/Out/L3/V") }
+ VBusItem { id: iOutL1; bind: Utils.path(inverterService, "/Ac/Out/L1/I") }
+ VBusItem { id: iOutL2; bind: Utils.path(inverterService, "/Ac/Out/L2/I") }
+ VBusItem { id: iOutL3; bind: Utils.path(inverterService, "/Ac/Out/L3/I") }
+ VBusItem { id: fInL1; bind: Utils.path(inverterService, "/Ac/ActiveIn/L1/F") }
+ VBusItem { id: fOutL1; bind: Utils.path(inverterService, "/Ac/Out/L1/F") }
+ VBusItem { id: dcPower; bind: Utils.path(inverterService, "/Dc/0/Power") }
+ VBusItem { id: dcCurrent; bind: Utils.path(inverterService, "/Dc/0/Current") }
+ VBusItem { id: _l2L1OutSummed; bind: Utils.path(inverterService, "/Ac/State/SplitPhaseL2L1OutSummed") }
+ VBusItem { id: phaseCountItem; bind: Utils.path(inverterService, "/Ac/NumberOfPhases") }
+
+ property bool noL2inverter: _l2L1OutSummed.valid
+ property bool l2AndL1OutSummed: noL2inverter && _l2L1OutSummed.value === 1
+ property int phaseCount: phaseCountItem.valid ? phaseCountItem.value : 0
+
+ // background
+ Rectangle
+ {
+ anchors
+ {
+ fill: parent
+ }
+ color: root.backgroundColor
+ }
+
+ Column
+ {
+ spacing: 2
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: parent.left; anchors.leftMargin: 3
+ Row
+ {
+ PowerGaugeMulti
+ {
+ id: gauge
+ width: rowTitleWidth + totalDataWidth
+ height: 15
+ inverterService: root.inverterService
+ }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Total Power") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: totalDataWidth; horizontalAlignment: Text.AlignHCenter
+ text:
+ {
+ var total = 0
+ var totalValid = false
+ if (pOutL1.valid && pInL1.valid)
+ {
+ total += pOutL1.value - pInL1.value
+ totalValid = true
+ }
+ if (pOutL2.valid && pInL2.valid)
+ {
+ total += pOutL2.value - pInL2.value
+ totalValid = true
+ }
+ if (pOutL3.valid && pInL3.valid)
+ {
+ total += pOutL3.value - pInL3.value
+ totalValid = true
+ }
+ if (totalValid)
+ return EnhFmt.formatValue (total, "W")
+ else
+ return "--"
+ }
+ }
+ visible: phaseCount >= 2
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("State") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: totalDataWidth; horizontalAlignment: Text.AlignHCenter
+ text: vebusState.text }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: "" }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: "L1" }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: "L2" }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: "L3"; visible: phaseCount >= 3 }
+ visible: phaseCount >= 2
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Power") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: formatValueDiff (pOutL1, pInL1, "W") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text:
+ {
+ if (l2AndL1OutSummed)
+ return "< < <"
+ else if (noL2inverter)
+ return qsTr("none")
+ else
+ return formatValueDiff (pOutL2, pInL2, "W")
+ }
+ visible: phaseCount >= 2
+ }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: formatValueDiff (pOutL3, pInL3, "W"); visible: phaseCount >= 3 }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Input Voltage") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (vInL1, "V") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (vInL2, "V"); visible: phaseCount >= 2 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (vInL3, "V"); visible: phaseCount >= 3 }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Output Voltage") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (vOutL1, "V") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (vOutL2, "V"); visible: phaseCount >= 2 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (vOutL3, "V"); visible: phaseCount >= 3 }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Input Current") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (iInL1, "A") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (iInL2, "A"); visible: phaseCount >= 2 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (iInL3, "A"); visible: phaseCount >= 3 }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Output Current") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (iOutL1, "A") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: l2AndL1OutSummed ? "< < <" : EnhFmt.formatVBusItem (iOutL2, "A"); visible: phaseCount >= 2 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (iOutL3, "A"); visible: phaseCount >= 3 }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Frequency In / Out") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: totalDataWidth / 2; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (fInL1, "Hz") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: totalDataWidth / 2; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (fOutL1, "Hz") }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text:
+ {
+ if (! dcPower.valid)
+ return ""
+ else if (dcPower.value > 0)
+ return qsTr ("DC: supplying")
+ else if (dcPower.value < 0)
+ return qsTr ("DC: consuming")
+ else
+ return ""
+ }
+ }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: totalDataWidth / 2; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItemAbs (dcPower, "W") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: totalDataWidth / 2; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItemAbs (dcCurrent, "A") }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth + totalDataWidth; horizontalAlignment: Text.AlignHCenter
+ text: l2AndL1OutSummed ? qsTr ("L2 Output values included in L1") : qsTr ("L2 AC out from AC in (no inverter)")
+ visible: noL2inverter
+ }
+ }
+ }
+ Column
+ {
+ id: inverterModeButtonArea
+ width: root.buttonAreaWidth
+ anchors.top: parent.top; anchors.topMargin: 3
+ anchors.right: parent.right; anchors.rightMargin: 3
+ spacing: 4
+
+ Row
+ {
+ Text
+ {
+ font.pixelSize: 12; color: "black"
+ width: root.buttonAreaWidth; horizontalAlignment: Text.AlignHCenter
+ text: qsTr("Inverter mode")
+ visible: showChargePriority
+ }
+ }
+ Row
+ {
+ spacing: 4
+ DetailButton
+ {
+ id: onButton
+ baseColor: inverterMode === 3 ? "green" : "#e6ffe6"
+ pressedColor: root.pressedColor
+ height: root.buttonHeight
+ width: root.buttonWidth
+ visible: !isInverter
+ onClicked: changeMode(3)
+ content: TileText
+ {
+ text: qsTr("On"); font.bold: true;
+ color: inverterMode === 3 ? "white" : "gray"
+ }
+ }
+ DetailButton
+ {
+ id: offButton
+ baseColor: inverterMode === 4 ? "black" : "#e6e6e6"
+ pressedColor: root.pressedColor
+ height: root.buttonHeight
+ width: root.buttonWidth
+ onClicked: changeMode(4)
+ content: TileText
+ {
+ text: qsTr("Off"); font.bold: true;
+ color: inverterMode === 4 ? "white" : "gray"
+ }
+ }
+ }
+ Row
+ {
+ spacing: 4
+ DetailButton
+ {
+ id: invertOnlyButton
+ baseColor: inverterMode === 2 ? "blue" : "#ccccff"
+ pressedColor: root.pressedColor
+ height: root.buttonHeight
+ width: root.buttonWidth
+ onClicked: changeMode(2)
+ content: TileText
+ {
+ text: isInverter ? qsTr("On") : qsTr("Inverter\nOnly"); font.bold: true;
+ color: inverterMode === 2 ? "white" : "gray"
+ }
+ }
+ DetailButton
+ {
+ id: chargeOnlyButton
+ baseColor: inverterMode === 1 ? "orange" : "#ffedcc"
+ pressedColor: root.pressedColor
+ height: root.buttonHeight
+ width: root.buttonWidth
+ visible: !isInverter
+ onClicked: changeMode(1)
+ content: TileText
+ {
+ text: qsTr("Charger\nOnly"); font.bold: true;
+ color: inverterMode === 1 ? "white" : "gray"
+ }
+ }
+ DetailButton
+ {
+ id: ecoButton
+ baseColor: inverterMode === 5 ? "orange" : "#ffedcc"
+ pressedColor: root.pressedColor
+ height: root.buttonHeight
+ width: root.buttonWidth
+ visible: isInverter
+ onClicked: changeMode(5)
+ content: TileText
+ {
+ text: qsTr("Eco"); font.bold: true;
+ color: inverterMode === 5 ? "white" : "black"
+ }
+ }
+ }
+ }
+ Column
+ {
+ id: chargePriorityButtonArea
+ width: root.buttonAreaWidth
+ anchors.bottom: parent.bottom; anchors.bottomMargin: 3
+ anchors.right: parent.right; anchors.rightMargin: 3
+ spacing: 4
+
+ Row
+ {
+ Text
+ {
+ font.pixelSize: 12; color: "black"
+ width: root.buttonAreaWidth; horizontalAlignment: Text.AlignHCenter
+ text: qsTr("Charge priority")
+ visible: showChargePriority
+ }
+ }
+ Row
+ {
+ spacing: 4
+ DetailButton
+ {
+ id: acPriorityButton
+ baseColor: ! preferRenewableEnergy && ! acInIsGenerator ? "orange" : "#ffedcc"
+ pressedColor: root.pressedColor
+ height: root.buttonHeight
+ width: root.buttonWidth
+ onClicked: { if (! acInIsGenerator) sys.preferRenewableEnergy.setValue (0)}
+ visible: showChargePriority
+ content: TileText
+ {
+ text: "Grid"; font.bold: true
+ color: ! preferRenewableEnergy && ! acInIsGenerator ? "white" : "gray"
+ }
+ }
+ DetailButton
+ {
+ id: renewablePriorityButton
+ baseColor: preferRenewableEnergy && ! acInIsGenerator ? "green" : "#e6ffe6"
+ pressedColor: root.pressedColor
+ height: root.buttonHeight
+ width: root.buttonWidth
+ visible: showChargePriority
+ onClicked: { if (! acInIsGenerator) sys.preferRenewableEnergy.setValue (1)}
+ content: TileText
+ {
+ text: qsTr("Renew\nable"); font.bold: true
+ color: preferRenewableEnergy && ! acInIsGenerator ? "white" : "gray"
+ }
+ }
+ }
+ Row
+ {
+ Text
+ {
+ font.pixelSize: 12; color: "black"
+ width: root.buttonAreaWidth; horizontalAlignment: Text.AlignHCenter
+ text:
+ {
+ if (acInIsGenerator)
+ return qsTr ("Generator active\nno priority")
+ else if (autoReturnToRenewable && ! preferRenewableEnergy)
+ return qsTr ("returns to Renewable\n at 100% SOC")
+ else return "\n"
+ }
+ visible: showChargePriority
+ }
+ }
+ }
+
+
+ function changeMode(newMode)
+ {
+ if (editable)
+ {
+ inverterModeItem.setValue(newMode)
+ pageStack.pop() // return to flow screen after changing inverter mode
+ }
+ }
+
+ function cancel()
+ {
+ pageStack.pop()
+ }
+
+ function highlightMode ()
+ {
+ if (editable)
+ inverterMode = inverterModeItem.value
+ else
+ inverterMode = 0
+ }
+
+
+ // When new service is found check if is a tank sensor
+ Connections
+ {
+ target: DBusServices
+ onDbusServiceFound: addService(service)
+ }
+
+ function addService(service)
+ {
+ switch (service.type)
+ {
+ case DBusService.DBUS_SERVICE_MULTI:
+ case DBusService.DBUS_SERVICE_MULTI_RS:
+ numberOfMultis++
+ if (numberOfMultis === 1)
+ inverterService = service.name;
+ break;;
+ case DBusService.DBUS_SERVICE_INVERTER:
+ numberOfInverters++
+ if (numberOfInverters === 1 && numberOfMultis === 0)
+ inverterService = service.name;
+ break;;
+ }
+ }
+
+ // Detect available services of interest
+ function discoverServices()
+ {
+ numberOfMultis = 0
+ numberOfInverters = 0
+ inverterService = ""
+ for (var i = 0; i < DBusServices.count; i++)
+ {
+ addService(DBusServices.at(i))
+ }
+ }
+
+ function formatValueDiff (item1, item2, unit)
+ {
+ if (item1.valid && item2.valid)
+ return EnhFmt.formatValue (item1.value - item2.value, unit)
+ else
+ return "--"
+ }
+
+
+ //// hard key handler
+ // used to press buttons when touch isn't available
+ // UP and DOWN buttons cycle through the list of buttons
+ // "space" button is used to simulate a button press
+ // button must be highlighted so that other uses of "space"
+ // will still occur
+
+ // list of buttons to be accessed via hard buttons
+ property variant buttonList:
+ [
+ onButton, offButton, invertOnlyButton, chargeOnlyButton, ecoButton
+ ]
+
+ property int buttonIndex: 0
+
+ Timer
+ {
+ id: targetTimer
+ interval: 5000
+ repeat: false
+ running: false
+ onTriggered: { clearHighlight () }
+ }
+
+ Keys.forwardTo: [keyHandler]
+
+ Item
+ {
+ id: keyHandler
+ Keys.onDownPressed:
+ {
+ nextTarget (+1)
+ event.accepted = true
+ }
+
+ Keys.onUpPressed:
+ {
+ nextTarget (-1)
+ event.accepted = true
+ }
+ }
+
+ function nextTarget (increment)
+ {
+ // make one pass through all possible targets to find an enabled one
+ // if found, that's the new selectedTarget,
+ // if not selectedTarget does not change
+ var newIndex = buttonIndex
+ for (var i = 0; i < buttonList.length; i++)
+ {
+ // just restore highlight if not visible
+ if ( ! targetTimer.running && buttonList[newIndex].visible)
+ {
+ setActiveButton (buttonIndex)
+ return
+ }
+ newIndex += increment
+ if (newIndex >= buttonList.length)
+ newIndex = 0
+ else if (newIndex < 0)
+ newIndex = buttonList.length - 1
+ if (buttonList[newIndex].visible)
+ {
+ setActiveButton (newIndex)
+ break
+ }
+ }
+ }
+
+ // Keys.onSpacePressed doesn't work - stolen by pageHandler
+ // so build a custom page handler so "space" can be used to press a button
+ pageToolbarHandler: detailToolbarHandler
+ ToolbarHandlerPages
+ {
+ id: detailToolbarHandler
+ isDefault: true
+ function centerAction()
+ {
+ acceptSpaceButton ()
+ }
+ }
+
+ function acceptSpaceButton ()
+ {
+ if (targetTimer.running)
+ {
+ buttonList[buttonIndex].clicked ()
+ }
+ }
+
+ function setActiveButton (newIndex)
+ {
+ buttonIndex = newIndex
+ for (var i = 0; i < buttonList.length; i++)
+ if (i == newIndex)
+ buttonList[i].highlight = true
+ else
+ buttonList[i].highlight = false
+ targetTimer.restart ()
+ }
+
+ function clearHighlight ()
+ {
+ for (var i = 0; i < buttonList.length; i++)
+ buttonList[i].highlight = false
+ }
+}
diff --git a/FileSets/v3.60~25/DetailInverter.qml.orig b/FileSets/v3.60~25/DetailInverter.qml.orig
new file mode 100644
index 00000000..6efa32bf
--- /dev/null
+++ b/FileSets/v3.60~25/DetailInverter.qml.orig
@@ -0,0 +1,170 @@
+import QtQuick 2
+import "utils.js" as Utils
+
+Item {
+ id: root
+
+ property string systemPrefix: "com.victronenergy.system"
+ property string vebusPrefix: _vebusService.valid ? _vebusService.value : ""
+
+ property variant battery: _battery
+ property alias dcSystem: _dcSystem
+ property alias pvCharger: _pvCharger
+ property alias pvOnAcIn1: _pvOnAcIn1
+ property alias pvOnAcIn2: _pvOnAcIn2
+ property alias pvOnAcOut: _pvOnAcOut
+ property alias inverterChargerDc: _inverterChargerDc
+ property alias acLoad: _acLoad
+ property alias acInLoad: _acInLoad
+ property alias acOutLoad: _acOutLoad
+ property alias grid: _grid
+ property alias genset: _genset
+ property alias acInput: _activein
+ property VBusItem systemType: VBusItem { bind: Utils.path(systemPrefix, "/SystemType") }
+ property bool hasGridMeter: _hasGridMeter.valid
+ property variant acSource: _acSource.value
+ property VBusItem preferRenewableEnergy: VBusItem { bind: Utils.path(vebusPrefix, "/Dc/0/PreferRenewableEnergy") }
+ property VBusItem remoteGeneratorSelected: VBusItem { bind: Utils.path(vebusPrefix, "/Ac/State/RemoteGeneratorSelected") }
+
+ property alias pvOnGrid: _pvOnAcIn2
+
+ property int batteryStateIdle: 0
+ property int batteryStateCharging: 1
+ property int batteryStateDischarging: 2
+
+ property int acSourceNotAvailable: 0
+ property int acSourceGrid: 1
+ property int acSourceGenset: 2
+ property int acSourceShore: 3 // same as grid
+
+ property alias pvInvertersProductIds: _pvInvertersProductIds
+ property alias batteryProductId: _batteryProductId
+
+ VBusItem {
+ id: _pvInvertersProductIds
+ bind: Utils.path(systemPrefix, "/PvInvertersProductIds")
+ }
+
+ VBusItem {
+ id: _batteryProductId
+ bind: Utils.path(systemPrefix, "/Dc/Battery/ProductId")
+ }
+
+ VBusItem {
+ id: _vebusService
+ bind: Utils.path(systemPrefix, "/VebusService")
+ }
+
+ QtObject {
+ id: _pvCharger
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Pv/Power"); unit: "W"}
+ }
+
+ ObjectAcConnection {
+ id: _pvOnAcOut
+ bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnOutput")
+ }
+
+ ObjectAcConnection {
+ id: _pvOnAcIn1
+ bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGenset")
+ }
+
+ ObjectAcConnection {
+ id: _pvOnAcIn2
+ bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGrid")
+ }
+
+ ObjectAcConnection {
+ id: _genset
+ splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0
+ bindPrefix: Utils.path(systemPrefix, "/Ac/Genset")
+ }
+
+ VBusItem {
+ id: _acSource
+ bind: Utils.path(systemPrefix, "/Ac/ActiveIn/Source")
+ }
+
+ VBusItem {
+ id: _hasGridMeter
+ bind: Utils.path(systemPrefix, "/Ac/Grid/DeviceType")
+ }
+
+ /*
+ * Single Multis that can be split-phase reports NrOfPhases of 2
+ * When L2 is disconnected from the input the output L1 and L2
+ * are shorted. This item indicates if L2 is passed through
+ * from AC-in to AC-out.
+ * 1: L2 is being passed through from AC-in to AC-out.
+ * 0: L1 and L2 are shorted together.
+ * invalid: The unit is configured in such way that its L2 output is not used.
+ */
+
+ VBusItem {
+ id: _splitPhaseL2Passthru
+ bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2Passthru")
+ }
+
+ VBusItem {
+ id: _l2L1OutSummed
+ bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2L1OutSummed")
+ }
+
+
+ ObjectAcConnection {
+ id: _grid
+ splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0
+ bindPrefix: Utils.path(systemPrefix, "/Ac/Grid")
+ }
+
+ ObjectAcConnection {
+ id: _activein
+ bindPrefix: Utils.path(systemPrefix, "/Ac/ActiveIn")
+ }
+
+ ObjectAcConnection {
+ id: _acLoad
+ l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0)
+ isAcOutput: true
+ bindPrefix: Utils.path(systemPrefix, "/Ac/Consumption")
+ }
+
+ ObjectAcConnection {
+ id: _acOutLoad
+ l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0)
+ isAcOutput: true
+ bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnOutput")
+ }
+
+ ObjectAcConnection {
+ id: _acInLoad
+ splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0
+ bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnInput")
+ }
+
+ ObjectAcConnection {
+ id: _acUnknown
+ }
+
+ QtObject {
+ id: _inverterChargerDc
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/InverterCharger/Power"); unit: "W"}
+ }
+
+ QtObject {
+ id: _battery
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Power"); unit: "W"}
+ property VBusItem voltage: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Voltage"); unit: "V"}
+ property VBusItem current: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Current"); unit: "A"}
+ property VBusItem soc: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Soc"); unit: "%"}
+
+ // Get the battery charge state, see batteryState properties
+ property VBusItem state: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/State")}
+ }
+
+ QtObject {
+ id: _dcSystem
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/System/Power"); unit: "W"}
+ }
+}
diff --git a/FileSets/v3.60~25/DetailLoadsCombined.qml b/FileSets/v3.60~25/DetailLoadsCombined.qml
new file mode 100644
index 00000000..025e55a7
--- /dev/null
+++ b/FileSets/v3.60~25/DetailLoadsCombined.qml
@@ -0,0 +1,146 @@
+////// detail page for displaying critical AC output details
+////// pushed from Flow overview
+
+import QtQuick 2
+import "utils.js" as Utils
+import com.victron.velib 1.0
+import "enhancedFormat.js" as EnhFmt
+
+MbPage {
+ id: root
+
+ title: qsTr ("AC Loads detail")
+
+ property variant sys: theSystem
+ property string systemPrefix: "com.victronenergy.system"
+
+ property int fontPixelSize: 18
+ property color backgroundColor: "#b3b3b3"
+
+ property int dataColumns: 3
+ property int rowTitleWidth: 130
+ property int totalDataWidth: root.width - rowTitleWidth - 20
+ property int tableColumnWidth: totalDataWidth / dataColumns
+ property int legColumnWidth: phaseCount <= 1 ? totalDataWidth : totalDataWidth / phaseCount
+
+ property int phaseCount: sys.acLoad.phaseCount.valid ? sys.acLoad.phaseCount.value : 0
+
+ VBusItem { id: vebusServiceItem; bind: Utils.path(systemPrefix, "/VebusService") }
+ property string inverterService: vebusServiceItem.valid ? vebusServiceItem.value : ""
+ property bool l2AndL1OutSummed: sys.acLoad.l2AndL1OutSummed
+
+ // background
+ Rectangle
+ {
+ anchors
+ {
+ fill: parent
+ }
+ color: root.backgroundColor
+ }
+
+ Row
+ {
+ spacing: 5
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.verticalCenter: parent.verticalCenter
+ Column
+ {
+ spacing: 2
+ Row
+ {
+ Text { id: totalLabel; font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Total Power") }
+ Text { id: totalPower; font.pixelSize: 12; font.bold: true; color: "black"
+ width: tableColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.acLoad.power, "W")
+ }
+ PowerGauge
+ {
+ id: gauge
+ width: (root.width * 0.9) - totalLabel.width - totalPower.width
+ height: 15
+ maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/AcOutputMaxPower"
+ connection: sys.acLoad
+ }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: "" }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: "L1" }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: "L2"; visible: phaseCount >= 2 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: "L3"; visible: phaseCount >= 3 }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Power") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.acLoad.powerL1, "W") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: l2AndL1OutSummed ? "< < <" : EnhFmt.formatVBusItem (sys.acLoad.powerL2, "W"); visible: phaseCount >= 2 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.acLoad.powerL3, "W"); visible: phaseCount >= 3 }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Voltage") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.acLoad.voltageL1, "V") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: l2AndL1OutSummed ? "< < <" : EnhFmt.formatVBusItem (sys.acLoad.voltageL2, "V"); visible: phaseCount >= 2 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.acLoad.voltageL3, "V"); visible: phaseCount >= 3 }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Current") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.acLoad.currentL1, "A") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: l2AndL1OutSummed ? "< < <" : EnhFmt.formatVBusItem (sys.acLoad.currentL2, "A"); visible: phaseCount >= 2 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.acLoad.currentL3, "A"); visible: phaseCount >= 3 }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Frequency") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: totalDataWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.acLoad.frequency, "Hz") }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth + totalDataWidth; horizontalAlignment: Text.AlignHCenter
+ text: "L2 values included in L1"
+ visible: l2AndL1OutSummed }
+ }
+ }
+ }
+}
diff --git a/FileSets/v3.60~25/DetailLoadsCombined.qml.orig b/FileSets/v3.60~25/DetailLoadsCombined.qml.orig
new file mode 100644
index 00000000..6efa32bf
--- /dev/null
+++ b/FileSets/v3.60~25/DetailLoadsCombined.qml.orig
@@ -0,0 +1,170 @@
+import QtQuick 2
+import "utils.js" as Utils
+
+Item {
+ id: root
+
+ property string systemPrefix: "com.victronenergy.system"
+ property string vebusPrefix: _vebusService.valid ? _vebusService.value : ""
+
+ property variant battery: _battery
+ property alias dcSystem: _dcSystem
+ property alias pvCharger: _pvCharger
+ property alias pvOnAcIn1: _pvOnAcIn1
+ property alias pvOnAcIn2: _pvOnAcIn2
+ property alias pvOnAcOut: _pvOnAcOut
+ property alias inverterChargerDc: _inverterChargerDc
+ property alias acLoad: _acLoad
+ property alias acInLoad: _acInLoad
+ property alias acOutLoad: _acOutLoad
+ property alias grid: _grid
+ property alias genset: _genset
+ property alias acInput: _activein
+ property VBusItem systemType: VBusItem { bind: Utils.path(systemPrefix, "/SystemType") }
+ property bool hasGridMeter: _hasGridMeter.valid
+ property variant acSource: _acSource.value
+ property VBusItem preferRenewableEnergy: VBusItem { bind: Utils.path(vebusPrefix, "/Dc/0/PreferRenewableEnergy") }
+ property VBusItem remoteGeneratorSelected: VBusItem { bind: Utils.path(vebusPrefix, "/Ac/State/RemoteGeneratorSelected") }
+
+ property alias pvOnGrid: _pvOnAcIn2
+
+ property int batteryStateIdle: 0
+ property int batteryStateCharging: 1
+ property int batteryStateDischarging: 2
+
+ property int acSourceNotAvailable: 0
+ property int acSourceGrid: 1
+ property int acSourceGenset: 2
+ property int acSourceShore: 3 // same as grid
+
+ property alias pvInvertersProductIds: _pvInvertersProductIds
+ property alias batteryProductId: _batteryProductId
+
+ VBusItem {
+ id: _pvInvertersProductIds
+ bind: Utils.path(systemPrefix, "/PvInvertersProductIds")
+ }
+
+ VBusItem {
+ id: _batteryProductId
+ bind: Utils.path(systemPrefix, "/Dc/Battery/ProductId")
+ }
+
+ VBusItem {
+ id: _vebusService
+ bind: Utils.path(systemPrefix, "/VebusService")
+ }
+
+ QtObject {
+ id: _pvCharger
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Pv/Power"); unit: "W"}
+ }
+
+ ObjectAcConnection {
+ id: _pvOnAcOut
+ bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnOutput")
+ }
+
+ ObjectAcConnection {
+ id: _pvOnAcIn1
+ bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGenset")
+ }
+
+ ObjectAcConnection {
+ id: _pvOnAcIn2
+ bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGrid")
+ }
+
+ ObjectAcConnection {
+ id: _genset
+ splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0
+ bindPrefix: Utils.path(systemPrefix, "/Ac/Genset")
+ }
+
+ VBusItem {
+ id: _acSource
+ bind: Utils.path(systemPrefix, "/Ac/ActiveIn/Source")
+ }
+
+ VBusItem {
+ id: _hasGridMeter
+ bind: Utils.path(systemPrefix, "/Ac/Grid/DeviceType")
+ }
+
+ /*
+ * Single Multis that can be split-phase reports NrOfPhases of 2
+ * When L2 is disconnected from the input the output L1 and L2
+ * are shorted. This item indicates if L2 is passed through
+ * from AC-in to AC-out.
+ * 1: L2 is being passed through from AC-in to AC-out.
+ * 0: L1 and L2 are shorted together.
+ * invalid: The unit is configured in such way that its L2 output is not used.
+ */
+
+ VBusItem {
+ id: _splitPhaseL2Passthru
+ bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2Passthru")
+ }
+
+ VBusItem {
+ id: _l2L1OutSummed
+ bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2L1OutSummed")
+ }
+
+
+ ObjectAcConnection {
+ id: _grid
+ splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0
+ bindPrefix: Utils.path(systemPrefix, "/Ac/Grid")
+ }
+
+ ObjectAcConnection {
+ id: _activein
+ bindPrefix: Utils.path(systemPrefix, "/Ac/ActiveIn")
+ }
+
+ ObjectAcConnection {
+ id: _acLoad
+ l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0)
+ isAcOutput: true
+ bindPrefix: Utils.path(systemPrefix, "/Ac/Consumption")
+ }
+
+ ObjectAcConnection {
+ id: _acOutLoad
+ l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0)
+ isAcOutput: true
+ bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnOutput")
+ }
+
+ ObjectAcConnection {
+ id: _acInLoad
+ splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0
+ bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnInput")
+ }
+
+ ObjectAcConnection {
+ id: _acUnknown
+ }
+
+ QtObject {
+ id: _inverterChargerDc
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/InverterCharger/Power"); unit: "W"}
+ }
+
+ QtObject {
+ id: _battery
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Power"); unit: "W"}
+ property VBusItem voltage: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Voltage"); unit: "V"}
+ property VBusItem current: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Current"); unit: "A"}
+ property VBusItem soc: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Soc"); unit: "%"}
+
+ // Get the battery charge state, see batteryState properties
+ property VBusItem state: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/State")}
+ }
+
+ QtObject {
+ id: _dcSystem
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/System/Power"); unit: "W"}
+ }
+}
diff --git a/FileSets/v3.60~25/DetailLoadsOnInput.qml b/FileSets/v3.60~25/DetailLoadsOnInput.qml
new file mode 100644
index 00000000..fd228670
--- /dev/null
+++ b/FileSets/v3.60~25/DetailLoadsOnInput.qml
@@ -0,0 +1,146 @@
+////// detail page for displaying non-critical AC output details
+////// pushed from Flow overview
+
+import QtQuick 2
+import "utils.js" as Utils
+import com.victron.velib 1.0
+import "enhancedFormat.js" as EnhFmt
+
+MbPage {
+ id: root
+
+ title: "Loads on AC Input Detail"
+
+ property variant sys: theSystem
+ property string systemPrefix: "com.victronenergy.system"
+
+ property int fontPixelSize: 18
+ property color backgroundColor: "#b3b3b3"
+
+ property int dataColumns: 3
+ property int rowTitleWidth: 130
+ property int totalDataWidth: root.width - rowTitleWidth - 20
+ property int tableColumnWidth: totalDataWidth / dataColumns
+ property int legColumnWidth: phaseCount <= 1 ? totalDataWidth : totalDataWidth / phaseCount
+
+ property int phaseCount: sys.acInLoad.phaseCount.valid ? sys.acInLoad.phaseCount.value : 0
+
+ VBusItem { id: vebusServiceItem; bind: Utils.path(systemPrefix, "/VebusService") }
+ property string inverterService: vebusServiceItem.valid ? vebusServiceItem.value : ""
+ property bool splitPhasePassthruDisabled: sys.acInput.splitPhasePassthruDisabled
+
+ // background
+ Rectangle
+ {
+ anchors
+ {
+ fill: parent
+ }
+ color: root.backgroundColor
+ }
+
+ Row
+ {
+ spacing: 5
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.verticalCenter: parent.verticalCenter
+ Column
+ {
+ spacing: 2
+ Row
+ {
+ Text { id: totalLabel; font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Total Power") }
+ Text { id: totalPower; font.pixelSize: 12; font.bold: true; color: "black"
+ width: tableColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.acInLoad.power, "W")
+ }
+ PowerGauge
+ {
+ id: gauge
+ width: (root.width * 0.9) - totalLabel.width - totalPower.width
+ height: 15
+ maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/AcOutputMaxPower"
+ connection: sys.acInLoad
+ }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: "" }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: "L1" }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: "L2"; visible: phaseCount >= 2 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: "L3"; visible: phaseCount >= 3 }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Power") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.acInLoad.powerL1, "W") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: splitPhasePassthruDisabled ? "< < <" : EnhFmt.formatVBusItem (sys.acInLoad.powerL2, "W"); visible: phaseCount >= 2 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.acInLoad.powerL3, "W"); visible: phaseCount >= 3 }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Voltage") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.acInLoad.voltageL1, "V") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: splitPhasePassthruDisabled ? "< < <" : EnhFmt.formatVBusItem (sys.acInLoad.voltageL2, "V"); visible: phaseCount >= 2 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.acInLoad.voltageL3, "V"); visible: phaseCount >= 3 }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Current") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.acInLoad.currentL1, "A") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: splitPhasePassthruDisabled ? "< < <" : EnhFmt.formatVBusItem (sys.acInLoad.currentL2, "A"); visible: phaseCount >= 2 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.acInLoad.currentL3, "A"); visible: phaseCount >= 3 }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Frequency") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: totalDataWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (sys.acInLoad.frequency, "Hz") }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth + totalDataWidth; horizontalAlignment: Text.AlignHCenter
+ text: "L2 values included in L1"
+ visible: splitPhasePassthruDisabled }
+ }
+ }
+ }
+}
diff --git a/FileSets/v3.60~25/DetailLoadsOnInput.qml.orig b/FileSets/v3.60~25/DetailLoadsOnInput.qml.orig
new file mode 100644
index 00000000..6efa32bf
--- /dev/null
+++ b/FileSets/v3.60~25/DetailLoadsOnInput.qml.orig
@@ -0,0 +1,170 @@
+import QtQuick 2
+import "utils.js" as Utils
+
+Item {
+ id: root
+
+ property string systemPrefix: "com.victronenergy.system"
+ property string vebusPrefix: _vebusService.valid ? _vebusService.value : ""
+
+ property variant battery: _battery
+ property alias dcSystem: _dcSystem
+ property alias pvCharger: _pvCharger
+ property alias pvOnAcIn1: _pvOnAcIn1
+ property alias pvOnAcIn2: _pvOnAcIn2
+ property alias pvOnAcOut: _pvOnAcOut
+ property alias inverterChargerDc: _inverterChargerDc
+ property alias acLoad: _acLoad
+ property alias acInLoad: _acInLoad
+ property alias acOutLoad: _acOutLoad
+ property alias grid: _grid
+ property alias genset: _genset
+ property alias acInput: _activein
+ property VBusItem systemType: VBusItem { bind: Utils.path(systemPrefix, "/SystemType") }
+ property bool hasGridMeter: _hasGridMeter.valid
+ property variant acSource: _acSource.value
+ property VBusItem preferRenewableEnergy: VBusItem { bind: Utils.path(vebusPrefix, "/Dc/0/PreferRenewableEnergy") }
+ property VBusItem remoteGeneratorSelected: VBusItem { bind: Utils.path(vebusPrefix, "/Ac/State/RemoteGeneratorSelected") }
+
+ property alias pvOnGrid: _pvOnAcIn2
+
+ property int batteryStateIdle: 0
+ property int batteryStateCharging: 1
+ property int batteryStateDischarging: 2
+
+ property int acSourceNotAvailable: 0
+ property int acSourceGrid: 1
+ property int acSourceGenset: 2
+ property int acSourceShore: 3 // same as grid
+
+ property alias pvInvertersProductIds: _pvInvertersProductIds
+ property alias batteryProductId: _batteryProductId
+
+ VBusItem {
+ id: _pvInvertersProductIds
+ bind: Utils.path(systemPrefix, "/PvInvertersProductIds")
+ }
+
+ VBusItem {
+ id: _batteryProductId
+ bind: Utils.path(systemPrefix, "/Dc/Battery/ProductId")
+ }
+
+ VBusItem {
+ id: _vebusService
+ bind: Utils.path(systemPrefix, "/VebusService")
+ }
+
+ QtObject {
+ id: _pvCharger
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Pv/Power"); unit: "W"}
+ }
+
+ ObjectAcConnection {
+ id: _pvOnAcOut
+ bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnOutput")
+ }
+
+ ObjectAcConnection {
+ id: _pvOnAcIn1
+ bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGenset")
+ }
+
+ ObjectAcConnection {
+ id: _pvOnAcIn2
+ bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGrid")
+ }
+
+ ObjectAcConnection {
+ id: _genset
+ splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0
+ bindPrefix: Utils.path(systemPrefix, "/Ac/Genset")
+ }
+
+ VBusItem {
+ id: _acSource
+ bind: Utils.path(systemPrefix, "/Ac/ActiveIn/Source")
+ }
+
+ VBusItem {
+ id: _hasGridMeter
+ bind: Utils.path(systemPrefix, "/Ac/Grid/DeviceType")
+ }
+
+ /*
+ * Single Multis that can be split-phase reports NrOfPhases of 2
+ * When L2 is disconnected from the input the output L1 and L2
+ * are shorted. This item indicates if L2 is passed through
+ * from AC-in to AC-out.
+ * 1: L2 is being passed through from AC-in to AC-out.
+ * 0: L1 and L2 are shorted together.
+ * invalid: The unit is configured in such way that its L2 output is not used.
+ */
+
+ VBusItem {
+ id: _splitPhaseL2Passthru
+ bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2Passthru")
+ }
+
+ VBusItem {
+ id: _l2L1OutSummed
+ bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2L1OutSummed")
+ }
+
+
+ ObjectAcConnection {
+ id: _grid
+ splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0
+ bindPrefix: Utils.path(systemPrefix, "/Ac/Grid")
+ }
+
+ ObjectAcConnection {
+ id: _activein
+ bindPrefix: Utils.path(systemPrefix, "/Ac/ActiveIn")
+ }
+
+ ObjectAcConnection {
+ id: _acLoad
+ l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0)
+ isAcOutput: true
+ bindPrefix: Utils.path(systemPrefix, "/Ac/Consumption")
+ }
+
+ ObjectAcConnection {
+ id: _acOutLoad
+ l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0)
+ isAcOutput: true
+ bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnOutput")
+ }
+
+ ObjectAcConnection {
+ id: _acInLoad
+ splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0
+ bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnInput")
+ }
+
+ ObjectAcConnection {
+ id: _acUnknown
+ }
+
+ QtObject {
+ id: _inverterChargerDc
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/InverterCharger/Power"); unit: "W"}
+ }
+
+ QtObject {
+ id: _battery
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Power"); unit: "W"}
+ property VBusItem voltage: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Voltage"); unit: "V"}
+ property VBusItem current: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Current"); unit: "A"}
+ property VBusItem soc: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Soc"); unit: "%"}
+
+ // Get the battery charge state, see batteryState properties
+ property VBusItem state: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/State")}
+ }
+
+ QtObject {
+ id: _dcSystem
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/System/Power"); unit: "W"}
+ }
+}
diff --git a/FileSets/v3.60~25/DetailLoadsOnOutput.qml b/FileSets/v3.60~25/DetailLoadsOnOutput.qml
new file mode 100644
index 00000000..9344a5f1
--- /dev/null
+++ b/FileSets/v3.60~25/DetailLoadsOnOutput.qml
@@ -0,0 +1,151 @@
+////// detail page for displaying critical AC output details
+////// pushed from Flow overview
+
+import QtQuick 2
+import "utils.js" as Utils
+import com.victron.velib 1.0
+import "enhancedFormat.js" as EnhFmt
+
+MbPage {
+ id: root
+
+ title: combineAcLoads ? qsTr ("AC Loads detail") : qsTr ("Loads on AC Output detail")
+
+ property variant sys: theSystem
+ property string systemPrefix: "com.victronenergy.system"
+ property string settingsPrefix: "com.victronenergy.settings"
+
+ property int fontPixelSize: 18
+ property color backgroundColor: "#b3b3b3"
+
+ property int dataColumns: 3
+ property int rowTitleWidth: 130
+ property int totalDataWidth: root.width - rowTitleWidth - 20
+ property int tableColumnWidth: totalDataWidth / dataColumns
+ property int legColumnWidth: phaseCount <= 1 ? totalDataWidth : totalDataWidth / phaseCount
+
+ property int phaseCount: outputLoad.phaseCount.valid ? outputLoad.phaseCount.value : 0
+
+ VBusItem { id: vebusServiceItem; bind: Utils.path(systemPrefix, "/VebusService") }
+ property string inverterService: vebusServiceItem.valid ? vebusServiceItem.value : ""
+ property bool l2AndL1OutSummed: sys.acLoad.l2AndL1OutSummed
+
+ VBusItem { id: _combineAcLoads; bind: "com.victronenergy.settings/Settings/GuiMods/EnhancedFlowCombineLoads" }
+ property bool combineAcLoads: _combineAcLoads.valid && _combineAcLoads.value === 1
+ property variant outputLoad: combineAcLoads ? sys.acLoad : sys.acOutLoad
+
+ // background
+ Rectangle
+ {
+ anchors
+ {
+ fill: parent
+ }
+ color: root.backgroundColor
+ }
+
+ Row
+ {
+ spacing: 5
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.verticalCenter: parent.verticalCenter
+ Column
+ {
+ spacing: 2
+ Row
+ {
+ Text { id: totalLabel; font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Total Power") }
+ Text { id: totalPower; font.pixelSize: 12; font.bold: true; color: "black"
+ width: tableColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (outputLoad.power, "W")
+ }
+ PowerGauge
+ {
+ id: gauge
+ width: (root.width * 0.9) - totalLabel.width - totalPower.width
+ height: 15
+ maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/AcOutputMaxPower"
+ connection: outputLoad
+ }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: "" }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: "L1" }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: "L2"; visible: phaseCount >= 2 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: "L3"; visible: phaseCount >= 3 }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Power") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (outputLoad.powerL1, "W") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: l2AndL1OutSummed ? "< < <" : EnhFmt.formatVBusItem (outputLoad.powerL2, "W"); visible: phaseCount >= 2 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (outputLoad.powerL3, "W"); visible: phaseCount >= 3 }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Voltage") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (outputLoad.voltageL1, "V") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: l2AndL1OutSummed ? "< < <" : EnhFmt.formatVBusItem (outputLoad.voltageL2, "V"); visible: phaseCount >= 2 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (outputLoad.voltageL3, "V"); visible: phaseCount >= 3 }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Current") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (outputLoad.currentL1, "A") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: l2AndL1OutSummed ? "< < <" : EnhFmt.formatVBusItem (outputLoad.currentL2, "A"); visible: phaseCount >= 2 }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: legColumnWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (outputLoad.currentL3, "A"); visible: phaseCount >= 3 }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth; horizontalAlignment: Text.AlignRight
+ text: qsTr("Frequency") }
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: totalDataWidth; horizontalAlignment: Text.AlignHCenter
+ text: EnhFmt.formatVBusItem (outputLoad.frequency, "Hz") }
+ }
+ Row
+ {
+ Text { font.pixelSize: 12; font.bold: true; color: "black"
+ width: rowTitleWidth + totalDataWidth; horizontalAlignment: Text.AlignHCenter
+ text: "L2 values included in L1"
+ visible: l2AndL1OutSummed }
+ }
+ }
+ }
+}
diff --git a/FileSets/v3.60~25/DetailLoadsOnOutput.qml.orig b/FileSets/v3.60~25/DetailLoadsOnOutput.qml.orig
new file mode 100644
index 00000000..6efa32bf
--- /dev/null
+++ b/FileSets/v3.60~25/DetailLoadsOnOutput.qml.orig
@@ -0,0 +1,170 @@
+import QtQuick 2
+import "utils.js" as Utils
+
+Item {
+ id: root
+
+ property string systemPrefix: "com.victronenergy.system"
+ property string vebusPrefix: _vebusService.valid ? _vebusService.value : ""
+
+ property variant battery: _battery
+ property alias dcSystem: _dcSystem
+ property alias pvCharger: _pvCharger
+ property alias pvOnAcIn1: _pvOnAcIn1
+ property alias pvOnAcIn2: _pvOnAcIn2
+ property alias pvOnAcOut: _pvOnAcOut
+ property alias inverterChargerDc: _inverterChargerDc
+ property alias acLoad: _acLoad
+ property alias acInLoad: _acInLoad
+ property alias acOutLoad: _acOutLoad
+ property alias grid: _grid
+ property alias genset: _genset
+ property alias acInput: _activein
+ property VBusItem systemType: VBusItem { bind: Utils.path(systemPrefix, "/SystemType") }
+ property bool hasGridMeter: _hasGridMeter.valid
+ property variant acSource: _acSource.value
+ property VBusItem preferRenewableEnergy: VBusItem { bind: Utils.path(vebusPrefix, "/Dc/0/PreferRenewableEnergy") }
+ property VBusItem remoteGeneratorSelected: VBusItem { bind: Utils.path(vebusPrefix, "/Ac/State/RemoteGeneratorSelected") }
+
+ property alias pvOnGrid: _pvOnAcIn2
+
+ property int batteryStateIdle: 0
+ property int batteryStateCharging: 1
+ property int batteryStateDischarging: 2
+
+ property int acSourceNotAvailable: 0
+ property int acSourceGrid: 1
+ property int acSourceGenset: 2
+ property int acSourceShore: 3 // same as grid
+
+ property alias pvInvertersProductIds: _pvInvertersProductIds
+ property alias batteryProductId: _batteryProductId
+
+ VBusItem {
+ id: _pvInvertersProductIds
+ bind: Utils.path(systemPrefix, "/PvInvertersProductIds")
+ }
+
+ VBusItem {
+ id: _batteryProductId
+ bind: Utils.path(systemPrefix, "/Dc/Battery/ProductId")
+ }
+
+ VBusItem {
+ id: _vebusService
+ bind: Utils.path(systemPrefix, "/VebusService")
+ }
+
+ QtObject {
+ id: _pvCharger
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Pv/Power"); unit: "W"}
+ }
+
+ ObjectAcConnection {
+ id: _pvOnAcOut
+ bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnOutput")
+ }
+
+ ObjectAcConnection {
+ id: _pvOnAcIn1
+ bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGenset")
+ }
+
+ ObjectAcConnection {
+ id: _pvOnAcIn2
+ bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGrid")
+ }
+
+ ObjectAcConnection {
+ id: _genset
+ splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0
+ bindPrefix: Utils.path(systemPrefix, "/Ac/Genset")
+ }
+
+ VBusItem {
+ id: _acSource
+ bind: Utils.path(systemPrefix, "/Ac/ActiveIn/Source")
+ }
+
+ VBusItem {
+ id: _hasGridMeter
+ bind: Utils.path(systemPrefix, "/Ac/Grid/DeviceType")
+ }
+
+ /*
+ * Single Multis that can be split-phase reports NrOfPhases of 2
+ * When L2 is disconnected from the input the output L1 and L2
+ * are shorted. This item indicates if L2 is passed through
+ * from AC-in to AC-out.
+ * 1: L2 is being passed through from AC-in to AC-out.
+ * 0: L1 and L2 are shorted together.
+ * invalid: The unit is configured in such way that its L2 output is not used.
+ */
+
+ VBusItem {
+ id: _splitPhaseL2Passthru
+ bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2Passthru")
+ }
+
+ VBusItem {
+ id: _l2L1OutSummed
+ bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2L1OutSummed")
+ }
+
+
+ ObjectAcConnection {
+ id: _grid
+ splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0
+ bindPrefix: Utils.path(systemPrefix, "/Ac/Grid")
+ }
+
+ ObjectAcConnection {
+ id: _activein
+ bindPrefix: Utils.path(systemPrefix, "/Ac/ActiveIn")
+ }
+
+ ObjectAcConnection {
+ id: _acLoad
+ l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0)
+ isAcOutput: true
+ bindPrefix: Utils.path(systemPrefix, "/Ac/Consumption")
+ }
+
+ ObjectAcConnection {
+ id: _acOutLoad
+ l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0)
+ isAcOutput: true
+ bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnOutput")
+ }
+
+ ObjectAcConnection {
+ id: _acInLoad
+ splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0
+ bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnInput")
+ }
+
+ ObjectAcConnection {
+ id: _acUnknown
+ }
+
+ QtObject {
+ id: _inverterChargerDc
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/InverterCharger/Power"); unit: "W"}
+ }
+
+ QtObject {
+ id: _battery
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Power"); unit: "W"}
+ property VBusItem voltage: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Voltage"); unit: "V"}
+ property VBusItem current: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Current"); unit: "A"}
+ property VBusItem soc: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Soc"); unit: "%"}
+
+ // Get the battery charge state, see batteryState properties
+ property VBusItem state: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/State")}
+ }
+
+ QtObject {
+ id: _dcSystem
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/System/Power"); unit: "W"}
+ }
+}
diff --git a/FileSets/v3.60~25/OverviewAcValuesEnhanced.qml b/FileSets/v3.60~25/OverviewAcValuesEnhanced.qml
new file mode 100644
index 00000000..96882534
--- /dev/null
+++ b/FileSets/v3.60~25/OverviewAcValuesEnhanced.qml
@@ -0,0 +1,94 @@
+////// modified to show voltage, current and frequency in flow overview
+// only displays values for sys.acInput and sys.acLoad
+// because other connections don't have related parameters
+////// modified to show power bar graphs
+
+
+import QtQuick 2
+import "enhancedFormat.js" as EnhFmt
+
+Item {
+ id: root
+ width: parent.width
+ height: parent.height
+
+ // NOTE: data is taken by qml, hence it is called connection
+ property variant connection
+
+ property int phaseCount: root.connection !== undefined && root.connection.phaseCount.valid ? root.connection.phaseCount.value : 0
+
+ Column {
+////// modified to show power bar graphs
+ y: 6
+
+ width: parent.width
+ spacing: 0
+
+ // total power
+ TileText {
+ text: EnhFmt.formatVBusItem (root.connection.power)
+////// modified to show power bar graphs
+ font.pixelSize: 19
+ height: 21
+ }
+
+ // voltage for single leg
+ TileText {
+ text: EnhFmt.formatVBusItem (root.connection.voltageL1, "V")
+ visible: phaseCount <= 1
+ font.pixelSize: 15
+ }
+ // current for single leg
+ TileText {
+ text: EnhFmt.formatVBusItem (root.connection.currentL1, "A")
+ font.pixelSize: 15
+ visible: phaseCount <= 1
+ }
+
+ // power, voltage and current for multiple legs
+ TileText {
+ text: "L1: " + EnhFmt.formatVBusItem (root.connection.powerL1, "W")
+ + " " + EnhFmt.formatVBusItem (root.connection.voltageL1, "V")
+ + " " + EnhFmt.formatVBusItem (root.connection.currentL1, "A")
+ visible: phaseCount >= 2
+ font.pixelSize: 11
+ }
+ TileText {
+ text:
+ {
+ if (root.connection.l2AndL1OutSummed)
+ return "L2 included in L1"
+ else
+ {
+ return "L2:" + EnhFmt.formatVBusItem (root.connection.powerL2, "W")
+ + " " + EnhFmt.formatVBusItem (root.connection.voltageL2, "V")
+ + " " + EnhFmt.formatVBusItem (root.connection.currentL2, "A")
+ }
+ }
+ visible: phaseCount >= 2
+ font.pixelSize: 11
+ }
+ TileText {
+ text:
+ {
+ if (phaseCount >= 3)
+ return "L3: " + EnhFmt.formatVBusItem (root.connection.powerL3, "W")
+ + " " + EnhFmt.formatVBusItem (root.connection.voltageL3, "V")
+ + " " + EnhFmt.formatVBusItem (root.connection.currentL3, "A")
+ else
+ return " "
+ }
+ visible: phaseCount >= 2
+ font.pixelSize: 11
+ }
+ TileText {
+ text: EnhFmt.formatVBusItem (root.connection.frequency, "Hz")
+ font.pixelSize: phaseCount >= 2 ? 11 : 15
+ }
+ TileText {
+ text: qsTr("Limit: ") + EnhFmt.formatVBusItem (root.connection.inCurrentLimit)
+ font.pixelSize: phaseCount >= 2 ? 11 : 15
+ visible: root.connection == sys.acInput
+ }
+ }
+}
diff --git a/FileSets/v3.60~25/OverviewAcValuesEnhanced.qml.orig b/FileSets/v3.60~25/OverviewAcValuesEnhanced.qml.orig
new file mode 100644
index 00000000..ddabc93a
--- /dev/null
+++ b/FileSets/v3.60~25/OverviewAcValuesEnhanced.qml.orig
@@ -0,0 +1,42 @@
+import QtQuick 2
+
+Item {
+ id: root
+ width: parent.width
+
+ // NOTE: data is taken by qml, hence it is called connection
+ property variant connection
+
+ Column {
+ y: 0
+
+ width: parent.width
+ spacing: 0
+
+ TileText {
+ text: root.connection ? root.connection.power.format(0) : ""
+ font.pixelSize: 25
+ height: 27
+ }
+
+ TileText {
+ text: root.connection ? "L1: " + root.connection.powerL1.format(0) : ""
+ visible: root.connection !== undefined && root.connection.phaseCount.valid && root.connection.phaseCount.value > 1 && !root.connection.l2AndL1OutSummed
+ }
+
+ TileText {
+ text: "L1 + L2"
+ visible: root.connection !== undefined && root.connection.phaseCount.valid && root.connection.phaseCount.value > 1 && root.connection.l2AndL1OutSummed
+ }
+
+ TileText {
+ text: root.connection ? "L2: " + (!root.connection.splitPhaseL2PassthruDisabled ? root.connection.powerL2.format(0) : "--") : ""
+ visible: root.connection !== undefined && root.connection.phaseCount.valid && root.connection.phaseCount.value >= 2 && !root.connection.l2AndL1OutSummed
+ }
+
+ TileText {
+ text: root.connection ? "L3: " + root.connection.powerL3.format(0) : ""
+ visible: root.connection !== undefined && root.connection.phaseCount.valid && root.connection.phaseCount.value >= 3
+ }
+ }
+}
diff --git a/FileSets/v3.60~25/OverviewFlowComplex.qml b/FileSets/v3.60~25/OverviewFlowComplex.qml
new file mode 100644
index 00000000..667764ee
--- /dev/null
+++ b/FileSets/v3.60~25/OverviewFlowComplex.qml
@@ -0,0 +1,1549 @@
+///// Enhanced DC Coupled / AC Coupled Overview for GuiMods
+
+import QtQuick 2
+import "utils.js" as Utils
+import com.victron.velib 1.0
+import "timeToGo.js" as TTG
+import "enhancedFormat.js" as EnhFmt
+
+OverviewPage {
+ id: root
+
+////// GuiMods — DarkMode
+ property VBusItem darkModeItem: VBusItem { bind: "com.victronenergy.settings/Settings/GuiMods/DarkMode" }
+ property bool darkMode: darkModeItem.valid && darkModeItem.value == 1
+
+ VBusItem { id: flowOverviewItem; bind: Utils.path(settingsPrefix, "/Settings/GuiMods/FlowOverview") }
+ property bool dcCoupled: flowOverviewItem.valid && flowOverviewItem.value == 2
+
+ VBusItem { id: showInactiveTilesItem; bind: Utils.path(guiModsPrefix, "/ShowInactiveFlowTiles") }
+ property real disabledTileOpacity: (showInactiveTiles && showInactiveTilesItem.value === 1) ? 0.3 : 1
+ property bool showInactiveTiles: showInactiveTilesItem.valid && showInactiveTilesItem.value >= 1
+ property bool showInactiveFlow: showInactiveTilesItem.valid && showInactiveTilesItem.value == 3
+
+ property variant sys: theSystem
+ property string systemPrefix: "com.victronenergy.system"
+ property string settingsPrefix: "com.victronenergy.settings"
+ property color detailColor: "#b3b3b3"
+ property real laneWidth: (root.width - inOutTileWidth * 2 - battery.width) / 3
+
+ property int inOutTileHeight: (root.height - topOffset - bottomOffset - 3 * 5) / 4
+ property int inOutTileWidth: 145
+ VBusItem { id: timeToGo; bind: Utils.path(systemPrefix, "/Dc/Battery/TimeToGo") }
+
+ VBusItem { id: vebusService; bind: Utils.path(systemPrefix, "/VebusService") }
+ property bool isMulti: vebusService.valid
+ property string veDirectInverterService: ""
+ property string inverterService: vebusService.valid ? vebusService.value : veDirectInverterService
+
+ property bool combineAcLoads: dcCoupled || _combineAcLoads.valid && _combineAcLoads.value === 1
+ property variant outputLoad: combineAcLoads ? sys.acLoad : sys.acOutLoad
+
+ // for debug, ignore validity checks so all tiles and their flow lines will show
+ property bool showAllTiles: showInactiveTilesItem.valid && showInactiveTilesItem.value == 3
+
+ property bool hasInverter: false
+ property bool showInverter: hasInverter || inverterService != "" || showAllTiles
+
+ property bool showLoadsOnOutput: showInverter || outputLoad.power.valid
+ property bool showAcInput: isMulti || sys.acInput.power.valid || showAllTiles
+ property bool hasLoadsOnInput: showAcInput && ! combineAcLoads && (! loadsOnInputItem.valid || loadsOnInputItem.value === 1)
+ property bool showLoadsOnInput: !dcCoupled && hasLoadsOnInput
+ property bool hasPvOnInput: sys.pvOnGrid.power.valid
+ property bool showPvOnInput: (!dcCoupled || !hasAcCharger) && hasPvOnInput
+ property bool hasPvOnOutput: sys.pvOnAcOut.power.valid
+ property bool showPvOnOutput: (!dcCoupled || !hasFuelCell) && hasPvOnOutput
+ property bool showPvCharger: sys.pvCharger.power.valid
+ property bool showDcSystem: (hasDcSystemItem.valid && hasDcSystemItem.value > 0) || showAllTiles
+ property bool showAlternator: (dcCoupled || !hasLoadsOnInput) && sys.alternator.power.valid
+ property bool hasFuelCell: sys.fuelCell.power.valid
+ property bool showFuelCell: (dcCoupled || !hasPvOnOutput) && hasFuelCell
+ property bool showWindGen: sys.windGenerator.power.valid
+ property bool hasAcCharger: sys.acCharger != undefined && sys.acCharger.power.valid
+ property bool showAcCharger: (dcCoupled || !hasPvOnInput) && hasAcCharger
+
+ VBusItem { id: motorDrivePowerItem; bind: Utils.path(systemPrefix, "/Dc/MotorDrive/Power") }
+ property bool showMotorDrive: (dcCoupled || !hasLoadsOnInput) && ! showAlternator && motorDrivePowerItem.valid
+
+ property int bottomOffset: showTanksTemps ? 45 : 5
+ property int topOffset: showTanksTemps ? 1 : 5
+ property string settingsBindPreffix: "com.victronenergy.settings"
+ property string pumpBindPreffix: "com.victronenergy.pump.startstop0"
+ property int numberOfTemps: 0
+
+ property int tankCount: showTanksEnable ? tankModel.rowCount : 0
+ property int tempCount: showTempsEnable ? numberOfTemps : 0
+ property int tankTempCount: tankCount + tempCount
+ property bool showTanks: showTanksEnable ? showStatusBar ? false : tankCount > 0 ? true : false : false
+ property bool showTemps: showTempsEnable ? showStatusBar ? false : tempCount > 0 ? true : false : false
+ property bool showTanksTemps: showTanks || showTemps
+ property int compactThreshold: 45 // height below this will be compacted vertically
+ property int batteryHeight: 91
+ property bool compact: showTanks && showTemps && tankTempCount > 4
+ property int tanksHeight: compact ? 22 : 45
+
+ VBusItem { id: ignoreAcInput1; bind: Utils.path(inverterService, "/Ac/State/IgnoreAcIn1") }
+ VBusItem { id: ignoreAcInput2; bind: Utils.path(inverterService, "/Ac/State/IgnoreAcIn2") }
+ VBusItem { id: acActiveInput; bind: Utils.path(inverterService, "/Ac/ActiveIn/ActiveInput") }
+
+ property string guiModsPrefix: "com.victronenergy.settings/Settings/GuiMods"
+ VBusItem { id: showGaugesItem; bind: Utils.path(guiModsPrefix, "/ShowGauges") }
+ property bool showGauges: showGaugesItem.valid ? showGaugesItem.value === 1 ? true : false : false
+ VBusItem { id: showTanksItem; bind: Utils.path(guiModsPrefix, "/ShowEnhancedFlowOverviewTanks") }
+ property bool showTanksEnable: showTanksItem.valid ? showTanksItem.value === 1 ? true : false : false
+ VBusItem { id: showTempsItem; bind: Utils.path(guiModsPrefix, "/ShowEnhancedFlowOverviewTemps") }
+ property bool showTempsEnable: showTempsItem.valid ? showTempsItem.value === 1 ? true : false : false
+
+ VBusItem { id: hasDcSystemItem; bind: "com.victronenergy.settings/Settings/SystemSetup/HasDcSystem" }
+
+ VBusItem { id: timeFormatItem; bind: Utils.path(guiModsPrefix, "/TimeFormat") }
+ property string timeFormat: getTimeFormat ()
+
+ property double acInputFlow: showAcInput ? noNoise (sys.acInput.power) : 0
+ property VBusItem vebusAcPower: VBusItem { bind: [sys.vebusPrefix, "/Ac/ActiveIn/P"] }
+ property double multiAcInputFlow: isMulti ? -noNoise (vebusAcPower) : 0
+ property double pvOnInputFlow: showPvOnInput ? noNoise (sys.pvOnGrid.power) : 0
+ property double loadsOnInputFlow: sys.acInLoad.power.valid ? -noNoise (sys.acInLoad.power) : 0
+ property double pvInverterOnAcOutFlow: showPvOnOutput && sys.pvOnAcOut.power.valid ? noNoise (sys.pvOnAcOut.power) : 0
+ property double acOutLoadFlow: sys.acOutLoad.power.valid ? -noNoise (sys.acOutLoad.power) : 0
+
+ property double pvChargerFlow: showPvCharger ? noNoise (sys.pvCharger.power) : 0
+ property double dcSystemFlow: showDcSystem ? -noNoise (sys.dcSystem.power) : 0
+ property double alternatorFlow: showAlternator ? noNoise (sys.alternator.power) : 0
+ property double motorDriveFlow: showMotorDrive ? noNoise (motorDrivePowerItem) : 0
+ property double inverterDcFlow: showInverter ? noNoise (sys.inverterChargerDc.power) : 0
+ property double batteryFlow: noNoise (sys.battery.power)
+ property double windGenFlow: noNoise (sys.windGenerator.power)
+ property double acChargerFlow: noNoise (sys.acCharger.power)
+ property double fuelCellFlow: noNoise (sys.fuelCell.power)
+
+ VBusItem { id: showBatteryTempItem; bind: Utils.path(guiModsPrefix, "/ShowBatteryTempOnFlows") }
+ property bool showBatteryTemp: showBatteryTempItem.valid && showBatteryTempItem.value == 1
+
+
+ function getTimeFormat ()
+ {
+ if (!timeFormatItem.valid || timeFormatItem.value === 0)
+ return ""
+ else if (timeFormatItem.value === 2)
+ return "h:mm ap"
+ else
+ return "hh:mm"
+ }
+
+ //Component.onCompleted: { discoverServices(); showHelp () }
+ onActiveChanged:
+ {
+ if (root.active)
+ {
+ discoverServices()
+ showHelp ()
+ }
+ }
+
+ title: dcCoupled ? qsTr("DC Coupled overview") : qsTr("AC Coupled overview")
+
+ VBusItem { id: loadsOnInputItem; bind: "com.victronenergy.settings/Settings/GuiMods/ShowEnhancedFlowLoadsOnInput" }
+ VBusItem { id: _combineAcLoads; bind: "com.victronenergy.settings/Settings/GuiMods/EnhancedFlowCombineLoads" }
+
+ OverviewBox {
+ id: acInBox
+ opacity: showAcInput ? 1 : disabledTileOpacity
+ visible: showAcInput || showInactiveTiles
+ width: inOutTileWidth
+ height: inOutTileHeight
+ title:
+ {
+ // input 1 is active
+ if (acActiveInput.value == 0)
+ {
+ if (ignoreAcInput1.valid && ignoreAcInput1.value == 1)
+ return qsTr ("AC In 1 Ignored")
+ else
+ return getAcSourceName(sys.acSource)
+ }
+ // input 2 is active
+ else if (acActiveInput.value == 1)
+ {
+ if (ignoreAcInput2.valid && ignoreAcInput2.value == 1)
+ return qsTr ("AC In 2 Ignored")
+ else
+ return getAcSourceName(sys.acSource)
+ }
+ else
+ return "no input"
+ }
+////// GuiMods — DarkMode
+ titleColor: !darkMode ? "#E74c3c" : "#73261E"
+ color: !darkMode ? "#C0392B" : "#601C15"
+ anchors {
+ top: root.top; topMargin: topOffset
+ left: parent.left; leftMargin: 5
+ }
+ values: TileText {
+ y: 13
+ text: EnhFmt.formatVBusItem (sys.acInput.power)
+ font.pixelSize: 17
+ visible: showAcInput
+ }
+
+ MbIcon {
+ iconId: getAcSourceIcon(sys.acSource)
+ anchors {
+ bottom: parent.bottom
+ left: parent.left; leftMargin: 2
+ }
+ opacity: 0.5
+ }
+ PowerGauge
+ {
+ id: acInGauge
+ width: parent.width
+ height: 15
+ anchors
+ {
+ top: parent.top; topMargin: 18
+ horizontalCenter: parent.horizontalCenter
+ }
+ connection: sys.acInput
+ useInputCurrentLimit: true
+ maxForwardPowerParameter: ""
+ maxReversePowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/MaxFeedInPower"
+ visible: showGauges && showAcInput
+ }
+ DetailTarget { id: acInputTarget; detailsPage: "DetailAcInput.qml" }
+ }
+
+ OverviewBox
+ {
+ id: pvInverterOnInput
+////// GuiMods — DarkMode
+ titleColor: !darkMode ? "#F4B350" : "#7A5928"
+ color: !darkMode ? "#F39C12" : "#794E09"
+ title: qsTr("PV on Input")
+ width: inOutTileWidth
+ height: inOutTileHeight
+ visible: showPvOnInput || (showInactiveTiles && !dcCoupled)
+ opacity: showPvOnInput ? 1 : disabledTileOpacity
+ MbIcon
+ {
+ source:
+ {
+ var ids = sys.pvInvertersProductIds.text
+ if (ids.indexOf(0xA142) > -1)
+ return "image://theme/overview-fronius-logo"
+ return ""
+ }
+ visible: showPvOnInput
+ opacity: 0.3
+ anchors {
+ bottom: parent.bottom
+ left: parent.left
+ margins: 2
+ }
+ }
+ values: TileText {
+ y: 11
+ text: EnhFmt.formatVBusItem (sys.pvOnGrid.power)
+ font.pixelSize: 17
+ visible: showPvOnInput
+ }
+ anchors {
+ top: acInBox.bottom
+ topMargin: 5
+ left: acInBox.left
+ }
+ PowerGauge
+ {
+ id: pvInverterOnInputGauge
+ width: parent.width
+ height: 15
+ anchors
+ {
+ top: parent.top; topMargin: 18
+ horizontalCenter: parent.horizontalCenter
+ }
+ connection: sys.pvOnGrid
+ maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/PvOnGridMaxPower"
+ visible: showGauges && showPvOnInput
+ }
+ DetailTarget { id: pvOnInputTarget; detailsPage: "DetailPvInverter.qml" }
+ }
+
+ OverviewBox {
+ id: acLoadOnInputBox
+ title: qsTr("AC In Loads")
+////// GuiMods — DarkMode
+ color: !darkMode ? "#27AE60" : "#135730"
+ titleColor: !darkMode ? "#2ECC71" : "#176638"
+ width: inOutTileWidth
+ height: inOutTileHeight
+ opacity: showLoadsOnInput ? 1 : disabledTileOpacity
+ visible: showLoadsOnInput || (showInactiveTiles && !dcCoupled)
+ anchors {
+ top: pvInverterOnInput.bottom
+ topMargin: 5
+ left: acInBox.left
+ }
+ values: TileText {
+ y: 13
+ text: EnhFmt.formatVBusItem (sys.acInLoad.power)
+ font.pixelSize: 17
+ visible: showLoadsOnInput
+ }
+ PowerGauge
+ {
+ id: acInLoadGauge
+ width: parent.width
+ height: 15
+ anchors
+ {
+ top: parent.top; topMargin: 18
+ horizontalCenter: parent.horizontalCenter
+ }
+ connection: sys.acInLoad
+ maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/AcOutputNonCriticalMaxPower"
+ visible: showGauges && showLoadsOnInput
+ }
+ DetailTarget { id: acLoadsOnInputTarget; detailsPage: "DetailLoadsOnInput.qml" }
+ }
+
+ // check inverter to see if AC out 2 exists and hide noncritical loads if so
+ VBusItem { id: inverterOut2Item; bind: Utils.path(root.inverterService, "/Ac/Out/L2/V") }
+
+ OverviewBox {
+ id: acOutputBox
+ title: combineAcLoads ? qsTr ("AC Loads") : qsTr ("AC Out Loads")
+////// GuiMods — DarkMode
+ color: !darkMode ? "#27AE60" : "#135730"
+ titleColor: !darkMode ? "#2ECC71" : "#176638"
+ height: inOutTileHeight
+ width: inOutTileWidth
+ opacity: showLoadsOnOutput ? 1 : disabledTileOpacity
+ visible: showLoadsOnOutput || showInactiveTiles
+ anchors {
+ right: root.right; rightMargin: 5
+ top: root.top; topMargin: topOffset
+ }
+
+ values: TileText {
+ y: 13
+ text: EnhFmt.formatVBusItem (outputLoad.power)
+ font.pixelSize: 17
+ visible: showLoadsOnOutput
+ }
+ PowerGauge
+ {
+ id: acOutLoadGauge
+ width: parent.width
+ height: 15
+ anchors
+ {
+ top: parent.top; topMargin: 18
+ horizontalCenter: parent.horizontalCenter
+ }
+ connection: outputLoad
+ maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/AcOutputMaxPower"
+ visible: showGauges && showLoadsOnOutput
+ }
+ DetailTarget { id: acLoadsOnOutputTarget; detailsPage: "DetailLoadsOnOutput.qml" }
+ }
+ Timer {
+ id: wallClock
+ running: timeFormat != ""
+ repeat: true
+
+ interval: 1000
+ triggeredOnStart: true
+ onTriggered: time = Qt.formatDateTime(new Date(), timeFormat)
+
+ property string time
+ }
+
+ MultiEnhancedGP {
+ id: multi
+ iconId: "overview-inverter-short"
+ opacity: showInverter ? 1 : disabledTileOpacity
+ visible: showInverter || showInactiveTiles
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ top: acInBox.top
+ }
+ inverterService: root.inverterService
+ PowerGaugeMulti
+ {
+ id: multiGauge
+ width: multi.width
+ height: 13
+ anchors
+ {
+ top: parent.top; topMargin: 21
+ horizontalCenter: multi.horizontalCenter
+ }
+ inverterService: root.inverterService
+ visible: showGauges && showInverter
+ }
+ DetailTarget { id: multiTarget; detailsPage: "DetailInverter.qml"; width: 60; height: 60 }
+ }
+ TileText
+ {
+ text: wallClock.time
+ color: showInverter || darkMode ? "white" : "black"
+ width: inOutTileWidth
+ wrapMode: Text.WordWrap
+ font.pixelSize: 16
+ anchors
+ {
+ bottom: multi.bottom; bottomMargin: 1
+ horizontalCenter: multi.horizontalCenter;
+ horizontalCenterOffset: multiDcConnector.active ? -10 : 0
+ }
+ visible: wallClock.running
+ }
+
+ Battery {
+ id: battery
+ width: 145
+ height: 96
+ anchors {
+ bottom: parent.bottom; bottomMargin: bottomOffset;
+ right: acOutputBox.left; rightMargin: laneWidth
+ }
+ soc: sys.battery.soc.valid ? sys.battery.soc.value : 0
+////// add battery current bar graph
+ PowerGaugeBattery
+ {
+ id: batteryBar
+ width: parent.width
+ height: 10
+ anchors
+ {
+ top: parent.top; topMargin: 52
+ horizontalCenter: parent.horizontalCenter
+ }
+ visible: showGauges
+ }
+ values: Column {
+ width: parent.width
+
+ TileText {
+ text: sys.battery.soc.value === undefined ? "--" : sys.battery.soc.format (0)
+ font.pixelSize: 25
+ }
+ TileText {
+ text: EnhFmt.formatVBusItem (sys.battery.power, "W")
+ }
+ TileText {
+ text: " "
+ font.pixelSize: 6
+ }
+ TileText {
+ text: EnhFmt.formatVBusItem (sys.battery.voltage, "V ", 2)
+ + EnhFmt.formatVBusItem (sys.battery.current, "A")
+ }
+ TileText {
+ text: timeToGo.valid ? qsTr ("Remain: ") + TTG.formatTimeToGo (timeToGo) : qsTr (" ")
+ }
+ }
+ DetailTarget { id: batteryTarget; detailsPage: "DetailBattery.qml" }
+ }
+
+ OverviewBox
+ {
+ id: pvInverterOnAcOut
+////// GuiMods — DarkMode
+ titleColor: !darkMode ? "#F4B350" : "#7A5928"
+ color: !darkMode ? "#F39C12" : "#794E09"
+ title: qsTr("PV on Output")
+ width: inOutTileWidth
+ height: inOutTileHeight
+ opacity: showPvOnOutput ? 1 : disabledTileOpacity
+ visible: showPvOnOutput || (showInactiveTiles && !dcCoupled)
+ MbIcon
+ {
+ source:
+ {
+ var ids = sys.pvInvertersProductIds.text
+ if (ids.indexOf(0xA142) > -1)
+ return "image://theme/overview-fronius-logo"
+ return ""
+ }
+ visible: showPvOnOutput
+ opacity: 0.3
+ anchors {
+ bottom: parent.bottom
+ right: parent.right
+ margins: 2
+ }
+ }
+
+ values: TileText {
+ y: 11
+ text: EnhFmt.formatVBusItem (sys.pvOnAcOut.power)
+ font.pixelSize: 17
+ visible: showPvOnOutput
+ }
+ anchors {
+ top: acOutputBox.bottom
+ topMargin: 5
+ right: acOutputBox.right
+ }
+ PowerGauge
+ {
+ id: pvInverterOnAcOutGauge
+ width: parent.width
+ height: 15
+ anchors
+ {
+ top: parent.top; topMargin: 18
+ horizontalCenter: parent.horizontalCenter
+ }
+ connection: sys.pvOnAcOut
+ maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/PvOnOutputMaxPower"
+ visible: showGauges && showPvOnOutput
+ }
+ DetailTarget { id: pvOnOutputTarget; detailsPage: "DetailPvInverter.qml" }
+ }
+
+ OverviewBox
+ {
+ id: acChargerBox
+ title: qsTr ("AC Charger")
+////// GuiMods — DarkMode
+ color: !darkMode ? "#157894" : "#0a3c4a"
+ titleColor: !darkMode ? "#419FB9" : "#204f5c"
+ height: inOutTileHeight
+ width: inOutTileWidth
+ opacity: showAcCharger ? 1 : disabledTileOpacity
+ visible: showAcCharger || (showInactiveTiles && dcCoupled)
+ anchors
+ {
+ left: root.left; leftMargin: 5
+ bottom: alternatorBox.top; bottomMargin: 5
+ }
+ values: TileText {
+ text: EnhFmt.formatVBusItem (sys.acCharger.power)
+ font.pixelSize: 17
+ visible: showAcCharger
+ anchors
+ {
+ bottom: parent.bottom; bottomMargin: 0
+ horizontalCenter: parent.horizontalCenter
+ }
+ }
+ PowerGauge
+ {
+ id: acChargerGauge
+ width: parent.width
+ height: 10
+ anchors
+ {
+ top: parent.top; topMargin: 20
+ horizontalCenter: parent.horizontalCenter
+ }
+ connection: sys.acCharger
+ reversePower: true
+ maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/MaxAcChargerPower"
+ visible: showGauges && showAcCharger
+ }
+ DetailTarget { id: acChargerTarget; detailsPage: "DetailAcCharger.qml" }
+ }
+
+ OverviewBox
+ {
+ id: alternatorBox
+ title: qsTr ("Alternator")
+////// GuiMods — DarkMode
+ color: !darkMode ? "#157894" : "#0a3c4a"
+ titleColor: !darkMode ? "#419FB9" : "#204f5c"
+ height: inOutTileHeight
+ width: inOutTileWidth
+ opacity: showAlternator ? 1 : disabledTileOpacity
+ visible: showAlternator || (showInactiveTiles && dcCoupled)
+ anchors
+ {
+ left: root.left; leftMargin: 5
+ bottom: pvChargerBox.top; bottomMargin: 5
+ }
+ values: TileText {
+ text: EnhFmt.formatVBusItem (sys.alternator.power)
+ font.pixelSize: 17
+ visible: showAlternator
+ anchors
+ {
+ bottom: parent.bottom; bottomMargin: 0
+ horizontalCenter: parent.horizontalCenter
+ }
+ }
+ PowerGauge
+ {
+ id: alternatorGauge
+ width: parent.width
+ height: 10
+ anchors
+ {
+ top: parent.top; topMargin: 20
+ horizontalCenter: parent.horizontalCenter
+ }
+ connection: sys.alternator
+ maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/MaxAlternatorPower"
+ visible: showGauges && showAlternator
+ }
+ DetailTarget { id: alternatorTarget; detailsPage: "DetailAlternator.qml" }
+ }
+
+ OverviewBox
+ {
+ id: motorDriveBox
+ title: qsTr ("Motor Drive")
+////// GuiMods — DarkMode
+ color: !darkMode ? "#157894" : "#0a3c4a"
+ titleColor: !darkMode ? "#419FB9" : "#204f5c"
+ height: inOutTileHeight
+ width: inOutTileWidth
+ opacity: showMotorDrive ? 1 : disabledTileOpacity
+ visible: showMotorDrive
+ anchors
+ {
+ left: root.left; leftMargin: 5
+ bottom: pvChargerBox.top; bottomMargin: 5
+ }
+ values: TileText {
+ text: EnhFmt.formatVBusItem (motorDrivePowerItem)
+ font.pixelSize: 17
+ visible: showMotorDrive
+ anchors
+ {
+ bottom: parent.bottom; bottomMargin: 0
+ horizontalCenter: parent.horizontalCenter
+ }
+ }
+ PowerGauge
+ {
+ id: motorDriveGauge
+ width: parent.width
+ height: 10
+ anchors
+ {
+ top: parent.top; topMargin: 20
+ horizontalCenter: parent.horizontalCenter
+ }
+ connection: motorDrivePowerItem
+ maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/MaxMotorDriveLoad"
+ maxReversePowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/MaxMotorDriveCharge"
+ visible: showGauges && showMotorDrive
+ showLabels: true
+ }
+ DetailTarget { id: motorDriveTarget; detailsPage: "DetailMotorDrive.qml" }
+ }
+
+ VBusItem { id: dcSystemNameItem; bind: Utils.path(settingsPrefix, "/Settings/GuiMods/CustomDcSystemName") }
+
+ OverviewBox {
+ id: dcSystemBox
+ width: inOutTileWidth
+ height: inOutTileHeight
+ opacity: showDcSystem ? 1 : disabledTileOpacity
+ visible: showDcSystem || showInactiveTiles
+ title: dcSystemNameItem.valid && dcSystemNameItem.value != "" ? dcSystemNameItem.value : qsTr ("DC System")
+ anchors
+ {
+ right: root.right; rightMargin: 5
+ bottom: parent.bottom
+ bottomMargin: bottomOffset
+ }
+ values: TileText {
+ text: EnhFmt.formatVBusItem (sys.dcSystem.power)
+ font.pixelSize: 17
+ visible: showDcSystem
+ anchors
+ {
+ bottom: parent.bottom; bottomMargin: 0
+ horizontalCenter: parent.horizontalCenter
+ }
+ }
+ PowerGauge
+ {
+ id: dcSystemGauge
+ width: parent.width
+ height: 10
+ anchors
+ {
+ top: parent.top; topMargin: 20
+ horizontalCenter: parent.horizontalCenter
+ }
+ connection: sys.dcSystem
+ maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/DcSystemMaxLoad"
+ maxReversePowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/DcSystemMaxCharge"
+ showLabels: true
+ visible: showGauges && showDcSystem
+ }
+ DetailTarget { id: dcSystemTarget; detailsPage: "DetailDcSystem.qml" }
+ }
+
+ OverviewBox {
+ id: fuelCellBox
+////// GuiMods — DarkMode
+ color: !darkMode ? "#157894" : "#0a3c4a"
+ titleColor: !darkMode ? "#419FB9" : "#204f5c"
+ width: inOutTileWidth
+ height: inOutTileHeight
+ opacity: showFuelCell ? 1 : disabledTileOpacity
+ visible: showFuelCell || (showInactiveTiles && dcCoupled)
+ title: qsTr ("Fuel Cell")
+ anchors {
+ left: windGenBox.left
+ bottom: windGenBox.top; bottomMargin: 5
+ }
+ values: TileText {
+ text: EnhFmt.formatVBusItem (sys.fuelCell.power)
+ font.pixelSize: 17
+ visible: fuelCellBox.visible
+ anchors
+ {
+ bottom: parent.bottom; bottomMargin: 0
+ horizontalCenter: parent.horizontalCenter
+ }
+ }
+ PowerGauge
+ {
+ id: fuelCellGauge
+ width: parent.width
+ height: 10
+ anchors
+ {
+ top: parent.top; topMargin: 20
+ horizontalCenter: parent.horizontalCenter
+ }
+ connection: sys.fuelCell
+ maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/MaxFuelCellPower"
+ visible: showGauges && fuelCellBox.visible
+ }
+ DetailTarget { id: fuelCellTarget; detailsPage: "DetailFuelCell.qml" }
+ }
+
+ OverviewBox {
+ id: windGenBox
+////// GuiMods — DarkMode
+ color: !darkMode ? "#157894" : "#0a3c4a"
+ titleColor: !darkMode ? "#419FB9" : "#204f5c"
+ width: inOutTileWidth
+ height: inOutTileHeight
+ opacity: showWindGen ? 1 : disabledTileOpacity
+ visible: showWindGen || showInactiveTiles
+ title: qsTr ("Wind Generator")
+ anchors
+ {
+ right: dcSystemBox.right
+ bottom: dcSystemBox.top; bottomMargin: 5
+ }
+ values: TileText {
+ text: EnhFmt.formatVBusItem (sys.windGenerator.power)
+ font.pixelSize: 17
+ visible: showWindGen
+ anchors
+ {
+ bottom: parent.bottom; bottomMargin: 0
+ horizontalCenter: parent.horizontalCenter
+ }
+ }
+ PowerGauge
+ {
+ id: windGenGauge
+ width: parent.width
+ height: 10
+ anchors
+ {
+ top: parent.top; topMargin: 20
+ horizontalCenter: parent.horizontalCenter
+ }
+ connection: sys.windGenerator
+ maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/MaxWindGenPower"
+ visible: showGauges && showWindGen
+ }
+ DetailTarget { id: windGenTarget; detailsPage: "DetailWindGen.qml" }
+ }
+
+ OverviewBox {
+ id: pvChargerBox
+ title: qsTr("PV Charger")
+////// GuiMods — DarkMode
+ titleColor: !darkMode ? "#F4B350" : "#7A5928"
+ color: !darkMode ? "#F39C12" : "#794E09"
+ width: inOutTileWidth
+ height: inOutTileHeight
+ opacity: showPvCharger ? 1 : disabledTileOpacity
+ visible: showPvCharger || showInactiveTiles
+ anchors
+ {
+ left: root.left; leftMargin: 5
+ bottom: parent.bottom; bottomMargin: bottomOffset
+ }
+ values: TileText {
+ y: 12
+ text: EnhFmt.formatVBusItem (sys.pvCharger.power)
+ font.pixelSize: 17
+ }
+ // moved sun icon here from OverviewSolarChager so it can be put below text, etc
+ MbIcon {
+ iconId: "overview-sun"
+ anchors {
+ bottom: parent.bottom
+ right: parent.right; rightMargin: 2
+ }
+ opacity: 0.5
+ }
+
+ PowerGauge
+ {
+ id: pvChargerGauge
+ width: parent.width
+ height: 10
+ anchors
+ {
+ top: parent.top; topMargin: 20
+ horizontalCenter: parent.horizontalCenter
+ }
+ connection: sys.pvCharger
+ maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/PvChargerMaxPower"
+ visible: showGauges && showPvCharger
+ }
+ DetailTarget { id: pvChargerTarget; detailsPage: "DetailPvCharger.qml" }
+ }
+
+ // move ESS reason to Battery details page
+
+ // invisible item to connection all AC input connections to..
+ Item {
+ id: acInBus
+ width: laneWidth
+ anchors {
+ left: acInBox.right;
+ top: multi.top; topMargin: multi.height / 2 + 10
+ bottom: pvInverterOnInput.bottom; bottomMargin: 8
+ }
+ }
+ Item {
+ id: dcLaneLeft
+ width: laneWidth
+ anchors {
+ right: battery.left;
+ top: multi.top; topMargin: multi.height / 2 + 10
+ bottom: dcSystemBox.bottom; bottomMargin: 8
+ }
+ }
+ Item {
+ id: dcLaneRight
+ width: laneWidth * 0.8
+ anchors {
+ left: battery.right;
+
+ top: dcLaneLeft.top
+ bottom: dcLaneLeft.bottom
+ }
+ }
+ Item {
+ id: dcLaneTop
+ anchors {
+ left: battery.left
+ right: battery.right
+ top: multi.bottom;
+ bottom: battery.top
+ }
+ }
+
+ OverviewConnection {
+ id: multiAcInFlow
+ ballCount: 1
+ path: straight
+ active: root.active && ( showAcInput || showPvOnInput || showLoadsOnInput )
+ value: -Utils.sign (multiAcInputFlow)
+ startPointVisible: false
+ endPointVisible: true
+
+ anchors {
+ left: acInBus.horizontalCenter; leftMargin: -0.5
+ right: multi.left; rightMargin: -8
+ bottom: acInBus.top
+ }
+ }
+
+ // AC source power flow
+ OverviewConnection {
+ id: acSource
+ ballCount: 1
+ path: corner
+ active: root.active && showAcInput
+ value: Utils.sign (acInputFlow)
+ startPointVisible: true
+ endPointVisible: false
+
+ anchors {
+ left: acInBox.right; leftMargin: -8
+ right: acInBus.horizontalCenter
+ top: acInBox.bottom; topMargin: -8
+ bottom: acInBus.top
+ }
+ }
+
+ // Coupled AC sources
+ OverviewConnection {
+ id: coupledAcConnection
+ ballCount: 1
+ path: straight
+ active: root.active && ((showLoadsOnInput && showPvOnInput) || (!dcCoupled && showInactiveFlow))
+ value: -Utils.sign (pvOnInputFlow + loadsOnInputFlow)
+ startPointVisible: false
+ endPointVisible: false
+
+ anchors {
+ right: acInBus.horizontalCenter
+ rightMargin: 0.5 // makes this line up with others
+ top: acInBus.top
+ bottom: acInBus.bottom
+ }
+ }
+
+ // Grid inverter power flow
+ OverviewConnection {
+ id: pvInverterOnInputConnection
+ ballCount: showLoadsOnInput ? 1 : 2
+ path: showLoadsOnInput || (!dcCoupled && showInactiveFlow) ? straight : corner
+ active: root.active && (showPvOnInput || (!dcCoupled && showInactiveFlow))
+ value: Utils.sign (pvOnInputFlow)
+ startPointVisible: true
+ endPointVisible: false
+
+ anchors {
+ left: pvInverterOnInput.right; leftMargin: -8
+ right: acInBus.horizontalCenter
+ top: pvInverterOnInput.bottom; topMargin: -8
+ bottom: showLoadsOnInput || (!dcCoupled && showInactiveFlow) ? pvInverterOnInput.bottom : multiAcInFlow.verticalCenter
+ bottomMargin: showLoadsOnInput || (!dcCoupled && showInactiveFlow) ? 8 : 0
+ }
+ }
+
+ // power to loads on input
+ OverviewConnection {
+ id: loadsOnInput
+ ballCount: 1
+ path: corner
+ active: root.active && (showLoadsOnInput || (!dcCoupled && showInactiveFlow))
+ value: Utils.sign (loadsOnInputFlow)
+ startPointVisible: true
+ endPointVisible: false
+
+ anchors {
+ left: acLoadOnInputBox.right; leftMargin: -8
+ right: acInBus.horizontalCenter
+ rightMargin: 0.5 // makes this line up with others
+ top: acLoadOnInputBox.top; topMargin: 8
+ bottom: showPvOnInput|| (!dcCoupled && showInactiveFlow) ? acInBus.bottom : acInBus.top
+ }
+ }
+
+ // invisible item to connection all AC output connections to..
+ Item {
+ id: acOutNode
+ height: 6
+ anchors {
+ left: multi.right
+ right: acOutputBox.left
+ verticalCenter: acInBus.top
+ }
+ }
+
+ // AC out connection
+ OverviewConnection {
+ id: multiAcOutConnection
+
+ ballCount: 1
+ path: straight
+ active: root.active && ((showLoadsOnOutput || showPvOnOutput) || (!dcCoupled && showInactiveFlow))
+ value: -Utils.sign (acOutLoadFlow + pvInverterOnAcOutFlow)
+ endPointVisible: false
+
+ anchors {
+ left: multi.right; leftMargin: -8
+ right: acOutNode.horizontalCenter
+ top: acOutNode.verticalCenter
+ }
+ }
+
+ // loads on output conenction
+ OverviewConnection {
+ id: acOutBoxConnection
+
+ ballCount: 1
+ path: corner
+ active: root.active && (showLoadsOnOutput || (!dcCoupled && showInactiveFlow))
+ value: Utils.sign (acOutLoadFlow)
+ startPointVisible: true
+ endPointVisible: false
+
+ anchors {
+ right: acOutNode.horizontalCenter
+ rightMargin: -0.5 // makes this line up with others
+ left: acOutputBox.left; leftMargin: 8
+ top: acOutputBox.bottom; topMargin: -8
+ bottom: acOutNode.verticalCenter
+ }
+ }
+
+ // PV Inverter on AC out connection
+ OverviewConnection {
+ id: pvOnAcOutConnection
+
+ ballCount: 2
+ path: corner
+ active: root.active && (showPvOnOutput || (!dcCoupled && showInactiveFlow))
+ value: Utils.sign (pvInverterOnAcOutFlow)
+ startPointVisible: true
+ endPointVisible: false
+
+ anchors {
+ left: pvInverterOnAcOut.left; leftMargin: 8
+ top: pvInverterOnAcOut.bottom; topMargin: -8
+ right: acOutNode.horizontalCenter
+ bottom: acOutNode.verticalCenter
+ }
+ }
+
+ // invisible summing point for all DC connections
+ Item {
+ id: dcNode
+ height: 10
+ width: 10
+ anchors {
+ horizontalCenter: batteryDcConnector.horizontalCenter
+ verticalCenter: dcLaneTop.verticalCenter
+ }
+ }
+
+ // DC bus segments
+ OverviewConnection {
+ id: dcBus1
+ ballCount: 1
+ path: straight
+ active: root.active && ((showAlternator || showMotorDrive || showPvCharger) || (dcCoupled && showInactiveFlow))
+ value: -Utils.sign (alternatorFlow + motorDriveFlow + pvChargerFlow)
+ startPointVisible: false
+ endPointVisible: false
+
+ anchors {
+ right: dcLaneLeft.horizontalCenter
+ rightMargin: 0.5 // makes this line up with others
+ bottom: alternatorConnection.verticalCenter
+ top: dcLaneTop.verticalCenter
+ }
+ }
+ OverviewConnection {
+ id: dcBus2
+ ballCount: 1
+ path: straight
+ active: root.active && ((showAlternator || showMotorDrive || showAcCharger || showPvCharger) || (dcCoupled && showInactiveFlow))
+ value: Utils.sign (alternatorFlow + motorDriveFlow + pvChargerFlow + acChargerFlow)
+ startPointVisible: false
+ endPointVisible: false
+
+ anchors {
+ left: dcLaneLeft.horizontalCenter
+ right: dcBus3.left
+ bottom: dcLaneTop.verticalCenter
+ }
+ }
+ OverviewConnection {
+ id: dcBus3
+ ballCount: 2
+ path: straight
+ active: root.active && ((showInverter || showFuelCell || showWindGen || showDcSystem) || showInactiveFlow)
+ value: -Utils.sign (inverterDcFlow + fuelCellFlow + windGenFlow + dcSystemFlow)
+ startPointVisible: false
+ endPointVisible: false
+
+ anchors {
+ left: batteryDcConnector.horizontalCenter
+ right: multiDcConnector.horizontalCenter
+ bottom: dcLaneTop.verticalCenter
+ }
+ }
+ OverviewConnection {
+ id: dcBus4
+ ballCount: 1
+ path: straight
+ active: root.active && ((showFuelCell || showWindGen || showDcSystem) || showInactiveFlow)
+ value: -Utils.sign (fuelCellFlow + windGenFlow + dcSystemFlow)
+ startPointVisible: false
+ endPointVisible: false
+
+ anchors {
+ left: multiDcConnector.horizontalCenter
+ right: dcLaneRight.horizontalCenter
+ bottom: dcLaneTop.verticalCenter
+ }
+ }
+ OverviewConnection {
+ id: dcBus5
+ ballCount: 1
+ path: straight
+ active: root.active && ((showWindGen || showDcSystem) || showInactiveFlow)
+ value: -Utils.sign (windGenFlow + dcSystemFlow)
+ startPointVisible: false
+ endPointVisible: false
+
+ anchors {
+ left: dcLaneRight.horizontalCenter
+ top: dcLaneTop.verticalCenter
+ bottom: windGenConnection.verticalCenter
+ }
+ }
+
+
+ // DC connection multi to bus
+ OverviewConnection {
+ id: multiDcConnector
+ ballCount: 1
+ path: straight
+ active: root.active && (showInverter || showInactiveFlow)
+ value: Utils.sign (inverterDcFlow)
+ startPointVisible: true
+ endPointVisible: false
+
+ anchors {
+ right: multi.right; rightMargin: 25
+ top: multi.bottom; topMargin: -8
+ bottom: dcLaneTop.verticalCenter
+ }
+ }
+ // DC connection battery to bus
+ OverviewConnection {
+ id: batteryDcConnector
+ ballCount: 1
+ path: straight
+ active: root.active && ((sys.battery.soc.valid || showDcSystem) || (dcCoupled && showInactiveFlow))
+ value: -Utils.sign (batteryFlow)
+ startPointVisible: true
+ endPointVisible: false
+
+ anchors {
+ left: battery.left; leftMargin: 30
+ top: battery.top; topMargin: 15
+ bottom: dcLaneTop.verticalCenter
+ }
+ }
+
+ // AC charger to DC bus
+ OverviewConnection
+ {
+ id: acChargerConnection
+ ballCount: 1
+ path: corner
+ active: root.active && (showAcCharger || (dcCoupled && showInactiveFlow))
+ value: Utils.sign (acChargerFlow)
+ startPointVisible: true
+ endPointVisible: false
+ anchors
+ {
+ left: acChargerBox.right; leftMargin: -8
+ top: acChargerBox.bottom; topMargin: -8
+ right: dcLaneLeft.horizontalCenter
+ bottom: dcLaneTop.verticalCenter
+ }
+ }
+
+ // Alternator to bus
+ OverviewConnection
+ {
+ id: alternatorConnection
+ ballCount: 1
+ path: straight
+ active: root.active && (showAlternator || showMotorDrive || (dcCoupled && showInactiveFlow))
+ value: Utils.sign (alternatorFlow + motorDriveFlow)
+ startPointVisible: true
+ endPointVisible: false
+ anchors
+ {
+ left: alternatorBox.right; leftMargin: -8
+ top: alternatorBox.bottom; topMargin: -8
+ right: dcLaneLeft.horizontalCenter
+ }
+ }
+
+ // DC system to DC bus
+ OverviewConnection
+ {
+ id: dcSystemConnection
+ ballCount: 2
+ path: corner
+ active: root.active && (showDcSystem || (dcCoupled && showInactiveFlow))
+ value: Utils.sign (dcSystemFlow)
+ endPointVisible: false
+ anchors
+ {
+ left: dcSystemBox.left; leftMargin: 8
+ top: dcSystemBox.bottom; topMargin: -8
+ right: dcLaneRight.horizontalCenter
+ rightMargin: -0.5 // makes this line up with others
+ bottom: windGenConnection.verticalCenter
+ }
+ }
+
+
+ // other DC connection to DC right bus
+ OverviewConnection
+ {
+ id: fuelCellConnection
+ ballCount: 2
+ path: corner
+ active: root.active && (showFuelCell || (dcCoupled && showInactiveFlow))
+ value: Utils.sign (fuelCellFlow)
+ startPointVisible: true
+ endPointVisible: false
+ anchors
+ {
+ left: fuelCellBox.left; leftMargin: 8
+ top: fuelCellBox.bottom; topMargin: -8
+ right: dcLaneRight.horizontalCenter
+ rightMargin: -0.5 // makes this line up with others
+ bottom: dcLaneTop.verticalCenter
+ }
+ }
+
+ // Wind Gen DC right bus
+ OverviewConnection
+ {
+ id: windGenConnection
+ ballCount: 1
+ path: straight
+ active: root.active && (showWindGen || showInactiveFlow)
+ value: Utils.sign (windGenFlow)
+ startPointVisible: true
+ endPointVisible: false
+ anchors
+ {
+ left: windGenBox.left; leftMargin: 8
+ top: windGenBox.bottom; topMargin: -8
+ right: dcLaneRight.horizontalCenter
+ }
+ }
+
+ // Solar charger to DC right bus
+ OverviewConnection
+ {
+ id: pvChargerConnection
+ ballCount: 2
+ path: corner
+ active: root.active && (showPvCharger || showInactiveFlow)
+ value: Utils.sign (pvChargerFlow)
+ startPointVisible: true
+ endPointVisible: false
+ anchors
+ {
+ left: pvChargerBox.right; leftMargin: -8
+ top: pvChargerBox.bottom; topMargin: -8
+ right: dcLaneLeft.horizontalCenter
+ bottom: alternatorConnection.top
+ }
+ }
+
+ // Synchronise tank name text scroll start
+ Timer
+ {
+ id: scrollTimer
+ interval: 15000
+ repeat: true
+ running: root.active
+ }
+
+ ListView
+ {
+ id: tanksColum
+
+ visible: showTanks
+ width: compact ? root.width : root.width * tankCount / tankTempCount
+ property int tileWidth: width / Math.min (count, 5.2)
+ height: root.tanksHeight
+ anchors
+ {
+ bottom: root.bottom
+ left: root.left
+ }
+
+ // flickable list if more than will fit across bottom of screen
+ interactive: count > 4 ? true : false
+ orientation: ListView.Horizontal
+
+ model: TankModel { id: tankModel }
+ delegate: TileTankEnhanced {
+ // Without an intermediate assignment this will trigger a binding loop warning.
+ property variant theService: DBusServices.get(buddy.id)
+ service: theService
+ width: tanksColum.tileWidth
+ height: root.tanksHeight
+ pumpBindPrefix: root.pumpBindPreffix
+ compact: root.compact
+ Connections {
+ target: scrollTimer
+ onTriggered: doScroll()
+ }
+ }
+ Tile {
+ title: qsTr("tanks")
+ anchors.fill: parent
+ values: TileText {
+ text: qsTr("")
+ width: parent.width
+ wrapMode: Text.WordWrap
+ }
+ z: -1
+ }
+ }
+
+ ListView
+ {
+ id: tempsColumn
+
+ visible: showTemps
+ width: compact ? root.width : root.width * tempCount / tankTempCount
+ property int tileWidth: width / Math.min (count, 5.2)
+ height: root.tanksHeight
+ anchors
+ {
+ bottom: root.bottom
+ bottomMargin: compact ? root.tanksHeight : 0
+ right: root.right
+ }
+
+ // make list flickable if more tiles than will fit completely
+ interactive: count > 4 ? true : false
+ orientation: ListView.Horizontal
+
+ model: tempsModel
+ delegate: TileTemp
+ {
+ width: tempsColumn.tileWidth
+ height: tempsColumn.height
+ compact: root.compact
+ Connections
+ {
+ target: scrollTimer
+ onTriggered: doScroll()
+ }
+ }
+ Tile
+ {
+ title: qsTr("TEMPS")
+ anchors.fill: parent
+ values: TileText
+ {
+ text: qsTr("")
+ width: parent.width
+ wrapMode: Text.WordWrap
+ }
+ z: -1
+ }
+ }
+ ListModel { id: tempsModel }
+
+ // When new service is found check if is a tank sensor
+ Connections
+ {
+ target: DBusServices
+ onDbusServiceFound: addService(service)
+ }
+
+ // hack to get value(s) from within a loop inside a function when service is changing
+ property string tempServiceName: ""
+ property VBusItem temperatureItem: VBusItem { bind: Utils.path(tempServiceName, "/Dc/0/Temperature") }
+
+ function addService(service)
+ {
+ switch (service.type)
+ {
+ case DBusService.DBUS_SERVICE_TEMPERATURE_SENSOR:
+ numberOfTemps++
+ tempsModel.append({serviceName: service.name})
+ break;;
+
+ case DBusService.DBUS_SERVICE_MULTI:
+ hasInverter = true
+ root.tempServiceName = service.name
+ if (temperatureItem.valid && showBatteryTemp)
+ {
+ numberOfTemps++
+ tempsModel.append({serviceName: service.name})
+ }
+ break;;
+ case DBusService.DBUS_SERVICE_MULTI_RS:
+ hasInverter = true
+ break;;
+ case DBusService.DBUS_SERVICE_INVERTER:
+ hasInverter = true
+ if (veDirectInverterService == "")
+ veDirectInverterService = service.name;
+ break;;
+ case DBusService.DBUS_SERVICE_BATTERY:
+ root.tempServiceName = service.name
+ if (temperatureItem.valid && showBatteryTemp)
+ {
+ numberOfTemps++
+ tempsModel.append({serviceName: service.name})
+ }
+ break;;
+ }
+ }
+
+ // Detect available services of interest
+ function discoverServices()
+ {
+ numberOfTemps = 0
+ tempsModel.clear()
+ veDirectInverterService = ""
+ hasInverter = false
+ for (var i = 0; i < DBusServices.count; i++)
+ {
+ addService(DBusServices.at(i))
+ }
+ }
+ VBusItem { id: incomingTankName;
+ bind: Utils.path(settingsBindPreffix, "/Settings/Devices/TankRepeater/IncomingTankService") }
+
+ // help message shown when menu is first drawn
+ Rectangle
+ {
+ id: helpBox
+ color: "white"
+ width: multi.width
+ height: 32
+ opacity: 0.7
+ anchors
+ {
+ verticalCenter: dcLaneTop.verticalCenter
+ horizontalCenter: root.horizontalCenter
+ }
+ visible: false
+ }
+ TileText
+ {
+ text: qsTr ( "Tap tile center for detail at any time" )
+ color: "black"
+ anchors.fill: helpBox
+ wrapMode: Text.WordWrap
+ font.pixelSize: 12
+ visible: helpBox.visible
+ }
+
+ //// hard key handler
+ // used to press buttons when touch isn't available
+ // UP and DOWN buttons cycle through the list of touch areas
+ // "space" button is used to simulate a touch on the area
+ // target must be highlighted so that other uses of "space"
+ // will still occur
+
+ // list of all details touchable areas
+ property variant targetList:
+ [
+ acInputTarget, pvOnInputTarget, acLoadsOnInputTarget,
+ acChargerTarget, alternatorTarget, motorDriveTarget, pvChargerTarget,
+ multiTarget, batteryTarget,
+ acLoadsOnOutputTarget, pvOnOutputTarget, fuelCellTarget, windGenTarget, dcSystemTarget
+ ]
+
+ property int selectedTarget: 0
+
+ Timer
+ {
+ id: targetTimer
+ interval: 5000
+ repeat: false
+ running: false
+ onTriggered: { hideAllTargets () }
+ }
+
+ Keys.forwardTo: [keyHandler]
+ Item
+ {
+ id: keyHandler
+ Keys.onUpPressed:
+ {
+ nextTarget (-1)
+ event.accepted = true
+ }
+
+ Keys.onDownPressed:
+ {
+ nextTarget (+1)
+ event.accepted = true
+ }
+ Keys.onSpacePressed:
+ {
+ if (targetTimer.running)
+ {
+ var foo // hack to make clicked() work
+ bar.clicked (foo)
+ event.accepted = true
+ }
+ else
+ event.accepted = false
+ }
+ }
+ // hack to make clicked() work
+ property variant bar: targetList[selectedTarget]
+
+ function nextTarget (increment)
+ {
+ // make one pass through all possible targets to find an enabled one
+ // if found, that's the new selectedTarget,
+ // if not selectedTarget does not change
+ var newIndex = selectedTarget
+ for (var i = 0; i < targetList.length; i++)
+ {
+ if (( ! targetTimer.running || helpBox.visible) && targetList[newIndex].enabled)
+ {
+ highlightSelectedTarget ()
+ return
+ }
+ newIndex += increment
+ if (newIndex >= targetList.length)
+ newIndex = 0
+ else if (newIndex < 0)
+ newIndex = targetList.length - 1
+ if (targetList[newIndex].enabled)
+ {
+ selectedTarget = newIndex
+ highlightSelectedTarget ()
+ break
+ }
+ }
+ }
+
+ function showHelp ()
+ {
+ for (var i = 0; i < targetList.length; i++)
+ {
+ targetList[i].targetVisible = true
+ }
+ helpBox.visible = true
+ targetTimer.restart ()
+ }
+ function hideAllTargets ()
+ {
+ for (var i = 0; i < targetList.length; i++)
+ {
+ targetList[i].targetVisible = false
+ }
+ helpBox.visible = false
+ }
+ function highlightSelectedTarget ()
+ {
+ for (var i = 0; i < targetList.length; i++)
+ {
+ if (targetList[i] == targetList[selectedTarget])
+ targetList[i].targetVisible = true
+ else
+ targetList[i].targetVisible = false
+ }
+ helpBox.visible = false
+ targetTimer.restart ()
+ }
+}
diff --git a/FileSets/v3.60~25/OverviewFlowComplex.qml.orig b/FileSets/v3.60~25/OverviewFlowComplex.qml.orig
new file mode 100644
index 00000000..65f9139a
--- /dev/null
+++ b/FileSets/v3.60~25/OverviewFlowComplex.qml.orig
@@ -0,0 +1,484 @@
+import QtQuick 2
+import "utils.js" as Utils
+
+OverviewPage {
+ id: root
+
+ property variant sys: theSystem
+ property bool hasAcOutSystem: _hasAcOutSystem.value === 1
+
+ title: qsTr("Overview")
+
+ VBusItem {
+ id: _hasAcOutSystem
+ bind: "com.victronenergy.settings/Settings/SystemSetup/HasAcOutSystem"
+ }
+
+ OverviewBox {
+ id: acInBox
+
+ width: 148
+ height: 100
+ title: getAcSourceName(sys.acSource)
+ titleColor: "#E74c3c"
+ color: "#C0392B"
+ anchors {
+ top: root.top; topMargin: 1
+ left: parent.left; leftMargin: 5
+ }
+
+ values: OverviewAcValues {
+ connection: sys.acInput
+ }
+
+ MbIcon {
+ iconId: getAcSourceIcon(sys.acSource)
+ anchors {
+ bottom: parent.bottom
+ left: parent.left; leftMargin: 2
+ }
+ opacity: 0.5
+ }
+ }
+
+ OverviewBox {
+ id: acLoadBox
+ title: qsTr("AC Loads")
+ color: "#27AE60"
+ titleColor: "#2ECC71"
+ width: 148
+ height: 100
+
+ anchors {
+ left: acInBox.right
+ leftMargin: hasAcOutSystem ? 10 : 174
+ top: root.top; topMargin: 1
+ }
+
+ values: OverviewAcValues {
+ connection: sys.acInLoad
+ }
+ }
+
+ OverviewBox {
+ id: acOutputBox
+ title: qsTr("Critical Loads")
+ color: "#157894"
+ titleColor: "#419FB9"
+ height: 100
+ width: 148
+ visible: hasAcOutSystem
+ anchors {
+ right: root.right; rightMargin: 5
+ top: root.top; topMargin: 17
+ }
+
+ values: OverviewAcValues {
+ connection: sys.acOutLoad
+ }
+ }
+
+ Multi {
+ id: multi
+ iconId: "overview-inverter-short"
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ bottom: root.bottom; bottomMargin: 39
+ }
+ }
+
+ // invisible item to connection all AC connections to..
+ Item {
+ id: acBus
+ height: 10
+ anchors {
+ left: acInBox.left; leftMargin: hasAcOutSystem ? 5 : acInBox.width - 5
+ right: acLoadBox.right; rightMargin: 2
+ bottom: acInBox.bottom; bottomMargin: -15
+ }
+ }
+
+ Battery {
+ id: battery
+
+ soc: sys.battery.soc.valid ? sys.battery.soc.value : 0
+ preferRenewable: sys.preferRenewableEnergy.valid
+ preferRenewableOverride: sys.preferRenewableEnergy.value === 0 || sys.preferRenewableEnergy.value === 2
+ preferRenewableOverrideGenset: sys.remoteGeneratorSelected.value === 1 || sys.acSource.value === 2
+ height: pvInverterOnGrid.visible ? 81 : 101
+ width: 145
+
+ anchors {
+ bottom: parent.bottom; bottomMargin: 5;
+ left:parent.left; leftMargin: 5
+ }
+ values: Column {
+ y: pvInverterOnGrid.visible ? 0 : 8
+ width: parent.width
+
+ TileText {
+ text: sys.battery.soc.valid ? sys.battery.soc.value.toFixed(0) : "--"
+ font.pixelSize: 30
+
+ Text {
+ anchors {
+ bottom: parent.bottom; bottomMargin: 4
+ horizontalCenter: parent.horizontalCenter; horizontalCenterOffset: parent.paintedWidth / 2 + 5
+ }
+ visible: sys.battery.soc.valid
+ text: "%"
+ color: "white"
+ font.bold: true
+ font.pixelSize: 12
+ }
+ }
+ TileText {
+ text: sys.battery.power.format(0)
+ }
+ TileText {
+ text: sys.battery.voltage.format(1) + " " + sys.battery.current.format(1)
+ }
+ }
+ }
+
+ // PV inverter on AC in, AC Output ignored
+ OverviewSolarInverter {
+ id: pvInverterOnGridNoAcOut
+ title: qsTr("PV Inverter")
+ width: 154
+ height: 100
+ visible: sys.pvOnGrid.power.valid && !hasAcOutSystem
+ showInverterIcon: false
+ values: TileText {
+ y: 2
+ text: sys.pvOnGrid.power.format(0)
+ font.pixelSize: 25
+ }
+ anchors {
+ top: root.top; topMargin: 1
+ horizontalCenter: root.horizontalCenter
+ }
+ }
+
+ OverviewSolarInverter {
+ id: pvInverterOnGrid
+ title: qsTr("PV Inverter")
+ width: 148
+ height: 60
+ visible: sys.pvOnGrid.power.valid && hasAcOutSystem
+ showInverterIcon: false
+ values: TileText {
+ y: 2
+ text: sys.pvOnGrid.power.format(0)
+ font.pixelSize: 20
+ }
+ anchors {
+ bottom: battery.top; bottomMargin: 5
+ left: root.left; leftMargin: 5
+ }
+ }
+
+ OverviewSolarInverter {
+ id: pvInverterOnAcOut
+ title: qsTr("PV Inverter")
+ width: 148
+ height: 60
+ visible: sys.pvOnAcOut.power.valid
+ showInverterIcon: false
+
+ values: TileText {
+ y: 2
+ text: sys.pvOnAcOut.power.format(0)
+ font.pixelSize: 20
+ }
+ anchors {
+ bottom: blueSolarCharger.top; bottomMargin: 5
+ right: parent.right; rightMargin: 5
+ }
+ }
+
+ OverviewSolarCharger {
+ id: blueSolarCharger
+ title: qsTr("PV Charger")
+ width: 148
+ height: 60
+ visible: sys.pvCharger.power.valid
+ showChargerIcon: false
+
+ anchors {
+ right: root.right; rightMargin: 5
+ bottom: root.bottom; bottomMargin: 5;
+ }
+
+ values: TileText {
+ y: 2
+ text: sys.pvCharger.power.format(0)
+ font.pixelSize: 20
+ }
+ }
+
+ OverviewEssReason {
+ anchors {
+ bottom: parent.bottom; bottomMargin: 5
+ horizontalCenter: parent.horizontalCenter
+ }
+ }
+
+ // AC source power flow
+ OverviewConnection {
+ id: acSource
+ ballCount: 4
+ path: corner
+ active: root.active && hasAcOutSystem
+ value: flow(sys.acInput ? sys.acInput.power : undefined) * -1
+ startPointVisible: false
+
+ anchors {
+ right: acInBox.left; rightMargin: -9
+ left: pvInverterOnGridConnection.horizontalCenter
+ bottom: acInBox.bottom; bottomMargin: 8
+ top: acBus.verticalCenter
+ }
+ }
+
+ // Coupled AC sources
+ OverviewConnection {
+ id: coupledAcConnection
+
+ property VBusItem coupled: VBusItem {
+ property double gridPower: sys.acInput.power.valid ? sys.acInput.power.value : 0
+ property double pvPower: sys.pvOnGrid.power.valid ? sys.pvOnGrid.power.value : 0
+ value: gridPower + pvPower
+ }
+
+ ballCount: 1
+ path: straight
+ active: root.active && hasAcOutSystem
+ value: flow(coupled)
+ startPointVisible: false
+ endPointVisible: false
+
+ anchors {
+ left: pvInverterOnGridConnection.right
+ right: vebusConnection.left
+ top: acBus.verticalCenter
+ bottom: acBus.verticalCenter
+ }
+ }
+
+ // AC source power flow, ignored AC output
+ OverviewConnection {
+ id: acSourceNoAcOut
+ ballCount: 5
+ path: corner
+ active: root.active && !hasAcOutSystem
+ value: acSource.value
+ startPointVisible: false
+
+ anchors {
+ right: acInBox.left; rightMargin: -9
+ left: pvInverterOnGridConnectionNoAcOut.horizontalCenter
+ bottom: acInBox.bottom; bottomMargin: 8
+ top: acBus.verticalCenter
+ }
+ }
+
+ // Coupled AC sources, ignored AC output
+ OverviewConnection {
+ id: coupledAcConnectionNoAcOut
+
+ ballCount: 1
+ path: straight
+ active: root.active && !hasAcOutSystem
+ value: coupledAcConnection.value
+ startPointVisible: false
+ endPointVisible: false
+
+ anchors {
+ left: pvInverterOnGridConnectionNoAcOut.right
+ right: vebusConnection.left
+ top: acBus.verticalCenter
+ bottom: acBus.verticalCenter
+ }
+ }
+
+ // Grid inverter power flow, ignored AC output
+ OverviewConnection {
+ id: pvInverterOnGridConnectionNoAcOut
+ ballCount: 1
+ path: straight
+ active: root.active && pvInverterOnGridNoAcOut.visible
+ value: flow(sys.pvOnGrid ? sys.pvOnGrid.power : undefined)
+ startPointVisible: true
+ endPointVisible: false
+
+ anchors {
+ top: pvInverterOnGridNoAcOut.bottom; topMargin: -8
+ bottom: acBus.verticalCenter
+ left: pvInverterOnGridNoAcOut.left; leftMargin: 8
+ right: pvInverterOnGridNoAcOut.left; rightMargin: -8
+ }
+ }
+
+ // Grid inverter power flow
+ OverviewConnection {
+ id: pvInverterOnGridConnection
+ ballCount: 1
+ path: straight
+ active: root.active && pvInverterOnGrid.visible
+ value: flow(sys.pvOnGrid ? sys.pvOnGrid.power : undefined) * -1
+ startPointVisible: false
+
+ anchors {
+ top: acBus.verticalCenter
+ bottom: pvInverterOnGrid.top; bottomMargin: -8
+ left: pvInverterOnGrid.right; leftMargin: -8
+ }
+ }
+
+ // power to loads
+ OverviewConnection {
+ id: loadConnection
+ ballCount: hasAcOutSystem ? 3 : 5
+ path: corner
+ active: root.active
+ value: flow(sys.acInLoad.power)
+ startPointVisible: false
+ endPointVisible: true
+
+ anchors {
+ right: acLoadBox.right; rightMargin: hasAcOutSystem ? 10 : acLoadBox.width - 10
+ left: vebusConnection.horizontalCenter
+ top: acBus.verticalCenter
+ bottom: acLoadBox.bottom; bottomMargin: 8
+ }
+ }
+
+ // Towards vebus system
+ OverviewConnection {
+ id: vebusConnection
+
+ property VBusItem vebusAcPower: VBusItem { bind: [sys.vebusPrefix, "/Ac/ActiveIn/P"] }
+
+ ballCount: 1
+ path: straight
+ active: root.active
+ value: flow(vebusAcPower)
+ startPointVisible: false
+ endPointVisible: true
+
+ anchors {
+ left: multi.left; leftMargin: 8
+ top: acBus.verticalCenter
+ bottom: multi.top; bottomMargin: -7
+ }
+ }
+
+ // AC out connection
+ OverviewConnection {
+ id: acOutConnection
+
+ property double pvInverterOnAcOutPower: sys.pvOnAcOut.power.valid ? sys.pvOnAcOut.power.value : 0
+ property double acOutLoad: sys.acOutLoad.power.valid ? sys.acOutLoad.power.value : 0
+ property VBusItem vebusAcOutPower: VBusItem { value: acOutConnection.acOutLoad - acOutConnection.pvInverterOnAcOutPower }
+
+ ballCount: 1
+ path: straight
+ active: root.active && (hasAcOutSystem || pvInverterOnAcOut.visible)
+ value: flow(vebusAcOutPower)
+ endPointVisible: false
+
+ anchors {
+ left: multi.right; leftMargin: -8
+ right: acOutBoxConnection.left
+ top: multi.top; topMargin: 8
+ }
+ }
+
+ // UPS conenction
+ OverviewConnection {
+ id: acOutBoxConnection
+
+ ballCount: 1
+ path: straight
+ active: root.active && hasAcOutSystem
+ value: flow(sys.acOutLoad.power)
+ startPointVisible: false
+
+ anchors {
+ left: acOutputBox.left; leftMargin: 10
+ top: acOutConnection.verticalCenter
+ bottom: acOutputBox.bottom; bottomMargin: 9
+ }
+ }
+
+ // PV Inverter on AC out connection
+ OverviewConnection {
+ id: pvOnAcOutConnection
+
+ ballCount: 1
+ path: straight
+ active: root.active && pvInverterOnAcOut.visible
+ value: flow(sys.pvOnAcOut.power)
+ endPointVisible: false
+
+ anchors {
+ left: acOutBoxConnection.left
+ bottom: acOutConnection.verticalCenter
+ top: pvInverterOnAcOut.top; topMargin: 8
+ }
+ }
+
+ // DC connection from multi
+ OverviewConnection {
+ ballCount: 1
+ path: straight
+ active: root.active
+ value: flow(sys.inverterChargerDc.power)
+ endPointVisible: false
+
+ anchors {
+ right: dcConnection.right;
+ top: multi.bottom; topMargin: -10
+ bottom: dcConnection.top;
+ }
+ }
+
+ // Battery to DC connection
+ OverviewConnection {
+ ballCount: 3
+ path: straight
+ active: root.active
+ value: Utils.sign(noNoise(sys.pvCharger.power) + noNoise(sys.inverterChargerDc.power))
+ startPointVisible: false
+
+ anchors {
+ left: dcConnection.left;
+ top: dcConnection.verticalCenter
+ right: battery.right; rightMargin: 10
+ }
+ }
+
+ // Solar charger to DC connection
+ OverviewConnection {
+ ballCount: 3
+ path: straight
+ active: root.active && blueSolarCharger.visible
+ value: flow(sys.pvCharger.power)
+ endPointVisible: false
+
+ anchors {
+ right: dcConnection.right;
+ top: dcConnection.top
+ left: blueSolarCharger.left; leftMargin: 10
+ }
+ }
+
+ Item {
+ id: dcConnection
+ anchors {
+ horizontalCenter: multi.horizontalCenter
+ top: multi.bottom; topMargin: 10
+ }
+ }
+}
diff --git a/FileSets/v3.60~25/OverviewGeneratorEnhanced.qml b/FileSets/v3.60~25/OverviewGeneratorEnhanced.qml
new file mode 100644
index 00000000..37895168
--- /dev/null
+++ b/FileSets/v3.60~25/OverviewGeneratorEnhanced.qml
@@ -0,0 +1,549 @@
+// GuiMods enhanced generator overview
+// This file has been modified to:
+// add Auto Start display and control
+// show voltage, current, frequency, and power gauge in AC input tile
+// show the generator running state inside the icon top left
+// show a warning when the generator digital input and expected generator state disagree
+// move current run time to separate tile
+
+import QtQuick 2
+import "utils.js" as Utils
+import "enhancedFormat.js" as EnhFmt
+
+OverviewPage {
+ id: root
+
+ property string settingsBindPrefix
+ property string bindPrefix
+ property variant sys: theSystem
+//////// added to show alternator in place of inactive genset
+ property string guiModsPrefix: "com.victronenergy.settings/Settings/GuiMods"
+ VBusItem { id: replaceAcInItem; bind: Utils.path(guiModsPrefix, "/ReplaceInactiveAcIn") }
+ property bool hasAlternator: sys.alternator.power.valid
+ property bool showAlternator: replaceAcInItem.valid && replaceAcInItem.value == 1 && hasAlternator && ! sys.genset.power.valid
+ property bool showAcIn: ! showAlternator
+
+ property string icon: "overview-generator"
+ property VBusItem state: VBusItem { bind: Utils.path(bindPrefix, "/State") }
+ property VBusItem error: VBusItem { bind: Utils.path(bindPrefix, "/Error") }
+ property VBusItem runningTime: VBusItem { bind: Utils.path(bindPrefix, "/Runtime") }
+ property VBusItem runningBy: VBusItem { bind: Utils.path(bindPrefix, "/RunningByConditionCode") }
+ VBusItem { id: totalAcummulatedTime; bind: Utils.path(settingsBindPrefix, "/AccumulatedTotal") }
+ VBusItem { id: totalAccumulatedTimeOffset; bind: Utils.path(settingsBindPrefix, "/AccumulatedTotalOffset") }
+ property VBusItem quietHours: VBusItem { bind: Utils.path(bindPrefix, "/QuietHours") }
+ property VBusItem testRunDuration: VBusItem { bind: Utils.path(settingsBindPrefix, "/TestRun/Duration") }
+ property VBusItem nextTestRun: VBusItem { bind: Utils.path(bindPrefix, "/NextTestRun") }
+ property VBusItem skipTestRun: VBusItem { bind: Utils.path(bindPrefix, "/SkipTestRun") }
+
+ property VBusItem todayRuntime: VBusItem { bind: Utils.path(bindPrefix, "/TodayRuntime") }
+ property VBusItem manualTimer: VBusItem { bind: Utils.path(bindPrefix, "/ManualStartTimer") }
+ property VBusItem autoStart: VBusItem { bind: Utils.path(settingsBindPrefix, "/AutoStartEnabled") }
+
+ property bool errors: ! state.valid || state.value == 10
+
+ property VBusItem externalOverrideItem: VBusItem { bind: Utils.path(bindPrefix, "/ExternalOverride") }
+ property bool externalOverride: externalOverrideItem.valid && externalOverrideItem.value == 1 && ! errors
+
+ VBusItem { id: showGaugesItem; bind: Utils.path(guiModsPrefix, "/ShowGauges") }
+ property bool showGauges: showGaugesItem.valid ? showGaugesItem.value === 1 ? true : false : false
+ property bool editMode: autoRunTile.editMode || manualTile.editMode
+
+ VBusItem { id: serviceInterval; bind: Utils.path(settingsBindPrefix, "/ServiceInterval") }
+ VBusItem { id: serviceCounterItem; bind: Utils.path(bindPrefix, "/ServiceCounter") }
+ property bool showServiceInfo: serviceCounterItem.valid && serviceInterval.valid && serviceInterval.value > 0
+ property bool serviceOverdue: showServiceInfo && serviceCounterItem.value < 0
+
+//////// add to display AC input ignored
+ VBusItem { id: ignoreAcInput1; bind: Utils.path(sys.vebusPrefix, "/Ac/State/IgnoreAcIn1") }
+ VBusItem { id: ignoreAcInput2; bind: Utils.path(sys.vebusPrefix, "/Ac/State/IgnoreAcIn2") }
+ VBusItem { id: acActiveInput; bind: Utils.path(sys.vebusPrefix, "/Ac/ActiveIn/ActiveInput") }
+ VBusItem { id: ac1source; bind: Utils.path("com.victronenergy.settings", "/Settings/SystemSetup/AcInput1") }
+ VBusItem { id: ac2source; bind: Utils.path("com.victronenergy.settings", "/Settings/SystemSetup/AcInput2") }
+
+ title: qsTr("Generator")
+
+ property bool autoStartSelected: false
+
+ Component.onCompleted:
+ {
+ setFocusManual ()
+ }
+
+ Keys.forwardTo: [keyHandler]
+ Item
+ {
+ id: keyHandler
+ Keys.onUpPressed:
+ {
+ setFocusAuto ()
+ event.accepted = true
+ }
+ Keys.onDownPressed:
+ {
+ setFocusManual ()
+ event.accepted = true
+ }
+ // prevents page changes while timers are running
+ //// Keys.onReturnPressed: event.accepted = manualTile.startCountdown || autoRunTile.startCountdown
+ //// Keys.onEscapePressed: event.accepted = manualTile.startCountdown || autoRunTile.startCountdown
+ }
+
+ function setFocusManual ()
+ {
+ autoStartSelected = false
+ }
+
+ function setFocusAuto ()
+ {
+ autoStartSelected = true
+ }
+
+ function formatTime (time)
+ {
+ if (time >= 3600)
+ return (time / 3600).toFixed(0) + " h"
+ else
+ return (time / 60).toFixed(0) + " m"
+ }
+
+ function stateDescription()
+ {
+ if (!state.valid)
+ return qsTr ("")
+ else if (state.value === 10)
+ {
+ switch(error.value)
+ {
+ case 1:
+ return qsTr("Error: Remote switch control disabled")
+ case 2:
+ return qsTr("Error: Generator in fault condition")
+ case 3:
+ return qsTr("Error: Generator not detected at AC input")
+ default:
+ return qsTr("Error")
+ }
+ }
+ else
+ {
+ var condition = ""
+ var running = true
+ var manual = false
+ switch (runningBy.value)
+ {
+ case 0: // stopped
+ condition = ""
+ running = false
+ break;;
+ case 1:
+ manual = true
+ condition = ""
+ break;;
+ case 2:
+ condition = qsTr("Test run")
+ break;;
+ case 3:
+ condition = qsTr("Loss of communication")
+ break;;
+ case 4:
+ condition = qsTr("SOC")
+ break;;
+ case 5:
+ condition = qsTr("AC load")
+ break;;
+ case 6:
+ condition = qsTr("Battery current")
+ break;;
+ case 7:
+ condition = qsTr("Battery voltage")
+ break;;
+ case 8:
+ condition = qsTr("Inverter temperature")
+ break;;
+ case 9:
+ condition = qsTr("Inverter overload")
+ break;;
+ default:
+ condition = qsTr("???")
+ break;;
+ }
+
+ if (externalOverride)
+ {
+ if (running && ! manual)
+ return qsTr ("auto pending: ") + condition
+ else
+ return " "
+ }
+ else if (manual)
+ {
+ if (manualTimer.valid && manualTimer.value > 0)
+ return qsTr("Timed run")
+ else
+ return qsTr("Manual run")
+ }
+ else if (running)
+ return qsTr ("auto run: ") + condition
+ else
+ return " "
+ }
+ }
+
+ function getNextTestRun()
+ {
+ if ( ! root.state.valid)
+ return ""
+ if (!nextTestRun.value)
+ return qsTr("No test run programmed")
+
+ var todayDate = new Date()
+ var nextDate = new Date(nextTestRun.value * 1000)
+ var nextDateEnd = new Date(nextDate.getTime())
+ var message = ""
+ // blank "next run" if test run is active
+ if (runningBy.value == 2)
+ return " "
+ else if (todayDate.getDate() == nextDate.getDate() && todayDate.getMonth() == nextDate.getMonth())
+ {
+ message = qsTr("Next test run today %1").arg(
+ Qt.formatDateTime(nextDate, "hh:mm").toString())
+ }
+ else
+ {
+ message = qsTr("Next test run on %1").arg(
+ Qt.formatDateTime(nextDate, "dd/MM/yyyy").toString())
+ nextDateEnd.setSeconds(testRunDuration.value) }
+
+ if (skipTestRun.value === 1)
+ message += qsTr(" \(skipped\)")
+
+ return message
+ }
+
+ Tile {
+ id: imageTile
+ width: 180
+ height: 136
+ MbIcon {
+ id: generator
+ iconId: icon
+ anchors.centerIn: parent
+ }
+ anchors { top: parent.top; left: parent.left }
+ values: [
+ // spacer
+ TileText {
+ width: imageTile.width - 5
+ text: " "
+ font.pixelSize: 62
+ },
+ TileText {
+ width: imageTile.width - 5
+ text: runningTime.valid ? runningTime.value > 0 ? qsTr ("Running ") : qsTr ("Stopped ") : " "
+ }
+ ]
+ }
+
+ Tile {
+ id: statusTile
+ height: imageTile.height
+ color: "#4789d0"
+ anchors { top: parent.top; left: imageTile.right; right: root.right }
+ title: qsTr("STATUS")
+ values: [
+ TileText
+ {
+ width: statusTile.width - 5
+ color: externalOverride ? "yellow" : "white"
+ text:
+ {
+ var runPrefix
+ var message
+ if ( ! root.state.valid)
+ return qsTr ("Generator not connected")
+ else if (root.state.value === 2)
+ runPrefix = qsTr("Warming up for ")
+ else
+ runPrefix = qsTr ("Running for ")
+ if (!root.state.valid)
+ message = ""
+ else if (externalOverride)
+ if (root.state.value === 0)
+ message = qsTr("External Override - running")
+ else
+ message = qsTr("External Override - stopped")
+ else if (root.state.value === 3)
+ message = qsTr("Cool-down")
+ else if (root.state.value === 4)
+ message = qsTr("Stopping")
+ else if (runningBy.value == 0)
+ message = qsTr ("Stopped")
+ else if ( ! runningTime.valid)
+ message = runPrefix + "??"
+ else
+ {
+ message = runPrefix + formatTime (runningTime.value)
+ if (manualTimer.valid && manualTimer.value > 0)
+ message += qsTr (" ends in ") + formatTime (manualTimer.value)
+ }
+ return message
+ }
+ },
+ Rectangle
+ {
+ width: parent.width
+ height: 3
+ color: "transparent"
+ },
+ TileTextMultiLine
+ {
+ text: stateDescription()
+ width: statusTile.width - 5
+ },
+ Rectangle
+ {
+ width: parent.width
+ height: 3
+ color: "transparent"
+ },
+ TileText
+ {
+ text: qsTr("\nQuiet hours");
+ width: statusTile.width - 5
+ font.bold: runningBy.valid && runningBy.value != 0
+ color: font.bold ? "yellow" : "white"
+ visible: quietHours.value === 1
+ },
+ Rectangle
+ {
+ width: parent.width
+ height: 3
+ color: "transparent"
+ },
+ TileTextMultiLine
+ {
+ width: statusTile.width - 5
+ text: getNextTestRun()
+ }
+ ]
+ }
+
+ Tile {
+ id: acInTile
+ title: qsTr("GENERATOR POWER")
+ width: 150
+ height: 136
+ color: "#82acde"
+ anchors { top: imageTile.bottom; left: parent.left }
+ visible: showAcIn
+ values:
+ [
+ OverviewAcValuesEnhanced
+ {
+ connection: sys.genset
+ visible: sys.genset.power.valid
+ },
+ TileText
+ {
+ width: acInTile.width - 5
+ text:
+ {
+ if (ac1source.valid && ac1source.value == 2)
+ {
+ if (ignoreAcInput1.valid && ignoreAcInput1.value == 1)
+ return qsTr ("\nAC In Ignored\nduring\ngenerator\nstart / stop")
+ else
+ return ""
+ }
+ else if (ac2source.valid && ac2source.value == 2)
+ {
+ if (ignoreAcInput2.valid && ignoreAcInput2.value == 1)
+ return qsTr ("\nAC In Ignored\nduring\ngenerator\nstart / stop")
+ else
+ return ""
+ }
+ else
+ return qsTr ("\nAC In\nis not\ngenerator")
+ }
+ visible: !sys.genset.power.valid
+ }
+ ]
+////// add power bar graph
+ PowerGauge
+ {
+ id: acInBar
+ width: parent.width
+ height: 12
+ anchors
+ {
+ top: parent.top; topMargin: 20
+ horizontalCenter: parent.horizontalCenter
+ }
+ connection: sys.genset
+ useInputCurrentLimit: true
+ maxForwardPowerParameter: ""
+ maxReversePowerParameter: ""
+ visible: showGauges && sys.genset.power.valid
+ }
+ }
+
+//////// added to show alternator in place of AC generator
+ Tile {
+ id: alternatorTile
+ title: qsTr("ALTERNATOR POWER")
+ color: "#157894"
+ anchors.fill: acInTile
+ visible: showAlternator
+ values:
+ [
+ TileText
+ {
+ text: EnhFmt.formatVBusItem (sys.alternator.power, "W")
+ font.pixelSize: 22
+ }
+ ]
+////// add power bar graph
+ PowerGauge
+ {
+ id: alternatorGauge
+ width: parent.width
+ height: 12
+ anchors
+ {
+ top: parent.top; topMargin: 20
+ horizontalCenter: parent.horizontalCenter
+ }
+ connection: sys.alternator
+ maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/MaxAlternatorPower"
+ visible: showGauges && showAlternator
+ }
+ }
+
+ Tile {
+ id: runTimeTile
+ title: qsTr("RUN TIMES")
+ width: 140
+ anchors { top: acInTile.top; bottom: parent.bottom; left: acInTile.right }
+ values: [
+ TileText
+ {
+ width: runTimeTile.width - 5
+ text: qsTr ("Today")
+ },
+ TileText {
+ width: runTimeTile.width - 5
+ text: todayRuntime.valid ? formatTime (todayRuntime.value) : "--"
+ },
+ Rectangle
+ {
+ width: parent.width
+ height: 3
+ color: "transparent"
+ },
+ TileText
+ {
+ width: runTimeTile.width - 5
+ text: qsTr ("Accumulated")
+ },
+ TileText
+ {
+ width: runTimeTile.width - 5
+ text:
+ {
+ if ( ! totalAcummulatedTime.valid)
+ return "--"
+ else if (totalAccumulatedTimeOffset.valid )
+ return formatTime (totalAcummulatedTime.value - totalAccumulatedTimeOffset.value)
+ else
+ return formatTime (totalAcummulatedTime.value)
+ }
+ },
+ Rectangle
+ {
+ width: parent.width
+ height: 3
+ color: "transparent"
+ },
+ TileText
+ {
+ width: runTimeTile.width - 5
+ visible: showServiceInfo
+ color: serviceOverdue ? "red" : "white"
+ text: serviceOverdue ? qsTr ("Service OVERDUE") : qsTr ("Service in")
+ },
+ TileText
+ {
+ width: runTimeTile.width - 5
+ visible: showServiceInfo
+ color: serviceOverdue ? "red" : "white"
+ text: formatTime (Math.abs (serviceCounterItem.value))
+ }
+ ]
+ }
+
+ TileAutoRunEnhanced
+ {
+ id: autoRunTile
+ bindPrefix: root.bindPrefix
+ focus: root.active && autoStartSelected
+ connected: state.valid
+ tileHeight: acInTile.height / 2
+ anchors {
+ bottom: parent.bottom; bottomMargin: tileHeight
+ left: runTimeTile.right
+ right: parent.right
+ }
+ }
+
+ TileManualStartEnhanced
+ {
+ id: manualTile
+ bindPrefix: root.bindPrefix
+ focus: root.active && ! autoStartSelected
+ connected: state.valid
+ tileHeight: acInTile.height / 2
+ anchors {
+ bottom: parent.bottom
+ left: runTimeTile.right
+ right: parent.right
+ }
+ }
+
+ // mouse areas must be AFTER their associated objects so those objects can catch mouse events
+ // rejected by these areas
+ // mouse targets need to be disabled while changes are pending
+ MouseArea {
+ id: autoRunTarget
+ anchors.fill: autoRunTile
+ enabled: root.active && ! editMode
+ onPressed:
+ {
+ if ( ! root.autoStartSelected )
+ {
+ setFocusAuto ()
+ mouse.accepted = true
+ }
+ else
+ {
+ mouse.accepted = false
+ }
+ }
+ }
+ MouseArea {
+ id: manualStartTarget
+ anchors.fill: manualTile
+ enabled: root.active && ! editMode
+ onPressed:
+ {
+ if ( root.autoStartSelected )
+ {
+ setFocusManual ()
+ mouse.accepted = true
+ }
+ else
+ {
+ mouse.accepted = false
+ }
+ }
+ }
+}
diff --git a/FileSets/v3.60~25/OverviewGeneratorEnhanced.qml.orig b/FileSets/v3.60~25/OverviewGeneratorEnhanced.qml.orig
new file mode 100644
index 00000000..3dd70448
--- /dev/null
+++ b/FileSets/v3.60~25/OverviewGeneratorEnhanced.qml.orig
@@ -0,0 +1,96 @@
+import QtQuick 2
+import "utils.js" as Utils
+
+MbPage {
+ id: root
+ title: qsTr("Generator start/stop")
+ property string settingsBindPrefix
+ property string startStopBindPrefix
+ property bool showRunTime: true
+ property alias startStopModel: _startStopModel
+ property VBusItem activeCondition: VBusItem { bind: Utils.path(startStopBindPrefix, "/RunningByCondition") }
+ property VBusItem generatorState: VBusItem { bind: Utils.path(startStopBindPrefix, "/State") }
+ property VBusItem runningTime: VBusItem { bind: Utils.path(startStopBindPrefix, "/Runtime") }
+
+ FnGeneratorStates {
+ id: genState
+ }
+
+ model: startStopModel
+
+ function formatError(text, value)
+ {
+ return "#" + value.toString() + " " + text
+ }
+
+ VisibleItemModel {
+ id: _startStopModel
+
+ MbSwitch {
+ name: qsTr("Auto start functionality")
+ bind: Utils.path(startStopBindPrefix, "/AutoStartEnabled")
+ show: startStopBindPrefix === "com.victronenergy.generator.startstop0"
+ }
+
+ MbSubMenu {
+ description: qsTr("Manual start")
+ show: startStopBindPrefix === "com.victronenergy.generator.startstop0"
+ subpage:
+ Component {
+ PageGeneratorManualStart {
+ startStopBindPrefix: root.startStopBindPrefix
+ }
+ }
+ }
+
+ MbItemValue {
+ description: qsTr("Current run time")
+ item.text: runningTime.valid ? Utils.secondsToNoSecsString(runningTime.value) : "0"
+ show: generatorState.value >= 1 && generatorState.value <= 3 // Running, Warm-up, Cool-down
+ }
+
+ MbItemValue {
+ description: qsTr("State")
+ show: startStopBindPrefix === "com.victronenergy.generator.startstop0"
+ item.text: activeCondition.valid ? genState.getState(generatorState.value, activeCondition.value) : '---'
+ }
+
+ MbItemOptions {
+ id: _gensetStatus
+ description: qsTr("Error")
+ bind: Utils.path(startStopBindPrefix, "/Error")
+ readonly: true
+ show: valid && startStopBindPrefix === "com.victronenergy.generator.startstop0"
+ possibleValues: [
+ MbOption { description: qsTr("No error"); value: 0 },
+ MbOption { description: formatError(qsTr("Remote switch control disabled"), 1); value: 1 },
+ MbOption { description: formatError(qsTr("Generator in fault condition"), 2); value: 2 },
+ MbOption { description: formatError(qsTr("Generator not detected at AC input"), 3); value: 3 }
+ ]
+ }
+
+ MbSubMenu {
+ id: conditions
+ description: qsTr("Settings")
+ subpage: Component {
+ PageSettingsGenerator {
+ settingsBindPrefix: root.settingsBindPrefix
+ startStopBindPrefix: root.startStopBindPrefix
+ }
+ }
+ }
+
+ MbSubMenu {
+ id: runtimePage
+ description: qsTr("Run time and service")
+ subpage:
+ Component {
+ PageGeneratorRuntimeService {
+ title: qsTr("Run time and service")
+ settingsBindPrefix: root.settingsBindPrefix
+ startStopBindPrefix: root.startStopBindPrefix
+ }
+ }
+ }
+ }
+}
diff --git a/FileSets/v3.60~25/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.60~25/OverviewGeneratorRelayEnhanced.qml
new file mode 100644
index 00000000..48133381
--- /dev/null
+++ b/FileSets/v3.60~25/OverviewGeneratorRelayEnhanced.qml
@@ -0,0 +1,8 @@
+import QtQuick 2
+import "utils.js" as Utils
+
+OverviewGeneratorEnhanced {
+ icon: "overview-generator"
+ settingsBindPrefix: "com.victronenergy.settings/Settings/Generator0"
+ bindPrefix: "com.victronenergy.generator.startstop0"
+}
diff --git a/FileSets/v3.60~25/OverviewGeneratorRelayEnhanced.qml.orig b/FileSets/v3.60~25/OverviewGeneratorRelayEnhanced.qml.orig
new file mode 100644
index 00000000..3b1a162b
--- /dev/null
+++ b/FileSets/v3.60~25/OverviewGeneratorRelayEnhanced.qml.orig
@@ -0,0 +1,8 @@
+import QtQuick 2
+import "utils.js" as Utils
+
+OverviewGenerator {
+ icon: "overview-generator"
+ settingsBindPrefix: "com.victronenergy.settings/Settings/Generator0"
+ bindPrefix: "com.victronenergy.generator.startstop0"
+}
diff --git a/FileSets/v3.60~25/OverviewHubEnhanced.qml b/FileSets/v3.60~25/OverviewHubEnhanced.qml
new file mode 100644
index 00000000..c4c492d8
--- /dev/null
+++ b/FileSets/v3.60~25/OverviewHubEnhanced.qml
@@ -0,0 +1,1504 @@
+////// MODIFIED to show:
+////// tanks in a row along bottom
+////// PV voltage and current and DC power current (up to 2 MPPTs with tanks and temps or 3 without)
+////// PV inverter power (up to 2 with tanks and temps or 3 without)
+////// voltage, current, frequency in AC tiles (plus current limit for AC input)
+////// time of day
+////// current in DC Loads
+////// remaining time in Battery tile
+////// bar graphs on AC in/out and Multi
+////// detail pages for all tiles
+////// bar gauge on PV Charger tile
+////// add support for VE.Direct inverters
+
+import QtQuick 2
+import "utils.js" as Utils
+////// ADDED to show tanks
+import com.victron.velib 1.0
+import "timeToGo.js" as TTG
+import "enhancedFormat.js" as EnhFmt
+
+OverviewPage {
+ id: root
+
+ property variant sys: theSystem
+
+ property string systemPrefix: "com.victronenergy.system"
+ property string guiModsPrefix: "com.victronenergy.settings/Settings/GuiMods"
+
+////// GuiMods — DarkMode
+ property VBusItem darkModeItem: VBusItem { bind: "com.victronenergy.settings/Settings/GuiMods/DarkMode" }
+ property bool darkMode: darkModeItem.valid && darkModeItem.value == 1
+
+ VBusItem { id: vebusService; bind: Utils.path(systemPrefix, "/VebusService") }
+ property bool isMulti: vebusService.valid
+ property string veDirectInverterService: ""
+ property string inverterService: vebusService.valid ? vebusService.value : veDirectInverterService
+
+ VBusItem { id: replaceAcInItem; bind: Utils.path(guiModsPrefix, "/ReplaceInactiveAcIn") }
+ property bool hasAlternator: sys.alternator.power.valid
+ property bool replaceAcIn: replaceAcInItem.valid && replaceAcInItem.value == 1 && hasAlternator && (sys.acSource == 0 || sys.acSource == 240)
+ property bool showAcInput: ((isMulti || sys.acInput.power.valid) && ! replaceAcIn) || showAllTiles
+ property bool showAlternator: !showAcInput && hasAlternator
+ property double alternatorFlow: showAlternator ? noNoise (sys.alternator.power) : 0
+ property bool showAcLoads: isMulti || sys.acLoad.power.valid || veDirectInverterService != ""
+ property bool showDcSystem: (hasDcSystemItem.valid && hasDcSystemItem.value > 0) || showAllTiles
+ property bool hasInverter: false
+ property bool showInverter: hasInverter || inverterService != "" || showAllTiles
+
+ property bool hasAcSolarOnAcIn1: sys.pvOnAcIn1.power.valid
+ property bool hasAcSolarOnAcIn2: sys.pvOnAcIn2.power.valid
+ property bool hasAcSolarOnIn: hasAcSolarOnAcIn1 || hasAcSolarOnAcIn2
+ property bool hasAcSolarOnOut: sys.pvOnAcOut.power.valid
+ property bool hasAcSolar: hasAcSolarOnIn || hasAcSolarOnOut
+ property bool hasDcSolar: sys.pvCharger.power.valid
+ property bool hasDcAndAcSolar: hasAcSolar && hasDcSolar
+ property bool showDcAndAcSolar: hasDcAndAcSolar || showAllTiles
+ property bool showDcSolar: hasDcSolar || showAllTiles
+ property bool showAcSolar: hasAcSolar || showAllTiles
+////// ADDED to show tanks
+ property int bottomOffset: 45
+ property string settingsBindPreffix: "com.victronenergy.settings"
+ property string pumpBindPreffix: "com.victronenergy.pump.startstop0"
+ property int numberOfTemps: 0
+//////// added/modified for control show/hide gauges, tanks and temps from menus
+ property int tankCount: showTanksEnable ? tankModel.rowCount : 0
+ property int tempCount: showTempsEnable ? numberOfTemps : 0
+ property int tankTempCount: tankCount + tempCount
+ property bool showTanks: showTanksEnable ? showStatusBar ? false : tankCount > 0 ? true : false : false
+ property bool showTemps: showTempsEnable ? showStatusBar ? false : tempCount > 0 ? true : false : false
+ property bool showTanksTemps: showTanks || showTemps
+ property int compactThreshold: 45 // height below this will be compacted vertically
+ property bool compact: showTanks && showTemps && tankTempCount > 4
+ property int tanksHeight: compact ? 22 : 45
+
+ property int leftTileCount: (showAcInput ? 1 : 0) + (showAlternator ? 1 : 0) + (showAlternator ? 1 : 0)
+
+//////// add for PV CHARGER voltage and current
+ property string pvChargerPrefix1: ""
+ property string pvChargerPrefix2: ""
+ property string pvChargerPrefix3: ""
+ property string pvChargerPrefix4: ""
+ property string pvChargerPrefix5: ""
+ property string pvChargerPrefix6: ""
+ property string pvChargerPrefix7: ""
+ property int numberOfPvChargers: 0
+ property int pvChargerRows: showTanksTemps ? 4 : 7
+ property int pvRowsPerCharger: Math.max ( 1, Math.min (pvChargerRows / numberOfPvChargers, 3))
+ property bool pvChargerCompact: pvRowsPerCharger < 3 ? true : false
+ property bool pvShowDetails: pvRowsPerCharger >= 2 ? true : false
+
+//////// add for PV INVERTER power
+ property string pvInverterPrefix1: ""
+ property string pvInverterPrefix2: ""
+ property string pvInverterPrefix3: ""
+ property int numberOfPvInverters: 0
+
+//////// add for alternator - alternator replaces AC in if AC in is not present
+ property string alternatorPrefix1: ""
+ property string alternatorPrefix2: ""
+ property int numberOfAlternators: 0
+ VBusItem { id: alternatorName1; bind: Utils.path(alternatorPrefix1, "/CustomName") }
+ VBusItem { id: alternatorPower1; bind: Utils.path(alternatorPrefix1, "/Dc/0/Power") }
+ VBusItem { id: alternatorVoltage1; bind: Utils.path(alternatorPrefix1, "/Dc/0/Voltage") }
+ VBusItem { id: alternatorCurrent1; bind: Utils.path(alternatorPrefix1, "/Dc/0/Current") }
+ VBusItem { id: alternatorName2; bind: Utils.path(alternatorPrefix2, "/CustomName") }
+ VBusItem { id: alternatorPower2; bind: Utils.path(alternatorPrefix2, "/Dc/0/Power") }
+
+//////// added for control show/hide gauges, tanks and temps from menus
+ VBusItem { id: showGaugesItem; bind: Utils.path(guiModsPrefix, "/ShowGauges") }
+ property bool showGauges: showGaugesItem.valid ? showGaugesItem.value === 1 ? true : false : false
+ VBusItem { id: showTanksItem; bind: Utils.path(guiModsPrefix, "/ShowEnhancedFlowOverviewTanks") }
+ property bool showTanksEnable: showTanksItem.valid ? showTanksItem.value === 1 ? true : false : false
+ VBusItem { id: showTempsItem; bind: Utils.path(guiModsPrefix, "/ShowEnhancedFlowOverviewTemps") }
+ property bool showTempsEnable: showTempsItem.valid ? showTempsItem.value === 1 ? true : false : false
+
+//////// added to show/dim tiles
+ VBusItem { id: showInactiveTilesItem; bind: Utils.path(guiModsPrefix, "/ShowInactiveFlowTiles") }
+ property real disabledTileOpacity: (showInactiveTiles && showInactiveTilesItem.value === 1) ? 0.3 : 1
+ property bool showInactiveTiles: showInactiveTilesItem.valid && showInactiveTilesItem.value >= 1
+
+ VBusItem { id: showBatteryTempItem; bind: Utils.path(guiModsPrefix, "/ShowBatteryTempOnFlows") }
+ property bool showBatteryTemp: showBatteryTempItem.valid && showBatteryTempItem.value == 1
+
+ // for debug, ignore validity checks so all tiles and their flow lines will show
+ property bool showAllTiles: showInactiveTilesItem.valid && showInactiveTilesItem.value == 3
+
+//////// added to control time display
+ VBusItem { id: timeFormatItem; bind: Utils.path(guiModsPrefix, "/TimeFormat") }
+ property string timeFormat: getTimeFormat ()
+
+ function getTimeFormat ()
+ {
+ if (!timeFormatItem.valid || timeFormatItem.value === 0)
+ return ""
+ else if (timeFormatItem.value === 2)
+ return "h:mm ap"
+ else
+ return "hh:mm"
+ }
+
+//////// add to display individual PV charger power
+ VBusItem { id: pvName1; bind: Utils.path(pvChargerPrefix1, "/CustomName") }
+ VBusItem { id: pvPower1; bind: Utils.path(pvChargerPrefix1, "/Yield/Power") }
+ VBusItem { id: pvVoltage1; bind: Utils.path(pvChargerPrefix1, "/Pv/V") }
+ VBusItem { id: pvCurrent1; bind: Utils.path(pvChargerPrefix1, "/Pv/I") }
+ VBusItem { id: pv1NrTrackers; bind: Utils.path(pvChargerPrefix1, "/NrOfTrackers") }
+ VBusItem { id: pvName2; bind: Utils.path(pvChargerPrefix2, "/CustomName") }
+ VBusItem { id: pvPower2; bind: Utils.path(pvChargerPrefix2, "/Yield/Power") }
+ VBusItem { id: pvVoltage2; bind: Utils.path(pvChargerPrefix2, "/Pv/V") }
+ VBusItem { id: pvCurrent2; bind: Utils.path(pvChargerPrefix2, "/Pv/I") }
+ VBusItem { id: pv2NrTrackers; bind: Utils.path(pvChargerPrefix2, "/NrOfTrackers") }
+ VBusItem { id: pvName3; bind: Utils.path(pvChargerPrefix3, "/CustomName") }
+ VBusItem { id: pvPower3; bind: Utils.path(pvChargerPrefix3, "/Yield/Power") }
+ VBusItem { id: pvVoltage3; bind: Utils.path(pvChargerPrefix3, "/Pv/V") }
+ VBusItem { id: pvCurrent3; bind: Utils.path(pvChargerPrefix3, "/Pv/I") }
+ VBusItem { id: pv3NrTrackers; bind: Utils.path(pvChargerPrefix3, "/NrOfTrackers") }
+ VBusItem { id: pvName4; bind: Utils.path(pvChargerPrefix4, "/CustomName") }
+ VBusItem { id: pvPower4; bind: Utils.path(pvChargerPrefix4, "/Yield/Power") }
+ VBusItem { id: pvName5; bind: Utils.path(pvChargerPrefix5, "/CustomName") }
+ VBusItem { id: pvPower5; bind: Utils.path(pvChargerPrefix5, "/Yield/Power") }
+ VBusItem { id: pvName6; bind: Utils.path(pvChargerPrefix6, "/CustomName") }
+ VBusItem { id: pvPower6; bind: Utils.path(pvChargerPrefix6, "/Yield/Power") }
+ VBusItem { id: pvName7; bind: Utils.path(pvChargerPrefix7, "/CustomName") }
+ VBusItem { id: pvPower7; bind: Utils.path(pvChargerPrefix7, "/Yield/Power") }
+
+ VBusItem { id: timeToGo; bind: Utils.path("com.victronenergy.system","/Dc/Battery/TimeToGo") }
+
+//////// add to display PV Inverter power
+ VBusItem { id: pvInverterPower1; bind: Utils.path(pvInverterPrefix1, "/Ac/Power") }
+ VBusItem { id: pvInverterL1Power1; bind: Utils.path(pvInverterPrefix1, "/Ac/L1/Power") }
+ VBusItem { id: pvInverterL2Power1; bind: Utils.path(pvInverterPrefix1, "/Ac/L2/Power") }
+ VBusItem { id: pvInverterL3Power1; bind: Utils.path(pvInverterPrefix1, "/Ac/L3/Power") }
+ VBusItem { id: pvInverterName1; bind: Utils.path(pvInverterPrefix1, "/CustomName") }
+ VBusItem { id: pvInverterPower2; bind: Utils.path(pvInverterPrefix2, "/Ac/Power") }
+ VBusItem { id: pvInverterName2; bind: Utils.path(pvInverterPrefix2, "/CustomName") }
+ VBusItem { id: pvInverterPower3; bind: Utils.path(pvInverterPrefix3, "/Ac/Power") }
+ VBusItem { id: pvInverterName3; bind: Utils.path(pvInverterPrefix3, "/CustomName") }
+
+//////// add to display AC input ignored
+ VBusItem { id: ignoreAcInput1; bind: Utils.path(inverterService, "/Ac/State/IgnoreAcIn1") }
+ VBusItem { id: ignoreAcInput2; bind: Utils.path(inverterService, "/Ac/State/IgnoreAcIn2") }
+ VBusItem { id: acActiveInput; bind: Utils.path(inverterService, "/Ac/ActiveIn/ActiveInput") }
+
+ VBusItem { id: hasDcSystemItem; bind: "com.victronenergy.settings/Settings/SystemSetup/HasDcSystem" }
+
+ //Component.onCompleted: { discoverServices(); showHelp () }
+ onActiveChanged:
+ {
+ if (root.active)
+ {
+ discoverServices()
+ showHelp ()
+ }
+ }
+
+ title: qsTr("Simple Overview")
+
+ OverviewBox {
+ id: acInBox
+////// GuiMods — DarkMode
+ titleColor: !darkMode ? "#E74c3c" : "#73261E"
+ color: !darkMode ? "#C0392B" : "#601C15"
+ opacity: showAcInput ? 1 : disabledTileOpacity
+ visible: showAcInput || showInactiveTiles
+ width: 148
+ height: showStatusBar ? 100 : 120
+ title:
+ {
+ // input 1 is active
+ if (acActiveInput.value == 0)
+ {
+ if (ignoreAcInput1.valid && ignoreAcInput1.value == 1)
+ return qsTr ("AC In 1 Ignored")
+ else
+ return getAcSourceName(sys.acSource)
+ }
+ // input 2 is active
+ else if (acActiveInput.value == 1)
+ {
+ if (ignoreAcInput2.valid && ignoreAcInput2.value == 1)
+ return qsTr ("AC In 2 Ignored")
+ else
+ return getAcSourceName(sys.acSource)
+ }
+ else
+ return "no input"
+ }
+ anchors {
+ top: multi.top
+ left: parent.left; leftMargin: 10
+ }
+
+ values: OverviewAcValuesEnhanced {
+ connection: sys.acInput
+ }
+
+ MbIcon {
+ iconId: getAcSourceIcon(sys.acSource)
+ anchors {
+ bottom: parent.bottom
+ left: parent.left; leftMargin: 2
+ }
+ opacity: 0.5
+ }
+////// add power bar graph
+ PowerGauge
+ {
+ id: acInBar
+ width: parent.width
+ height: 12
+ anchors
+ {
+ top: parent.top; topMargin: 16
+ horizontalCenter: parent.horizontalCenter
+ }
+ connection: sys.acInput
+ useInputCurrentLimit: true
+ maxForwardPowerParameter: ""
+ maxReversePowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/MaxFeedInPower"
+ visible: showGauges && showAcInput
+ }
+ DetailTarget { id: acInputTarget; detailsPage: "DetailAcInput.qml" }
+ }
+
+
+ //// add alternator if AC input not present
+ OverviewBox {
+ id: alternatorBox
+ title: qsTr ("Alternator")
+ color: !darkMode ? "#157894" : "#0a3c4a"
+ titleColor: !darkMode ? "#419FB9" : "#204f5c"
+ opacity: showAlternator ? 1 : disabledTileOpacity
+ visible: showAlternator || showInactiveTiles && ! acInBox.visible
+ width: 148
+ height: showStatusBar ? 100 : 120
+ anchors.fill: acInBox
+ values: Column
+ {
+ width: parent.width
+ TileText
+ {
+ text: " "
+ font.pixelSize: 6
+ }
+ TileText
+ {
+ text: EnhFmt.formatVBusItem (sys.alternator.power, "W")
+ font.pixelSize: 19
+ }
+ TileText
+ {
+ text: alternatorName1.valid ? alternatorName1.value : "-"
+ visible: showAlternator && numberOfAlternators >= 1
+ }
+ TileText
+ {
+ text: EnhFmt.formatVBusItem (alternatorPower1, "W")
+ font.pixelSize: 15
+ visible: showAlternator && numberOfAlternators > 1
+ }
+ TileText {
+ text: EnhFmt.formatVBusItem (alternatorVoltage1, "V")
+ font.pixelSize: 15
+ visible: showAlternator && numberOfAlternators == 1
+ }
+ TileText {
+ text: EnhFmt.formatVBusItem (alternatorCurrent1, "A")
+ font.pixelSize: 15
+ visible: showAlternator && numberOfAlternators == 1
+ }
+ TileText
+ {
+ text: alternatorName2.valid ? alternatorName2.value : "-"
+ visible: showAlternator && numberOfAlternators >= 2
+ }
+ TileText
+ {
+ text: EnhFmt.formatVBusItem (alternatorPower1, "W")
+ font.pixelSize: 15
+ visible: showAlternator && numberOfAlternators >= 2
+ }
+ }
+
+ PowerGauge
+ {
+ id: alternatorBar
+ width: parent.width
+ height: 12
+ anchors
+ {
+ top: parent.top; topMargin: 16
+ horizontalCenter: parent.horizontalCenter
+ }
+ connection: sys.alternator
+ maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/MaxAlternatorPower"
+ visible: showGauges && showAlternator
+ }
+ DetailTarget { id: alternatorTarget; detailsPage: "DetailAlternator.qml" }
+ }
+
+ MultiEnhanced {
+ id: multi
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ top: parent.top; topMargin: 3
+ }
+ inverterService: root.inverterService
+ opacity: showInverter ? 1 : disabledTileOpacity
+ visible: showInverter || showInactiveTiles
+////// add power bar graph
+ PowerGaugeMulti
+ {
+ id: multiBar
+ width: multi.width
+ height: 12
+ anchors
+ {
+ top: parent.top; topMargin: 23
+ horizontalCenter: parent.horizontalCenter
+ }
+ inverterService: root.inverterService
+ visible: showGauges && showInverter
+ }
+ DetailTarget { id: multiTarget; detailsPage: "DetailInverter.qml"; width: 60; height: 60 }
+ }
+
+////// ADDED to show time inside inverter icon
+ Timer {
+ id: wallClock
+ running: timeFormat != ""
+ repeat: true
+ interval: 1000
+ triggeredOnStart: true
+ onTriggered: time = Qt.formatDateTime(new Date(), timeFormat)
+ property string time
+ }
+ TileText
+ {
+ text: wallClock.time
+ font.pixelSize: 18
+ color: showInverter || darkMode ? "white" : "black"
+ anchors
+ {
+ top: multi.top; topMargin: 96
+ horizontalCenter: multi.horizontalCenter
+ }
+ visible: wallClock.running
+ }
+
+ OverviewBox {
+ id: acLoadBox
+ visible: showAcLoads || showInactiveTiles
+ opacity: showAcLoads ? 1 : disabledTileOpacity
+ title: qsTr("AC Loads")
+////// GuiMods — DarkMode
+ color: !darkMode ? "#27AE60" : "#135730"
+ titleColor: !darkMode ? "#2ECC71" : "#176638"
+ width: 148
+ height: showStatusBar ? 80 : 102
+
+ anchors {
+ right: parent.right; rightMargin: 10
+ top: multi.top
+ }
+
+ values: OverviewAcValuesEnhanced {
+ connection: sys.acLoad
+ }
+////// add power bar graph
+ PowerGauge
+ {
+ id: acLoadBar
+ width: parent.width
+ height: 12
+ anchors
+ {
+ top: parent.top; topMargin: 16
+ horizontalCenter: parent.horizontalCenter
+ }
+ connection: sys.acLoad
+ maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/AcOutputMaxPower"
+ visible: showGauges && showAcLoads
+ }
+ DetailTarget { id: loadsOnOutputTarget; detailsPage: "DetailLoadsCombined.qml" }
+ }
+
+ Battery {
+ id: battery
+ width: acInBox.width
+ height: 96
+ anchors {
+ bottom: parent.bottom; bottomMargin: showTanksTemps ? bottomOffset + 3 : 5;
+ left: parent.left; leftMargin: 10
+ }
+ soc: sys.battery.soc.valid ? sys.battery.soc.value : 0
+////// add battery current bar graph
+ PowerGaugeBattery
+ {
+ id: batteryBar
+ width: parent.width
+ height: 10
+ anchors
+ {
+ top: parent.top; topMargin: 52
+ horizontalCenter: parent.horizontalCenter
+ }
+ visible: showGauges
+ }
+
+ values: Column {
+ width: parent.width
+
+ TileText {
+ text: sys.battery.soc.value === undefined ? "--" : sys.battery.soc.format (0)
+ font.pixelSize: 25
+ }
+ TileText {
+ text: EnhFmt.formatVBusItem (sys.battery.power, "W")
+ }
+ TileText {
+ text: " "
+ font.pixelSize: 6
+ }
+ TileText {
+ text: EnhFmt.formatVBusItem (sys.battery.voltage, "V ", 2)
+ + EnhFmt.formatVBusItem (sys.battery.current, "A")
+ }
+ TileText {
+ text: timeToGo.valid ? qsTr ("Remain: ") + TTG.formatTimeToGo (timeToGo) : qsTr (" ")
+ }
+ }
+ DetailTarget { id: batteryTarget; detailsPage: "DetailBattery.qml" }
+ }
+
+ VBusItem { id: dcSystemNameItem; bind: Utils.path(settingsBindPreffix, "/Settings/GuiMods/CustomDcSystemName") }
+
+ OverviewBox {
+ id: dcSystemBox
+////// wider to make room for current
+ width: multi.width + 20
+ height: 45
+ opacity: showDcSystem ? 1 : disabledTileOpacity
+ visible: showDcSystem || showInactiveTiles
+ title: dcSystemNameItem.valid && dcSystemNameItem.value != "" ? dcSystemNameItem.value : qsTr ("DC System")
+
+ anchors {
+ horizontalCenter: multi.horizontalCenter
+ horizontalCenterOffset: 2
+////// MODIFIED to show tanks
+ bottom: parent.bottom; bottomMargin: showTanksTemps ? bottomOffset + 3 : 5
+ }
+
+ values:
+ [
+ TileText
+ {
+ width: parent.width
+ anchors
+ {
+ horizontalCenter: parent.horizontalCenter
+ bottom: parent.bottom; bottomMargin: 0
+ }
+ ////// modified to show current
+ text:
+ {
+ if (showDcSystem)
+ {
+ var current = ""
+ if (sys.dcSystem.power.valid && sys.battery.voltage.valid)
+ current = " " + EnhFmt.formatValue (sys.dcSystem.power.value / sys.battery.voltage.value, "A")
+ return EnhFmt.formatVBusItem (sys.dcSystem.power) + current
+ }
+ else
+ return "--"
+ }
+ }
+ ]
+ PowerGauge
+ {
+ id: dcSystemGauge
+ width: parent.width
+ height: 8
+ anchors
+ {
+ top: parent.top; topMargin: 19
+ left: parent.left; leftMargin: 18
+ right: parent.right
+ }
+ connection: sys.dcSystem
+ maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/DcSystemMaxLoad"
+ maxReversePowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/DcSystemMaxCharge"
+ showLabels: true
+ visible: showGauges && showDcSystem
+
+ }
+ DetailTarget { id: dcSystemTarget; detailsPage: "DetailDcSystem.qml" }
+ }
+
+ property int pvOffset1: 27
+ property int pvRowSpacing: 16
+ property int pvOffset2: pvOffset1 + pvRowSpacing * pvRowsPerCharger
+ property int pvOffset3: pvOffset2 + pvRowSpacing * pvRowsPerCharger
+ property int pvOffset4: pvOffset3 + pvRowSpacing * pvRowsPerCharger
+ property int pvOffset5: pvOffset4 + pvRowSpacing * pvRowsPerCharger
+ property int pvOffset6: pvOffset5 + pvRowSpacing * pvRowsPerCharger
+ property int pvOffset7: pvOffset6 + pvRowSpacing * pvRowsPerCharger
+
+////// replaced OverviewSolarCharger with OverviewBox
+ OverviewBox {
+ id: pvChargerBox
+ title: qsTr("PV Charger")
+////// GuiMods — DarkMode
+ titleColor: !darkMode ? "#F4B350" : "#7A5928"
+ color: !darkMode ? "#F39C12" : "#794E09"
+ visible: hasDcSolar || showInactiveTiles
+ opacity: hasDcSolar ? 1 : disabledTileOpacity
+
+////// MODIFIED to show tanks & provide extra space if not
+ height:
+ {
+ var availableHeight = root.height - 3 - acLoadBox.height - 5 - (showTanksTemps ? bottomOffset + 3 : 5)
+ if (showDcAndAcSolar)
+ return ((availableHeight - 5) / 2) + 4
+ else if (showDcSolar)
+ return availableHeight
+ else
+ return 0
+ }
+ width: 148
+
+ anchors {
+ right: root.right; rightMargin: 10
+ bottom: parent.bottom; bottomMargin: showTanksTemps ? bottomOffset + 3 : 5
+ }
+
+////// moved sun icon here from OverviewSolarChager so it can be put below text, etc
+ MbIcon {
+ iconId: "overview-sun"
+ anchors {
+ bottom: parent.bottom
+ right: parent.right; rightMargin: 2
+ }
+ opacity: 0.5
+ visible: ! showDcAndAcSolar
+ }
+
+//////// modified to add power for individual PV charger info
+ values:
+ [
+ TileText {
+ y: 8
+ text: EnhFmt.formatVBusItem (sys.pvCharger.power)
+ font.pixelSize: 19
+ },
+ MarqueeEnhanced
+ {
+ y: pvOffset1
+ id: pv1Name
+ // ofset left margin for this row if showing tanks/temps
+ width:
+ {
+ if (pvChargerCompact)
+ {
+ if (showTanksTemps)
+ return ((parent.width / 2) - 15)
+ else
+ return ((parent.width / 2) - 5)
+ }
+ else
+ return (parent.width - 10)
+ }
+ anchors.left: parent.left; anchors.leftMargin: (showTanksTemps && pvChargerCompact) ? 15 : 5
+ height: 15
+ text: pvName1.valid ? pvName1.value : "pv 1"
+ textHorizontalAlignment: pvChargerCompact ? Text.AlignLeft : Text.AlignHCenter
+ fontSize: 15
+ Connections { target: scrollTimer; onTriggered: pv1Name.doScroll() }
+ scroll: false
+ visible: numberOfPvChargers >= 1 && ! showDcAndAcSolar
+ },
+ TileText {
+ y: pvOffset1 + (pvChargerCompact ? 0 : pvRowSpacing)
+ text: EnhFmt.formatVBusItem (pvPower1, "W")
+ horizontalAlignment: pvChargerCompact ? Text.AlignRight : Text.AlignHCenter
+ anchors.right: parent.right; anchors.rightMargin: 5
+ font.pixelSize: 15
+ visible: numberOfPvChargers >= 1 && ! showDcAndAcSolar
+ },
+ TileText {
+ y: pvOffset1 + pvRowSpacing * (pvChargerCompact ? 1 : 2)
+ text:
+ {
+ var voltageText, currentText
+ if (root.numberOfPvChargers < 1)
+ return " "
+ else
+ {
+ if (pv1NrTrackers.valid && pv1NrTrackers.value > 1)
+ return qsTr ("multiple trackers")
+ else if (pvVoltage1.valid)
+ voltageText = EnhFmt.formatVBusItem (pvVoltage1, "V")
+ else
+ voltageText = "??V"
+ if (pvCurrent1.valid)
+ currentText = EnhFmt.formatVBusItem (pvCurrent1, "A")
+ else if (pvPower1.valid)
+ currentText = EnhFmt.formatValue ((pvPower1.value / pvVoltage1.value), "A")
+ else
+ currentText = "??A"
+ return voltageText + " " + currentText
+ }
+ }
+ font.pixelSize: 15
+ visible: pvShowDetails && numberOfPvChargers >= 1
+ },
+ MarqueeEnhanced
+ {
+ y: pvOffset2
+ id: pv2Name
+ width: pvChargerCompact ? ((parent.width / 2) - 5) : parent.width - 10
+ anchors.left: parent.left; anchors.leftMargin: 5
+ height: 15
+ text: pvName2.valid ? pvName2.value : "pv 2"
+ textHorizontalAlignment: pvChargerCompact ? Text.AlignLeft : Text.AlignHCenter
+ fontSize: 15
+ Connections { target: scrollTimer; onTriggered: pv2Name.doScroll() }
+ scroll: false
+ visible: numberOfPvChargers >= 2 && ! showDcAndAcSolar
+ },
+ TileText {
+ y: pvOffset2 + (pvChargerCompact ? 0 : pvRowSpacing)
+ text: EnhFmt.formatVBusItem (pvPower2, "W")
+ horizontalAlignment: pvChargerCompact ? Text.AlignRight : Text.AlignHCenter
+ anchors.right: parent.right; anchors.rightMargin: 5
+ font.pixelSize: 15
+ visible: numberOfPvChargers >= 2 && ! showDcAndAcSolar
+ },
+ TileText {
+ y: pvOffset2 + pvRowSpacing * (pvChargerCompact ? 1 : 2)
+ text:
+ {
+ var voltageText, currentText
+ if (root.numberOfPvChargers < 2)
+ return " "
+ else
+ {
+ if (pv2NrTrackers.valid && pv2NrTrackers.value > 1)
+ return qsTr ("multiple trackers")
+ else if (pvVoltage2.valid)
+ voltageText = EnhFmt.formatVBusItem (pvVoltage2, "V")
+ else
+ voltageText = "??V"
+ if (pvCurrent2.valid)
+ currentText = EnhFmt.formatVBusItem (pvCurrent2, "A")
+ else if (pvPower2.valid)
+ currentText = EnhFmt.formatValue ((pvPower2.value / pvVoltage2.value), "A")
+ else
+ currentText = "??A"
+ return voltageText + " " + currentText
+ }
+ }
+ font.pixelSize: 15
+ visible: pvShowDetails && numberOfPvChargers >= 2
+ },
+ MarqueeEnhanced
+ {
+ y: pvOffset3
+ id: pv3Name
+ width: pvChargerCompact ? ((parent.width / 2) - 5) : parent.width - 10
+ anchors.left: parent.left; anchors.leftMargin: 5
+ height: 15
+ text: pvName3.valid ? pvName3.value : "pv 3"
+ textHorizontalAlignment: pvChargerCompact ? Text.AlignLeft : Text.AlignHCenter
+ fontSize: 15
+ Connections { target: scrollTimer; onTriggered: pv3Name.doScroll() }
+ scroll: false
+ visible: numberOfPvChargers >= 3 && ! showDcAndAcSolar
+ },
+ TileText {
+ y: pvOffset3 + (pvChargerCompact ? 0 : pvRowSpacing)
+ text: EnhFmt.formatVBusItem (pvPower3, "W")
+ anchors.right: parent.right; anchors.rightMargin: 5
+ horizontalAlignment: pvChargerCompact ? Text.AlignRight : Text.AlignHCenter
+ font.pixelSize: 15
+ visible: numberOfPvChargers >= 3 && ! showDcAndAcSolar
+ },
+ TileText {
+ y: pvOffset3 + pvRowSpacing * (pvChargerCompact ? 1 : 2)
+ text:
+ {
+ var voltageText, currentText
+ if (root.numberOfPvChargers < 3)
+ return " "
+ else
+ {
+ if (pv3NrTrackers.valid && pv3NrTrackers.value > 1)
+ return qsTr ("multiple trackers")
+ else if (pvVoltage3.valid)
+ voltageText = EnhFmt.formatVBusItem (pvVoltage3, "V")
+ else
+ voltageText = "??V"
+ if (pvCurrent3.valid)
+ currentText = EnhFmt.formatVBusItem (pvCurrent3, "A")
+ else if (pvPower3.valid)
+ currentText = EnhFmt.formatValue ((pvPower3.value / pvVoltage3.value), "A")
+ else
+ currentText = "??A"
+ return voltageText + " " + currentText
+ }
+ }
+ font.pixelSize: 15
+ visible: pvShowDetails && numberOfPvChargers >= 2
+ },
+ MarqueeEnhanced
+ {
+ y: pvOffset4
+ id: pv4Name
+ // ofset left margin for this row if NOT showing tanks/temps
+ width:
+ {
+ if (pvChargerCompact)
+ {
+ if (! showTanksTemps)
+ return ((parent.width / 2) - 15)
+ else
+ return ((parent.width / 2) - 5)
+ }
+ else
+ return (parent.width - 10)
+ }
+ anchors.left: parent.left; anchors.leftMargin: ( ! showTanksTemps && pvChargerCompact) ? 15 : 5
+ height: 15
+ text: pvName4.valid ? pvName4.value : "pv 4"
+ textHorizontalAlignment: pvChargerCompact ? Text.AlignLeft : Text.AlignHCenter
+ fontSize: 15
+ Connections { target: scrollTimer; onTriggered: pv4Name.doScroll() }
+ scroll: false
+ visible: numberOfPvChargers >= 4 && ! showDcAndAcSolar
+ },
+ TileText {
+ y: pvOffset4 + (pvChargerCompact ? 0 : pvRowSpacing)
+ text: EnhFmt.formatVBusItem (pvPower4, "W")
+ anchors.right: parent.right; anchors.rightMargin: 5
+ horizontalAlignment: pvChargerCompact ? Text.AlignRight : Text.AlignHCenter
+ font.pixelSize: 15
+ visible: numberOfPvChargers >= 4 && ! showDcAndAcSolar
+ },
+ MarqueeEnhanced
+ {
+ y: pvOffset5
+ id: pv5Name
+ width: pvChargerCompact ? ((parent.width / 2) - 5) : parent.width - 10
+ anchors.left: parent.left; anchors.leftMargin: 5
+ height: 15
+ text: pvName5.valid ? pvName5.value : "pv 5"
+ textHorizontalAlignment: pvChargerCompact ? Text.AlignLeft : Text.AlignHCenter
+ fontSize: 15
+ Connections { target: scrollTimer; onTriggered: pv5Name.doScroll() }
+ scroll: false
+ visible: numberOfPvChargers >= 5 && pvChargerRows >= 5 && ! showDcAndAcSolar
+ },
+ TileText {
+ y: pvOffset5 + (pvChargerCompact ? 0 : pvRowSpacing)
+ text: EnhFmt.formatVBusItem (pvPower5, "W")
+ anchors.right: parent.right; anchors.rightMargin: 5
+ horizontalAlignment: pvChargerCompact ? Text.AlignRight : Text.AlignHCenter
+ font.pixelSize: 15
+ visible: numberOfPvChargers >= 5 && pvChargerRows >= 5 && ! showDcAndAcSolar
+ },
+ MarqueeEnhanced
+ {
+ y: pvOffset6
+ id: pv6Name
+ width: pvChargerCompact ? ((parent.width / 2) - 5) : parent.width - 10
+ anchors.left: parent.left; anchors.leftMargin: 5
+ height: 15
+ text: pvName6.valid ? pvName6.value : "pv 6"
+ textHorizontalAlignment: pvChargerCompact ? Text.AlignLeft : Text.AlignHCenter
+ fontSize: 15
+ Connections { target: scrollTimer; onTriggered: pv6Name.doScroll() }
+ scroll: false
+ visible: numberOfPvChargers >= 6 && pvChargerRows >= 6 && ! showDcAndAcSolar
+ },
+ TileText {
+ y: pvOffset6 + (pvChargerCompact ? 0 : pvRowSpacing)
+ text: EnhFmt.formatVBusItem (pvPower6, "W")
+ anchors.right: parent.right; anchors.rightMargin: 5
+ horizontalAlignment: pvChargerCompact ? Text.AlignRight : Text.AlignHCenter
+ font.pixelSize: 15
+ visible: numberOfPvChargers >= 6 && pvChargerRows >= 6 && ! showDcAndAcSolar
+ },
+ MarqueeEnhanced
+ {
+ y: pvOffset7
+ id: pv7Name
+ width: pvChargerCompact ? ((parent.width / 2) - 5) : parent.width - 10
+ anchors.left: parent.left; anchors.leftMargin: 5
+ height: 15
+ text: pvName7.valid ? pvName7.value : "pv 7"
+ textHorizontalAlignment: pvChargerCompact ? Text.AlignLeft : Text.AlignHCenter
+ fontSize: 15
+ Connections { target: scrollTimer; onTriggered: pv6Name.doScroll() }
+ scroll: false
+ visible: numberOfPvChargers >= 7 && pvChargerRows >= 7 && ! showDcAndAcSolar
+ },
+ TileText {
+ y: pvOffset7 + (pvChargerCompact ? 0 : pvRowSpacing)
+ text: EnhFmt.formatVBusItem (pvPower7, "W")
+ anchors.right: parent.right; anchors.rightMargin: 5
+ horizontalAlignment: pvChargerCompact ? Text.AlignRight : Text.AlignHCenter
+ font.pixelSize: 15
+ visible: numberOfPvChargers >= 7 && pvChargerRows >= 7 && ! showDcAndAcSolar
+ }
+ ]
+////// add power bar graph
+ PowerGauge
+ {
+ id: pvChargerBar
+ width: parent.width - (showDcAndAcSolar && ! showTanksTemps ? 20 : 0)
+ height: 10
+ anchors
+ {
+ top: parent.top; topMargin: 19
+ right: parent.right; rightMargin: 0.5
+ }
+ connection: sys.pvCharger
+ maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/PvChargerMaxPower"
+ visible: showGauges && showDcSolar
+ }
+ DetailTarget { id: pvChargerTarget; detailsPage: "DetailPvCharger.qml" }
+ }
+
+////// replaced OverviewSolarInverter with OverviewBox
+ OverviewBox {
+ id: pvInverter
+ title: qsTr("PV Inverter")
+////// GuiMods — DarkMode
+ titleColor: !darkMode ? "#F4B350" : "#7A5928"
+ color: !darkMode ? "#F39C12" : "#794E09"
+ visible: hasAcSolar || showInactiveTiles
+ opacity: hasAcSolar ? 1 : disabledTileOpacity
+
+////// MODIFIED to show tanks & provide extra space if not
+ height:
+ {
+ var availableHeight = root.height - 3 - acLoadBox.height -5
+ availableHeight -= (showTanksTemps ? bottomOffset + 3 : 5)
+ if (showDcAndAcSolar)
+ availableHeight -= pvChargerBox.height + 5
+ if (showAcSolar)
+ return availableHeight
+ else
+ return 0
+ }
+ width: 148
+
+ anchors {
+ right: root.right; rightMargin: 10;
+ bottom: showDcAndAcSolar ? pvChargerBox.top : root.bottom
+ bottomMargin: showDcAndAcSolar ? 5 : showTanksTemps ? bottomOffset + 3 : 5
+ }
+
+ values:
+ [
+ TileText {
+ id: coupledPvAc
+
+ property double pvInverterOnAcOut: sys.pvOnAcOut.power.valid ? sys.pvOnAcOut.power.value : 0
+ property double pvInverterOnAcIn1: sys.pvOnAcIn1.power.valid ? sys.pvOnAcIn1.power.value : 0
+ property double pvInverterOnAcIn2: sys.pvOnAcIn2.power.valid ? sys.pvOnAcIn2.power.value : 0
+ property bool powerValid: sys.pvOnAcOut.power.valid || sys.pvOnAcIn1.power.valid || sys.pvOnAcIn2.power.valid
+
+ y: 10
+ text: powerValid ? EnhFmt.formatValue (pvInverterOnAcOut + pvInverterOnAcIn1 + pvInverterOnAcIn2, "W") : ""
+ font.pixelSize: 19
+ visible: showAcSolar
+ },
+//////// add individual PV inverter powers
+ TileText {
+ y: 31
+ text: pvInverterName1.valid ? pvInverterName1.value : "-"
+ visible: !showDcAndAcSolar && numberOfPvInverters >= 2
+ },
+ TileText {
+ y: 47
+ text: EnhFmt.formatVBusItem (pvInverterPower1, "W")
+ font.pixelSize: 15
+ visible: !showDcAndAcSolar && numberOfPvInverters >= 2
+ },
+ TileText {
+ y: 63
+ text: pvInverterName2.valid ? pvInverterName2.value : "-"
+ visible: !showDcAndAcSolar && numberOfPvInverters >= 2
+ },
+ TileText {
+ y: 77
+ text: EnhFmt.formatVBusItem (pvInverterPower2, "W")
+ font.pixelSize: 15
+ visible: !showDcAndAcSolar && numberOfPvInverters >= 2
+ },
+ TileText {
+ y: 93
+ text: pvInverterName3.valid ? pvInverterName3.value : "-"
+ visible: !showDcAndAcSolar && numberOfPvInverters >=3 && ! showTanksTemps
+ },
+ TileText {
+ y: 107
+ text: EnhFmt.formatVBusItem (pvInverterPower3, "W")
+ font.pixelSize: 15
+ visible: !showDcAndAcSolar && numberOfPvInverters >=3 && ! showTanksTemps
+ },
+ TileText {
+ y: 31
+ text: qsTr ("L1: ") + EnhFmt.formatVBusItem (pvInverterL1Power1, "W")
+ visible: !showDcAndAcSolar && numberOfPvInverters == 1 && pvInverterL1Power1.valid && (pvInverterL2Power1.valid || pvInverterL3Power1.valid)
+ },
+ TileText {
+ y: 47
+ text: qsTr ("L2: ") + EnhFmt.formatVBusItem (pvInverterL2Power1, "W")
+ visible: !showDcAndAcSolar && numberOfPvInverters == 1 && pvInverterL2Power1.valid
+ },
+ TileText {
+ y: 63
+ text: qsTr ("L3: ") + EnhFmt.formatVBusItem (pvInverterL3Power1, "W")
+ visible: !showDcAndAcSolar && numberOfPvInverters == 1 && pvInverterL3Power1.valid
+ }
+ ]
+////// add power bar graph
+ PowerGauge
+ {
+ id: pvInverterBar
+ width: parent.width
+ height: 12
+ anchors
+ {
+ top: parent.top; topMargin: 19
+ horizontalCenter: parent.horizontalCenter
+ }
+ maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/PvOnOutputMaxPower"
+ maxForwardPowerParameter2: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/PvOnGridMaxPower"
+ connection: sys.pvOnAcOut
+ connection2: sys.pvOnGrid
+ visible: showGauges && showAcSolar
+ }
+ DetailTarget { id: pvInverterTarget; detailsPage: "DetailPvInverter.qml" }
+ }
+
+ OverviewConnection {
+ id: acInToMulti
+ ballCount: 2
+ path: straight
+ active: root.active && showAcInput && showInverter
+ value: flow(sys.acInput ? sys.acInput.power : 0)
+
+ anchors {
+ left: acInBox.right; leftMargin: -10
+ right: multi.left; rightMargin: -10; bottom: acInBox.bottom; bottomMargin: 25
+ }
+ }
+
+ OverviewConnection {
+ id: multiToAcLoads
+ ballCount: 2
+ path: straight
+ active: root.active && ( showAcLoads && showInverter )
+ value: flow(sys.acLoad.power)
+
+ anchors {
+ left: multi.right; leftMargin: -10;
+ right: acLoadBox.left; rightMargin: -10
+ bottom: acLoadBox.bottom; bottomMargin: 8
+ }
+ }
+
+ OverviewConnection {
+ id: pvInverterToMulti
+ ballCount: 3
+ path: corner
+ active: root.active && showAcSolar && showInverter
+ value: Utils.sign(noNoise(sys.pvOnAcOut.power) + noNoise(sys.pvOnAcIn1.power) + noNoise(sys.pvOnAcIn2.power))
+
+ anchors {
+ left: pvInverter.left; leftMargin: 8
+ top: pvInverter.verticalCenter; topMargin: showDcAndAcSolar ? 10 : 0
+ right: multi.horizontalCenter; rightMargin: -20
+ bottom: multi.bottom; bottomMargin: 10
+ }
+ }
+
+ // invisible anchor point to connect the chargers to the battery
+ Item {
+ id: dcConnect
+ anchors {
+ left: multi.horizontalCenter; leftMargin: showAcSolar ? -20 : 0
+ bottom: dcSystemBox.top; bottomMargin: showDcAndAcSolar ? 7 : 10
+ }
+ }
+
+ OverviewConnection
+ {
+ id: dcBus2
+ ballCount: 2
+ path: straight
+ active: root.active && ( showInverter || showDcSolar )
+ value: -Utils.sign (noNoise (sys.pvCharger.power) + noNoise (sys.inverterChargerDc.power))
+ startPointVisible: false
+ endPointVisible: false
+
+ anchors {
+ right: dcConnect.left
+ top: dcConnect.top
+
+ left: multi.left; leftMargin: -10
+ bottom: dcConnect.top
+ }
+ }
+
+ OverviewConnection
+ {
+ id: alternatorToDcBus2
+ ballCount: 3
+ path: corner
+ active: root.active && showAlternator
+ value: Utils.sign (alternatorFlow)
+ endPointVisible: false
+ anchors
+ {
+ left: alternatorBox.right; leftMargin: -10
+ top: alternatorBox.bottom; topMargin: -15
+
+ right: dcBus2.left
+ bottom: dcBus2.bottom
+ }
+ }
+
+ OverviewConnection {
+ id: multiToDcConnect
+ ballCount: showTanksTemps ? 2 : 4
+ path: straight
+ active: root.active && showInverter
+ value: -flow(sys.inverterChargerDc.power);
+ startPointVisible: false
+
+ anchors {
+ left: dcConnect.left
+ top: dcConnect.top
+
+ right: dcConnect.left
+ bottom: multi.bottom; bottomMargin: 10
+ }
+ }
+
+ OverviewConnection {
+ id: pvChargerBoxDcConnect
+ ballCount: 3
+ path: straight
+ active: root.active && showDcSolar
+ value: -flow(sys.pvCharger.power)
+ startPointVisible: false
+
+ anchors {
+ left: dcConnect.left
+ top: dcConnect.top
+
+ right: pvChargerBox.left; rightMargin: -8
+ bottom: dcConnect.top;
+ }
+ }
+
+ OverviewConnection {
+ id: batteryToDcBus2
+ ballCount: 1
+ path: straight
+ active: root.active && ( showInverter || showDcSolar )
+ value: Utils.sign(noNoise(sys.pvCharger.power) + noNoise(sys.inverterChargerDc.power) + alternatorFlow)
+ startPointVisible: false
+
+ anchors {
+ left: dcBus2.left
+ top: dcBus2.top
+
+ right: battery.right; rightMargin: 10
+ bottom: dcBus2.top
+ }
+ }
+
+ OverviewConnection {
+ id: batteryToDcSystem
+ ballCount: 2
+ path: straight
+ active: root.active && showDcSystem
+ value: flow(sys.dcSystem.power)
+
+ anchors {
+ left: battery.right; leftMargin: -10
+ top: dcSystemBox.verticalCenter;
+ right: dcSystemBox.left; rightMargin: -10
+ bottom: dcSystemBox.verticalCenter
+ }
+ }
+////// moved order so it covers connections
+////// moved to under Multi
+ OverviewEssReason {
+ anchors {
+ top: multi.bottom; topMargin: 7
+ horizontalCenter: parent.horizontalCenter
+ }
+ }
+
+////// ADDED to show tanks & temps
+ // Synchronise tank name text scroll start and PV Charger name scroll
+ Timer
+ {
+ id: scrollTimer
+ interval: 15000
+ repeat: true
+ running: root.active
+ }
+ ListView
+ {
+ id: tanksColum
+
+ visible: showTanks
+ width: compact ? root.width : root.width * tankCount / tankTempCount
+ property int tileWidth: width / Math.min (count, 5.2)
+ height: root.tanksHeight
+ anchors
+ {
+ bottom: root.bottom
+ left: root.left
+ }
+
+ // flickable list if more than will fit across bottom of screen
+ interactive: count > 4 ? true : false
+ orientation: ListView.Horizontal
+
+ model: TankModel { id: tankModel }
+ delegate: TileTankEnhanced {
+ // Without an intermediate assignment this will trigger a binding loop warning.
+ property variant theService: DBusServices.get(buddy.id)
+ service: theService
+ width: tanksColum.tileWidth
+ height: root.tanksHeight
+ pumpBindPrefix: root.pumpBindPreffix
+ compact: root.compact
+ Connections {
+ target: scrollTimer
+ onTriggered: doScroll()
+ }
+ }
+ Tile {
+ title: qsTr("TANKS")
+ anchors.fill: parent
+ values: TileText {
+ text: qsTr("")
+ width: parent.width
+ wrapMode: Text.WordWrap
+ }
+ z: -1
+ }
+ }
+
+ ListView
+ {
+ id: tempsColumn
+
+ visible: showTemps
+ width: compact ? root.width : root.width * tempCount / tankTempCount
+ property int tileWidth: width / Math.min (count, 5.2)
+ height: root.tanksHeight
+ anchors
+ {
+ bottom: root.bottom
+ bottomMargin: compact ? root.tanksHeight : 0
+ right: root.right
+ }
+
+ // make list flickable if more tiles than will fit completely
+ interactive: count > 4 ? true : false
+ orientation: ListView.Horizontal
+
+ model: tempsModel
+ delegate: TileTemp
+ {
+ width: tempsColumn.tileWidth
+ height: tempsColumn.height
+ compact: root.compact
+ Connections
+ {
+ target: scrollTimer
+ onTriggered: doScroll()
+ }
+ }
+ Tile
+ {
+ title: qsTr("TEMPS")
+ anchors.fill: parent
+ values: TileText
+ {
+ text: qsTr("")
+ width: parent.width
+ wrapMode: Text.WordWrap
+ }
+ z: -1
+ }
+ }
+ ListModel { id: tempsModel }
+
+ // When new service is found add resources as appropriate
+ Connections
+ {
+ target: DBusServices
+ onDbusServiceFound: addService(service)
+ }
+
+ // hack to get value(s) from within a loop inside a function when service is changing
+ property string tempServiceName: ""
+ property VBusItem temperatureItem: VBusItem { bind: Utils.path(tempServiceName, "/Dc/0/Temperature") }
+
+ function addService(service)
+ {
+ switch (service.type)
+ {
+//////// add for temp sensors
+ case DBusService.DBUS_SERVICE_TEMPERATURE_SENSOR:
+ numberOfTemps++
+ tempsModel.append({serviceName: service.name})
+ break;;
+ case DBusService.DBUS_SERVICE_MULTI:
+ hasInverter = true
+ root.tempServiceName = service.name
+ if (temperatureItem.valid && showBatteryTemp)
+ {
+ numberOfTemps++
+ tempsModel.append({serviceName: service.name})
+ }
+ break;;
+//////// add for VE.Direct inverters
+ case DBusService.DBUS_SERVICE_INVERTER:
+ hasInverter = true
+ if (veDirectInverterService == "")
+ veDirectInverterService = service.name;
+ break;;
+
+//////// add for PV CHARGER voltage and current display
+ case DBusService.DBUS_SERVICE_SOLAR_CHARGER:
+ case DBusService.DBUS_SERVICE_MULTI_RS:
+ if ( service.type == DBusService.DBUS_SERVICE_MULTI_RS )
+ hasInverter = true
+ numberOfPvChargers++
+ if (numberOfPvChargers === 1)
+ pvChargerPrefix1 = service.name;
+ else if (numberOfPvChargers === 2)
+ pvChargerPrefix2 = service.name;
+ else if (numberOfPvChargers === 3)
+ pvChargerPrefix3 = service.name;
+ else if (numberOfPvChargers === 4)
+ pvChargerPrefix4 = service.name;
+ else if (numberOfPvChargers === 5)
+ pvChargerPrefix5 = service.name;
+ else if (numberOfPvChargers === 6)
+ pvChargerPrefix6 = service.name;
+ else if (numberOfPvChargers === 7)
+ pvChargerPrefix7 = service.name;
+ break;;
+
+//////// add for PV INVERTER power display
+ case DBusService.DBUS_SERVICE_PV_INVERTER:
+ numberOfPvInverters++
+ if (numberOfPvInverters === 1)
+ pvInverterPrefix1 = service.name;
+ else if (numberOfPvInverters === 2)
+ pvInverterPrefix2 = service.name;
+ else if (numberOfPvInverters === 3)
+ pvInverterPrefix3 = service.name;
+ break;;
+ case DBusService.DBUS_SERVICE_BATTERY:
+ root.tempServiceName = service.name
+ if (temperatureItem.valid && showBatteryTemp)
+ {
+ numberOfTemps++
+ tempsModel.append({serviceName: service.name})
+ }
+ break;;
+//////// add for alternator
+ case DBusService.DBUS_SERVICE_ALTERNATOR:
+ numberOfAlternators++
+ if (numberOfAlternators === 1)
+ alternatorPrefix1 = service.name;
+ else if (numberOfAlternators === 2)
+ alternatorPrefix2 = service.name;
+ break;;
+ }
+ }
+
+ // Detect available services of interest
+ function discoverServices()
+ {
+ numberOfTemps = 0
+ numberOfPvChargers = 0
+ numberOfPvInverters = 0
+ numberOfAlternators = 0
+ veDirectInverterService = ""
+ hasInverter = false
+ pvChargerPrefix1 = ""
+ pvChargerPrefix2 = ""
+ pvChargerPrefix3 = ""
+ pvChargerPrefix4 = ""
+ pvChargerPrefix5 = ""
+ pvChargerPrefix6 = ""
+ pvChargerPrefix7 = ""
+ pvInverterPrefix1 = ""
+ pvInverterPrefix2 = ""
+ pvInverterPrefix3 = ""
+ alternatorPrefix1 = ""
+ alternatorPrefix2 = ""
+ tempsModel.clear()
+ for (var i = 0; i < DBusServices.count; i++)
+ {
+ addService(DBusServices.at(i))
+ }
+ }
+
+// Details targets
+
+ // help message shown when menu is first drawn
+ Rectangle
+ {
+ id: helpBox
+ color: "white"
+ width: multi.width
+ height: 32
+////// GuiMods — DarkMode
+ opacity: !darkMode ? 0.7 : 0.85
+ anchors
+ {
+ top: multi.bottom; topMargin: 1
+ horizontalCenter: root.horizontalCenter
+ }
+ visible: false
+ TileText
+ {
+ text: qsTr ( "Tap tile center for detail at any time" )
+ color: "black"
+ anchors.fill: helpBox
+ wrapMode: Text.WordWrap
+ font.pixelSize: 12
+ visible: parent.visible
+ }
+ }
+
+ //// hard key handler
+ // used to press buttons when touch isn't available
+ // UP and DOWN buttons cycle through the list of touch areas
+ // "space" button is used to simulate a touch on the area
+ // target must be highlighted so that other uses of "space"
+ // will still occur
+
+ // list of all details touchable areas
+ property variant targetList:
+ [
+ acInputTarget, alternatorTarget, batteryTarget,
+ multiTarget, dcSystemTarget,
+ loadsOnOutputTarget, pvInverterTarget, pvChargerTarget
+ ]
+
+ property int selectedTarget: 0
+
+ Timer
+ {
+ id: targetTimer
+ interval: 5000
+ repeat: false
+ running: false
+ onTriggered: { hideAllTargets () }
+ }
+
+ Keys.forwardTo: [keyHandler]
+ Item
+ {
+ id: keyHandler
+ Keys.onUpPressed:
+ {
+ nextTarget (-1)
+ event.accepted = true
+ }
+
+ Keys.onDownPressed:
+ {
+ nextTarget (+1)
+ event.accepted = true
+ }
+ Keys.onSpacePressed:
+ {
+ if (targetTimer.running)
+ {
+ var foo // hack to make clicked() work
+ bar.clicked (foo)
+ event.accepted = true
+ }
+ else
+ event.accepted = false
+ }
+ }
+ // hack to make clicked() work
+ property variant bar: targetList[selectedTarget]
+
+ function nextTarget (increment)
+ {
+ // make one pass through all possible targets to find an enabled one
+ // if found, that's the new selectedTarget,
+ // if not selectedTarget does not change
+ var newIndex = selectedTarget
+ for (var i = 0; i < targetList.length; i++)
+ {
+ if (( ! targetTimer.running || helpBox.visible) && targetList[newIndex].enabled)
+ {
+ highlightSelectedTarget ()
+ return
+ }
+ newIndex += increment
+ if (newIndex >= targetList.length)
+ newIndex = 0
+ else if (newIndex < 0)
+ newIndex = targetList.length - 1
+ if (targetList[newIndex].enabled)
+ {
+ selectedTarget = newIndex
+ highlightSelectedTarget ()
+ break
+ }
+ }
+ }
+
+ function showHelp ()
+ {
+ for (var i = 0; i < targetList.length; i++)
+ {
+ targetList[i].targetVisible = true
+ }
+ helpBox.visible = true
+ targetTimer.restart ()
+ }
+ function hideAllTargets ()
+ {
+ for (var i = 0; i < targetList.length; i++)
+ {
+ targetList[i].targetVisible = false
+ }
+ helpBox.visible = false
+ }
+ function highlightSelectedTarget ()
+ {
+ for (var i = 0; i < targetList.length; i++)
+ {
+ if (targetList[i] == targetList[selectedTarget])
+ targetList[i].targetVisible = true
+ else
+ targetList[i].targetVisible = false
+ }
+ helpBox.visible = false
+ targetTimer.restart ()
+ }
+}
diff --git a/FileSets/v3.60~25/OverviewHubEnhanced.qml.orig b/FileSets/v3.60~25/OverviewHubEnhanced.qml.orig
new file mode 100644
index 00000000..d6b85835
--- /dev/null
+++ b/FileSets/v3.60~25/OverviewHubEnhanced.qml.orig
@@ -0,0 +1,311 @@
+import QtQuick 2
+import "utils.js" as Utils
+
+OverviewPage {
+ id: root
+
+ property variant sys: theSystem
+ property bool hasAcSolarOnAcIn1: sys.pvOnAcIn1.power.valid
+ property bool hasAcSolarOnAcIn2: sys.pvOnAcIn2.power.valid
+ property bool hasAcSolarOnIn: hasAcSolarOnAcIn1 || hasAcSolarOnAcIn2
+ property bool hasAcSolarOnOut: sys.pvOnAcOut.power.valid
+ property bool hasAcSolar: hasAcSolarOnIn || hasAcSolarOnOut
+ property bool hasDcSolar: sys.pvCharger.power.valid
+ property bool hasDcAndAcSolar: hasAcSolar && hasDcSolar
+
+ title: qsTr("Overview")
+
+ OverviewBox {
+ id: acInBox
+
+ width: 148
+ height: showStatusBar ? 100 : 120
+ title: getAcSourceName(sys.acSource)
+ titleColor: "#E74c3c"
+ color: "#C0392B"
+
+ anchors {
+ top: multi.top
+ left: parent.left; leftMargin: 10
+ }
+
+ values: OverviewAcValues {
+ connection: sys.acInput
+ }
+
+ MbIcon {
+ iconId: getAcSourceIcon(sys.acSource)
+ anchors {
+ bottom: parent.bottom
+ left: parent.left; leftMargin: 2
+ }
+ opacity: 0.5
+ }
+ }
+
+ Multi {
+ id: multi
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ top: parent.top; topMargin: 5
+ }
+ }
+
+ OverviewBox {
+ id: acLoadBox
+ title: qsTr("AC Loads")
+ color: "#27AE60"
+ titleColor: "#2ECC71"
+ width: 148
+ height: showStatusBar ? 100 : 120
+
+ anchors {
+ right: parent.right; rightMargin: 10
+ top: multi.top
+ }
+
+ values: OverviewAcValues {
+ connection: sys.acLoad
+ }
+ }
+
+ Battery {
+ id: battery
+
+ soc: sys.battery.soc.valid ? sys.battery.soc.value : 0
+ preferRenewable: sys.preferRenewableEnergy.valid
+ preferRenewableOverride: sys.preferRenewableEnergy.value === 0 || sys.preferRenewableEnergy.value === 2
+ preferRenewableOverrideGenset: sys.remoteGeneratorSelected.value === 1 || sys.acSource.value === 2
+ anchors {
+ bottom: parent.bottom; bottomMargin: 5;
+ left: parent.left; leftMargin: 10
+ }
+ values: Column {
+ width: parent.width
+
+ TileText {
+ // Use value here instead of format() because format adds the unit to the number and we
+ // show the percentage symbol in a separated smaller text.
+ text: sys.battery.soc.value === undefined ? "--" : sys.battery.soc.value.toFixed(0)
+ font.pixelSize: 40
+
+ Text {
+ anchors {
+ bottom: parent.bottom; bottomMargin: 9
+ horizontalCenter: parent.horizontalCenter; horizontalCenterOffset: parent.paintedWidth / 2 + 5
+ }
+ visible: sys.battery.soc.valid
+ text: "%"
+ color: "white"
+ font.bold: true
+ font.pixelSize: 12
+ }
+ }
+ TileText {
+ text: sys.battery.power.format(0)
+ }
+ TileText {
+ text: sys.battery.voltage.format(1) + " " + sys.battery.current.format(1)
+ }
+ }
+ }
+
+ OverviewBox {
+ id: dcSystemBox
+ width: 105
+ height: 45
+ visible: sys.dcSystem.power.valid
+ title: qsTr("DC Power")
+
+ anchors {
+ horizontalCenter: multi.horizontalCenter
+ bottom: parent.bottom; bottomMargin: 5
+ }
+
+ values: TileText {
+ anchors.centerIn: parent
+ text: sys.dcSystem.power.format(0)
+ }
+ }
+
+ OverviewSolarCharger {
+ id: blueSolarCharger
+
+ height: hasDcAndAcSolar ? 65 : 114
+ width: 148
+ title: qsTr("PV Charger")
+ showChargerIcon: !hasDcAndAcSolar
+ visible: hasDcSolar || hasDcAndAcSolar
+
+ anchors {
+ right: root.right; rightMargin: 10
+ bottom: root.bottom; bottomMargin: 5;
+ }
+
+ values: TileText {
+ y: 5
+ text: sys.pvCharger.power.format(0)
+ font.pixelSize: 20
+ }
+ }
+
+ OverviewSolarInverter {
+ id: pvInverter
+ height: hasDcAndAcSolar ? 65 : 115
+ width: 148
+ title: qsTr("PV Inverter")
+ showInverterIcon: !hasDcAndAcSolar
+ visible: hasAcSolar
+
+ anchors {
+ right: root.right; rightMargin: 10;
+ bottom: root.bottom; bottomMargin: hasDcAndAcSolar ? 75 : 5
+ }
+
+ OverviewAcValues {
+ connection: hasAcSolarOnOut ? sys.pvOnAcOut : hasAcSolarOnAcIn1 ? sys.pvOnAcIn1 : sys.pvOnAcIn2
+ visible: !coupledPvAc.visible
+ }
+
+ TileText {
+ id: coupledPvAc
+
+ property double pvInverterOnAcOut: sys.pvOnAcOut.power.valid ? sys.pvOnAcOut.power.value : 0
+ property double pvInverterOnAcIn1: sys.pvOnAcIn1.power.valid ? sys.pvOnAcIn1.power.value : 0
+ property double pvInverterOnAcIn2: sys.pvOnAcIn2.power.valid ? sys.pvOnAcIn2.power.value : 0
+
+ y: 5
+ text: (pvInverterOnAcOut + pvInverterOnAcIn1 + pvInverterOnAcIn2).toFixed(0) + "W"
+ font.pixelSize: hasDcAndAcSolar ? 20 : 25
+ visible: hasDcAndAcSolar || (hasAcSolarOnIn && hasAcSolarOnOut) || (hasAcSolarOnAcIn1 && hasAcSolarOnAcIn2)
+ }
+ }
+
+ OverviewEssReason {
+ anchors {
+ bottom: parent.bottom; bottomMargin: dcSystemBox.visible ? battery.height + 15 : 5
+ horizontalCenter: parent.horizontalCenter; horizontalCenterOffset: dcSystemBox.visible ? -(root.width / 2 - battery.width / 2 - 10) : 0
+ }
+ }
+
+ OverviewConnection {
+ id: acInToMulti
+ ballCount: 2
+ path: straight
+ active: root.active
+ value: flow(sys.acInput ? sys.acInput.power : 0)
+
+ anchors {
+ left: acInBox.right; leftMargin: -10; top: multi.verticalCenter;
+ right: multi.left; rightMargin: -10; bottom: multi.verticalCenter
+ }
+ }
+
+ OverviewConnection {
+ id: multiToAcLoads
+ ballCount: 2
+ path: straight
+ active: root.active
+ value: flow(sys.acLoad.power)
+
+ anchors {
+ left: multi.right; leftMargin: -10;
+ top: multi.verticalCenter
+ right: acLoadBox.left; rightMargin: -10
+ bottom: multi.verticalCenter
+ }
+ }
+
+ OverviewConnection {
+ id: pvInverterToMulti
+
+ property int hasDcAndAcFlow: Utils.sign(noNoise(sys.pvOnAcOut.power) + noNoise(sys.pvOnAcIn1.power) + noNoise(sys.pvOnAcIn2.power))
+
+ ballCount: 4
+ path: corner
+ active: root.active && hasAcSolar
+ value: hasDcAndAcSolar ? hasDcAndAcFlow : flow(sys.pvOnAcOut.power)
+
+ anchors {
+ left: pvInverter.left; leftMargin: 8
+ top: pvInverter.verticalCenter; topMargin: hasDcAndAcSolar ? 1 : 0
+ right: multi.horizontalCenter; rightMargin: -20
+ bottom: multi.bottom; bottomMargin: 10
+ }
+ }
+
+ // invisible anchor point to connect the chargers to the battery
+ Item {
+ id: dcConnect
+ anchors {
+ left: multi.horizontalCenter; leftMargin: hasAcSolar ? -20 : 0
+ bottom: dcSystemBox.top; bottomMargin: 10
+ }
+ }
+
+ OverviewConnection {
+ id: multiToDcConnect
+ ballCount: 3
+ path: straight
+ active: root.active
+ value: -flow(sys.inverterChargerDc.power);
+ startPointVisible: false
+
+ anchors {
+ left: dcConnect.left
+ top: dcConnect.top
+
+ right: dcConnect.left
+ bottom: multi.bottom; bottomMargin: 10
+ }
+ }
+
+ OverviewConnection {
+ id: blueSolarChargerDcConnect
+ ballCount: 3
+ path: straight
+ active: root.active && hasDcSolar
+ value: -flow(sys.pvCharger.power)
+ startPointVisible: false
+
+ anchors {
+ left: dcConnect.left
+ top: dcConnect.top
+
+ right: blueSolarCharger.left; rightMargin: -8
+ bottom: dcConnect.top;
+ }
+ }
+
+ OverviewConnection {
+ id: chargersToBattery
+ ballCount: 3
+ path: straight
+ active: root.active
+ value: Utils.sign(noNoise(sys.pvCharger.power) + noNoise(sys.inverterChargerDc.power))
+ startPointVisible: false
+
+ anchors {
+ left: dcConnect.left
+ top: dcConnect.top
+
+ right: battery.right; rightMargin: 10
+ bottom: dcConnect.top
+ }
+ }
+
+ OverviewConnection {
+ id: batteryToDcSystem
+ ballCount: 2
+ path: straight
+ active: root.active && sys.dcSystem.power.valid
+ value: flow(sys.dcSystem.power)
+
+ anchors {
+ left: battery.right; leftMargin: -10
+ top: dcSystemBox.verticalCenter;
+ right: dcSystemBox.left; rightMargin: -10
+ bottom: dcSystemBox.verticalCenter
+ }
+ }
+}
diff --git a/FileSets/v3.60~25/OverviewMobileEnhanced.qml b/FileSets/v3.60~25/OverviewMobileEnhanced.qml
new file mode 100644
index 00000000..ceeeffc5
--- /dev/null
+++ b/FileSets/v3.60~25/OverviewMobileEnhanced.qml
@@ -0,0 +1,989 @@
+// GuiMods Enhancements to OverviewMobile screen
+
+// Removed logo and added AC INPUT and SYSTEM tiles originally displayed on other overviews
+// Added voltage, current and frequency to AC INPUT and AC LOADS tiles
+// Added source (Grid, Generator, Shore Power) to AC INPUT tile
+// Replaced to/from battery with current in DC SYSTEM tile
+// DC SYSTEM tile title now reflects direction: "DC LOADS, DC CHARGER"
+// Rearranged tiles to match a left to right signal flow : sources on left, loads on right
+// Standardized "info" tile sizes to 1 or 1.5 wide x 1 or 2 high
+// infoArea defines usable space for info tiles and all tiles are a child of infoArea
+// (makes repositioning easier than when they were in separate column objects)
+// Large text for main paremeter in each tile has been reduced in size to allow more parameters without
+// expanding tile height (30 to 22)
+// merged SYSTEM and STATUS tiles
+// removed speed from STATUS to reduce tile height
+// hide "reason" text if it's blank to save space
+// changed clock to 12-hour format
+// Capitialized battery state: "Idle", "Charging", "Discharging"
+// errors and notificaitons in SYSTEM/STATUS tile may push clock off bottom of tile
+// Tile content for items that are not present are made invisible - tile remains in place
+// that is no height adjustments when a tile provides no information
+// Adjust button widths so that pump button fits within tank column
+// Hide pump button when not enabled giving more room for tanks
+// Add temperature sensors to tanks column
+// add control of VE.Direct inverters
+
+// Includes changes to handle SeeLevel NMEA2000 tank sensor:
+// Ignore the real incoming tank dBus service because it's information changes
+// Changes in TileText.qml are also part of the TankRepeater package
+
+// Search for //////// to find changes
+
+import QtQuick 2
+import com.victron.velib 1.0
+import "utils.js" as Utils
+import "timeToGo.js" as TTG
+import "enhancedFormat.js" as EnhFmt
+
+OverviewPage {
+ title: qsTr("Mobile")
+ id: root
+
+ property color detailColor: "#b3b3b3"
+ property real touchTargetOpacity: 0.3
+ property int touchArea: 40
+
+ property variant sys: theSystem
+ property string settingsBindPreffix: "com.victronenergy.settings"
+ property string pumpBindPreffix: "com.victronenergy.pump.startstop0"
+ property variant activeNotifications: NotificationCenter.notifications.filter(
+ function isActive(obj) { return obj.active} )
+ property string noAdjustableByDmc: qsTr("This setting is disabled when a Digital Multi Control " +
+ "is connected. If it was recently disconnected execute " +
+ "\"Redetect system\" that is available on the inverter menu page.")
+ property string noAdjustableByBms: qsTr("This setting is disabled when a VE.Bus BMS " +
+ "is connected. If it was recently disconnected execute " +
+ "\"Redetect system\" that is available on the inverter menu page.")
+ property string noAdjustableTextByConfig: qsTr("This setting is disabled. " +
+ "Possible reasons are \"Overruled by remote\" is not enabled or " +
+ "an assistant is preventing the adjustment. Please, check " +
+ "the inverter configuration with VEConfigure.")
+
+//////// added to keep track of tanks and temps
+ property int numberOfTemps: 0
+ property int tankTempCount: tankModel.rowCount + numberOfTemps
+ property real tanksTempsHeight: root.height - (pumpButton.pumpEnabled ? pumpButton.height : 0)
+ property real tanksHeight: tankModel.rowCount > 0 ? tanksTempsHeight * tankModel.rowCount / tankTempCount : 0
+ property real tempsHeight: tanksTempsHeight - tanksHeight
+ property real minimumTankHeight: 21
+ property real maxTankHeight: 80
+ property real tankTileHeight: Math.min (Math.max (tanksTempsHeight / tankTempCount, minimumTankHeight), maxTankHeight)
+
+ property bool compact: tankTempCount > (pumpButton.pumpEnabled ? 5 : 6)
+
+ property string systemPrefix: "com.victronenergy.system"
+ VBusItem { id: vebusService; bind: Utils.path(systemPrefix, "/VebusService") }
+ property bool isMulti: vebusService.valid
+ property string veDirectInverterService: ""
+ property string inverterService: vebusService.valid ? vebusService.value : veDirectInverterService
+
+ property bool isInverter: ! isMulti && veDirectInverterService != ""
+ property bool hasAcInput: isMulti
+ VBusItem { id: _hasAcOutSystem; bind: "com.victronenergy.settings/Settings/SystemSetup/HasAcOutSystem" }
+ property bool hasAcOutSystem: _hasAcOutSystem.value === 1
+
+//////// add for system state
+ property bool hasSystemState: _systemState.valid
+
+//////// add for SYSTEM tile and voltage, power and frequency values
+ property VBusItem _systemState: VBusItem { bind: Utils.path(systemPrefix, "/SystemState/State") }
+//////// add for PV CHARGER voltage and current
+ property string pvChargerPrefix: ""
+ property int numberOfPvChargers: 0
+
+
+ //////// standard tile sizes
+ //////// positions are left, center, right and top, center, bottom of infoArea
+ property int tankWidth: 130
+
+ property int upperTileHeight: 185
+ property int acTileHeight: height - upperTileHeight
+
+ property int infoWidth: width - tankWidth
+ property int infoWidth3Column: infoWidth / 3
+ property int infoWidth2Column: infoWidth / 2
+
+//////// add for PV Charger voltage and current
+ VBusItem { id: pvNrTrackers; bind: Utils.path(pvChargerPrefix, "/NrOfTrackers") }
+ property bool singleTracker: ! pvNrTrackers.valid || pvNrTrackers.value == 1
+ property bool showPvVI: numberOfPvChargers == 1 && singleTracker
+ VBusItem { id: pvPower; bind: Utils.path(pvChargerPrefix, "/Yield/Power") }
+ VBusItem { id: pvVoltage; bind: Utils.path(pvChargerPrefix, singleTracker ? "/Pv/V" : "/Pv/0/V") }
+
+//////// add for inverter mode in STATUS
+ VBusItem { id: inverterMode; bind: Utils.path(inverterService, "/Mode") }
+
+//////// add for gauges
+ VBusItem { id: showGaugesItem; bind: Utils.path(guiModsPrefix, "/ShowGauges") }
+ property bool showGauges: showGaugesItem.valid ? showGaugesItem.value === 1 ? true : false : false
+
+//////// added to control time display
+ property string guiModsPrefix: "com.victronenergy.settings/Settings/GuiMods"
+ VBusItem { id: timeFormatItem; bind: Utils.path(guiModsPrefix, "/TimeFormat") }
+ property string timeFormat: getTimeFormat ()
+
+ function getTimeFormat ()
+ {
+ if (!timeFormatItem.valid || timeFormatItem.value === 0)
+ return ""
+ else if (timeFormatItem.value === 2)
+ return "h:mm ap"
+ else
+ return "hh:mm"
+ }
+
+ Component.onCompleted: { discoverServices(); showHelp () }
+
+ // define usable space for tiles but don't show anything
+ Rectangle {
+ id: infoArea
+ visible: false
+ anchors {
+ left: parent.left
+ right: tanksColum.left
+ top: parent.top;
+ bottom: parent.bottom;
+ }
+ }
+
+//////// change time to selectable 12/24 hour format
+ Timer {
+ id: wallClock
+ running: timeFormat != ""
+ repeat: true
+ interval: 1000
+ triggeredOnStart: true
+ onTriggered: time = Qt.formatDateTime(new Date(), timeFormat)
+ property string time
+ }
+
+ VBusItem { id: systemName; bind: Utils.path(settingsBindPreffix, "/Settings/SystemSetup/SystemName") }
+
+//////// copied SYSTEM from OverviewTiles.qml & combined SYSTEM and STATUS tiles
+ Tile {
+ title: qsTr("STATUS")
+ id: statusTile
+ anchors { left: parent.left; top: parent.top }
+ width: root.infoWidth3Column
+ height: root.upperTileHeight - inverterTile.height
+ color: "#4789d0"
+
+//////// relorder to give priority to errors
+ values: [
+ TileText {
+ text: systemName.valid && systemName.value !== "" ? systemName.value : sys.systemType.valid ? sys.systemType.value.toUpperCase() : ""
+ font.pixelSize: 16
+ wrapMode: Text.WordWrap
+ width: statusTile.width - 5
+ },
+ TileText {
+ text: wallClock.running ? wallClock.time : ""
+ font.pixelSize: 15
+ },
+//////// combine SystemReason with notifications
+ MarqueeEnhanced {
+ text:
+ {
+ if (activeNotifications.length === 0)
+ return systemReasonMessage.text
+ else
+ return notificationText() + " || " + systemReasonMessage.text
+ }
+ width: statusTile.width
+ textHorizontalAlignment: Text.AlignHCenter
+ interval: 100
+ SystemReasonMessage {
+ id: systemReasonMessage
+ }
+ },
+ TileText {
+ property VeQuickItem gpsService: VeQuickItem { uid: "dbus/com.victronenergy.system/GpsService" }
+ property VeQuickItem speed: VeQuickItem { uid: Utils.path("dbus/", gpsService.value, "/Speed") }
+ property VeQuickItem speedUnit: VeQuickItem { uid: "dbus/com.victronenergy.settings/Settings/Gps/SpeedUnit" }
+
+ text: speed.value === undefined ? "" : getValue()
+ visible: speed.value !== undefined && speedUnit.value !== undefined
+
+ function getValue()
+ {
+ if (speed.value < 0.5) // blank speed if less than about 1 MPH
+ return " "
+ if (speedUnit.value === "km/h")
+ return (speed.value * 3.6).toFixed(1) + speedUnit.value
+ if (speedUnit.value === "mph")
+ return (speed.value * 2.236936).toFixed(1) + speedUnit.value
+ if (speedUnit.value === "kt")
+ return (speed.value * (3600/1852)).toFixed(1) + speedUnit.value
+ return speed.value.toFixed(2) + "m/s"
+ }
+ }
+ ]
+ } // end Tile STATUS
+ Tile
+ {
+ title: qsTr("INVERTER")
+ id: inverterTile
+ anchors { left: parent.left; top: statusTile.bottom }
+ width: root.infoWidth3Column
+ height: 62
+ color: "#4789d0"
+
+ values: [
+ TileText
+ {
+ text: inverterMode.valid ? inverterMode.text : "--"
+ },
+ TileText {
+ text: qsTr(systemState.text)
+
+ SystemState {
+ id: systemState
+ bind: hasSystemState?Utils.path(systemPrefix, "/SystemState/State"):Utils.path(inverterService, "/State")
+ }
+ }
+ ]
+////// add power bar graph
+ PowerGaugeMulti
+ {
+ id: multiBar
+ width: parent.width - 10
+ height: 8
+ anchors
+ {
+ top: parent.top; topMargin: 20
+ horizontalCenter: parent.horizontalCenter
+ }
+ inverterService: root.inverterService
+ visible: showGauges
+ }
+ DetailTarget { id: multiTarget; detailsPage: "DetailInverter.qml" }
+ } // end Tile INVERTER
+
+ Tile {
+ title: qsTr("BATTERY")
+ id: batteryTile
+ anchors { horizontalCenter: infoArea.horizontalCenter; top: infoArea.top }
+ width: root.infoWidth3Column
+ height: root.upperTileHeight
+
+ values: [
+ TileText // spacer
+ {
+ text: ""
+ font.pixelSize: 6
+ },
+ TileText {
+ text: sys.battery.soc.absFormat(0)
+ font.pixelSize: 22
+ //////// remove height (for consistency with other tiles)
+ },
+ TileText {
+ text: {
+ if (!sys.battery.state.valid)
+ return "---"
+ switch(sys.battery.state.value) {
+ //////// change - capitalized words look better
+ case sys.batteryStateIdle: return qsTr("Idle")
+ case sys.batteryStateCharging : return qsTr("Charging")
+ case sys.batteryStateDischarging : return qsTr("Discharging")
+ }
+ }
+ },
+ TileText {
+//////// change to show negative for battery drain
+ text: sys.battery.power.text
+ font.pixelSize: 18
+ },
+ TileText {
+ text: sys.battery.voltage.format(2)
+ },
+ TileText {
+ text: sys.battery.current.format(1)
+ },
+ TileText {
+ text: qsTr("Remaining:")
+ visible: timeToGo.valid
+ },
+ TileText {
+ id: timeToGoText
+ text: timeToGo.valid ? TTG.formatTimeToGo (timeToGo) : " "
+ visible: timeToGo.valid
+
+ VBusItem {
+ id: timeToGo
+ bind: Utils.path("com.victronenergy.system","/Dc/Battery/TimeToGo")
+ }
+ }
+ ]
+////// add battery current bar graph
+ PowerGaugeBattery
+ {
+ id: batteryBar
+ width: parent.width - 10
+ height: 8
+ endLabelFontSize: 14
+ endLabelBackgroundColor: batteryTile.color
+ anchors
+ {
+ top: parent.top; topMargin: 22
+ horizontalCenter: parent.horizontalCenter
+ }
+ visible: showGauges
+ }
+ DetailTarget { id: batteryTarget; detailsPage: "DetailBattery.qml" }
+ } // end Tile BATTERY
+
+ VBusItem { id: dcSystemNameItem; bind: Utils.path(settingsBindPreffix, "/Settings/GuiMods/CustomDcSystemName") }
+
+ Tile {
+ title: dcSystemNameItem.valid && dcSystemNameItem.value != "" ? dcSystemNameItem.value : qsTr ("DC SYSTEM")
+ id: dcSystem
+ anchors { right: infoArea.right; bottom: infoArea.bottom; bottomMargin: root.acTileHeight }
+ width: root.infoWidth3Column
+ height: (root.upperTileHeight / 2) - 5
+ color: "#16a085"
+ values: [
+ TileText { // spacer
+ font.pixelSize: 6
+ text: ""
+ },
+ TileText {
+ font.pixelSize: 22
+ text: EnhFmt.formatVBusItem (sys.dcSystem.power)
+ visible: sys.dcSystem.power.valid
+ },
+ ////// replace to/from battery with current
+ TileText {
+ text: !sys.dcSystem.power.valid ? "---" :
+ EnhFmt.formatValue (sys.dcSystem.power.value / sys.battery.voltage.value, "A")
+ visible: sys.dcSystem.power.valid
+ }
+ ]
+ PowerGauge
+ {
+ id: dcSystemGauge
+ width: parent.width - 10
+ height: 8
+ anchors
+ {
+ top: parent.top; topMargin: 22
+ horizontalCenter: parent.horizontalCenter
+ }
+ connection: sys.dcSystem
+ endLabelFontSize: 12
+ endLabelBackgroundColor: dcSystem.color
+ maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/DcSystemMaxLoad"
+ maxReversePowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/DcSystemMaxCharge"
+ showLabels: true
+ visible: showGauges && sys.dcSystem.power.valid
+ }
+ DetailTarget { id: dcSystemTarget; detailsPage: "DetailDcSystem.qml" }
+ } // end Tile DC SYSTEM
+
+ Tile {
+ id: solarTile
+ title: qsTr("PV CHARGER")
+ anchors { right: infoArea.right; top: infoArea.top }
+ width: root.infoWidth3Column
+ height: root.upperTileHeight - dcSystem.height
+ color: "#2cc36b"
+ values: [
+ TileText {
+ font.pixelSize: 22
+ text: EnhFmt.formatVBusItem (sys.pvCharger.power)
+ },
+ //////// add voltage
+ TileText {
+ text:
+ {
+ if (showPvVI)
+ return EnhFmt.formatVBusItem (pvVoltage, "V")
+ else
+ return ""
+ }
+ visible: showPvVI
+ },
+ //////// add current
+ TileText {
+ text:
+ {
+ if (showPvVI && pvPower.valid && pvVoltage.valid)
+ {
+ var voltage = pvVoltage.value
+ return EnhFmt.formatValue ((pvPower.value / voltage), "A")
+ }
+ else
+ return ""
+ }
+ visible: showPvVI
+ }
+ ]
+////// add power bar graph
+ PowerGauge
+ {
+ id: pvChargerBar
+ width: parent.width - 10
+ height: 8
+ anchors
+ {
+ top: parent.top; topMargin: 20
+ horizontalCenter: parent.horizontalCenter
+ }
+ connection: sys.pvCharger
+ maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/PvChargerMaxPower"
+ visible: showGauges && sys.pvCharger.power.valid
+ }
+ DetailTarget { id: pvChargerTarget; detailsPage: "DetailPvCharger.qml" }
+ } // end Tile PV CHARGER
+
+//////// add to display AC input ignored
+ VBusItem { id: ignoreAcInput; bind: Utils.path(inverterService, "/Ac/State/IgnoreAcIn1") }
+
+//////// add AC INPUT tile
+ Tile {
+ id: acInputTile
+ title: {
+ if (isInverter)
+ return qsTr ("No AC Input")
+ else if (ignoreAcInput.valid && ignoreAcInput.value == 1)
+ return qsTr ("AC In Ignored")
+ else
+ {
+ switch(sys.acSource) {
+ case 1: return qsTr("GRID")
+ case 2: return qsTr("GENERATOR")
+ case 3: return qsTr("SHORE POWER")
+ default: return qsTr("AC INPUT")
+ }
+ }
+ }
+ anchors { left: infoArea.left; bottom: infoArea.bottom }
+ width: root.infoWidth2Column
+ height: root.acTileHeight
+ color: "#82acde"
+//////// add voltage and current
+ VBusItem { id: currentLimit; bind: Utils.path(inverterService, "/Ac/ActiveIn/CurrentLimit") }
+ values: [
+ TileText {
+ visible: isMulti
+ text: EnhFmt.formatVBusItem (sys.acInput.power)
+ font.pixelSize: 20
+
+ },
+//////// add voltage and current
+ TileText {
+ visible: isMulti
+ text: EnhFmt.formatVBusItem (sys.acInput.voltageL1, "V") + " " + EnhFmt.formatVBusItem (sys.acInput.currentL1, "A") + " " + EnhFmt.formatVBusItem (sys.acInput.frequency, "Hz")
+ },
+ TileText
+ {
+ text: qsTr ("Limit: ") + EnhFmt.formatVBusItem (currentLimit, "A")
+ visible: currentLimit.valid
+ }
+ ]
+////// add power bar graph
+ PowerGauge
+ {
+ id: acInBar
+ width: parent.width - 10
+ height: 8
+ anchors
+ {
+ top: parent.top; topMargin: 20
+ horizontalCenter: parent.horizontalCenter
+ }
+ connection: sys.acInput
+ useInputCurrentLimit: true
+ maxForwardPowerParameter: ""
+ maxReversePowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/MaxFeedInPower"
+ visible: showGauges && hasAcInput
+ }
+ DetailTarget { id: acInputTarget; detailsPage: "DetailAcInput.qml" }
+ }
+
+ Tile {
+ title: qsTr("AC LOADS")
+ id: acLoadsTile
+ anchors { right: infoArea.right; bottom: infoArea.bottom}
+ width: root.infoWidth2Column
+ height: root.acTileHeight
+ color: "#e68e8a"
+//////// add voltage and current
+ VBusItem { id: outVoltage; bind: Utils.path(inverterService, "/Ac/Out/L1/V") }
+ VBusItem { id: outCurrent; bind: Utils.path(inverterService, "/Ac/Out/L1/I") }
+ VBusItem { id: outFrequency; bind: Utils.path(inverterService, "/Ac/Out/L1/F") }
+
+ values: [
+ TileText {
+ text: EnhFmt.formatVBusItem (sys.acLoad.power)
+ font.pixelSize: 22
+ },
+//////// add voltage and current - no frequency for VE.Direct inverter
+ TileText {
+ text:
+ {
+ var lineText = ""
+ if (isMulti || isInverter)
+ {
+ lineText = EnhFmt.formatVBusItem (outVoltage, "V") + " " + EnhFmt.formatVBusItem (outCurrent, "A")
+ if (isMulti)
+ lineText += " " + EnhFmt.formatVBusItem (outFrequency, "Hz")
+ }
+ return lineText
+ }
+ }
+ ]
+////// add power bar graph
+ PowerGauge
+ {
+ id: acLoadBar
+ width: parent.width - 10
+ height: 8
+ anchors
+ {
+ top: parent.top; topMargin: 20
+ horizontalCenter: parent.horizontalCenter
+ }
+ connection: sys.acLoad
+ maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/AcOutputMaxPower"
+ visible: showGauges && hasAcOutSystem
+ }
+ DetailTarget { id: acLoadsOnOutputTarget; detailsPage: "DetailLoadsOnOutput.qml" }
+ }
+
+ // Synchronise tank name text scroll start
+ Timer {
+ id: scrollTimer
+ interval: 15000
+ repeat: true
+ running: root.active
+ }
+
+ ListView {
+ id: tanksColum
+
+ anchors {
+ top: root.top
+ right: root.right
+ }
+ height: root.tanksHeight
+ width: root.tankWidth
+//////// make list flickable if more tiles than will fit completely
+ interactive: root.tankTileHeight * count > (tanksColum.height + 1) ? true : false
+
+ model: TankModel { id: tankModel }
+ delegate: TileTankEnhanced {
+ // Without an intermediate assignment this will trigger a binding loop warning.
+ property variant theService: DBusServices.get(buddy.id)
+ service: theService
+ width: tanksColum.width
+ height: root.tankTileHeight
+ pumpBindPrefix: root.pumpBindPreffix
+//////// modified to control compact differently
+ compact: root.compact
+ Connections {
+ target: scrollTimer
+ onTriggered: doScroll()
+ }
+ }
+ Tile {
+ title: qsTr("TANKS")
+ anchors.fill: parent
+ values: TileText {
+ text: qsTr("")
+ width: parent.width
+ wrapMode: Text.WordWrap
+ }
+ z: -1
+ }
+ }
+
+//////// added temperature ListView and Model
+ ListView {
+ id: tempsColumn
+
+ anchors {
+ top: tanksColum.bottom
+ right: root.right
+ }
+ height: root.tempsHeight
+ width: root.tankWidth
+//////// make list flickable if more tiles than will fit completely
+ interactive: root.tankTileHeight * count > (tempsColumn.height + 1) ? true : false
+
+ model: tempsModel
+ delegate: TileTemp
+ {
+ width: tempsColumn.width
+ height: root.tankTileHeight
+//////// modified to control compact differently
+ compact: root.compact
+ Connections
+ {
+ target: scrollTimer
+ onTriggered: doScroll()
+ }
+ }
+ Tile
+ {
+ title: qsTr("TEMPS")
+ anchors.fill: parent
+ values: TileText
+ {
+ text: qsTr("")
+ width: parent.width
+ wrapMode: Text.WordWrap
+ }
+ z: -1
+ }
+ }
+ ListModel { id: tempsModel }
+
+ Tile {
+ id: pumpButton
+
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+
+ property variant texts: [ qsTr("AUTO"), qsTr("ON"), qsTr("OFF")]
+ property int value: 0
+ property bool reset: false
+ property bool pumpEnabled: pumpRelay.value === 3
+ isCurrentItem: false // not used by GuiMods key handler - focus shown a different way
+ //focus: root.active && isCurrentItem // don't switch focus -- messes up key handler
+
+ title: qsTr("PUMP")
+ width: pumpEnabled ? root.tankWidth : 0
+ height: 45
+ editable: true
+ readOnly: false
+ color: pumpButtonMouseArea.containsPressed ? "#d3d3d3" : "#A8A8A8"
+
+ VBusItem { id: pump; bind: Utils.path(settingsBindPreffix, "/Settings/Pump0/Mode") }
+ VBusItem { id: pumpRelay; bind: Utils.path(settingsBindPreffix, "/Settings/Relay/Function") }
+
+ values: [
+ TileText {
+ text: pumpButton.pumpEnabled ? qsTr("%1").arg(pumpButton.texts[pumpButton.value]) : qsTr("DISABLED")
+ }
+ ]
+
+ function edit() {
+ if (!pumpEnabled) {
+ toast.createToast(qsTr("Pump functionality is not enabled. To enable it go to the relay settings page and set function to \"Tank pump\""), 5000)
+ return
+ }
+
+ reset = true
+ applyAnimation.restart()
+ reset = false
+
+ if (value < 2)
+ value++
+ else
+ value = 0
+ }
+
+ MouseArea {
+ id: pumpButtonMouseArea
+ property bool containsPressed: containsMouse && pressed
+ anchors.fill: parent
+ onClicked: {
+ parent.edit()
+ }
+ }
+
+ Rectangle {
+ id: timerRect
+ height: 2
+ width: pumpButton.width * 0.8
+ visible: applyAnimation.running
+ anchors {
+ bottom: parent.bottom; bottomMargin: 5
+ horizontalCenter: parent.horizontalCenter
+ }
+ }
+
+ SequentialAnimation {
+ id: applyAnimation
+ alwaysRunToEnd: false
+ NumberAnimation {
+ target: timerRect
+ property: "width"
+ from: 0
+ to: pumpButton.width * 0.8
+ duration: 3000
+ }
+
+ ColorAnimation {
+ target: pumpButton
+ property: "color"
+ from: "#A8A8A8"
+ to: "#4789d0"
+ duration: 200
+ }
+
+ ColorAnimation {
+ target: pumpButton
+ property: "color"
+ from: "#4789d0"
+ to: "#A8A8A8"
+ duration: 200
+ }
+ PropertyAction {
+ target: timerRect
+ property: "width"
+ value: 0
+ }
+ // Do not set value if the animation is restarted by user pressing the button
+ // to move between options
+ onRunningChanged: if (!running && !pumpButton.reset) pump.setValue(pumpButton.value)
+ }
+ DetailTarget { id: pumpButtonTarget; detailsPage: "" }
+ }
+
+ // When new service is found add resources as appropriate
+ Connections {
+ target: DBusServices
+ onDbusServiceFound: addService(service)
+ }
+
+ // hack to get value(s) from within a loop inside a function when service is changing
+ property string tempServiceName: ""
+ property VBusItem temperatureItem: VBusItem { bind: Utils.path(tempServiceName, "/Dc/0/Temperature") }
+
+//////// rewrite to use switch in place of if statements
+ function addService(service)
+ {
+ switch (service.type)
+ {
+//////// add for temp sensors
+ case DBusService.DBUS_SERVICE_TEMPERATURE_SENSOR:
+ numberOfTemps++
+ tempsModel.append({serviceName: service.name})
+ break;;
+ case DBusService.DBUS_SERVICE_MULTI:
+ root.tempServiceName = service.name
+ if (temperatureItem.valid)
+ {
+ numberOfTemps++
+ tempsModel.append({serviceName: service.name})
+ }
+ break;;
+//////// add for VE.Direct inverters
+ case DBusService.DBUS_SERVICE_INVERTER:
+ if (veDirectInverterService == "")
+ veDirectInverterService = service.name;
+ break;;
+
+//////// add for PV CHARGER voltage and current display
+ case DBusService.DBUS_SERVICE_SOLAR_CHARGER:
+ numberOfPvChargers++
+ if (pvChargerPrefix === "")
+ pvChargerPrefix = service.name;
+ break;;
+ case DBusService.DBUS_SERVICE_BATTERY:
+ root.tempServiceName = service.name
+ if (temperatureItem.valid)
+ {
+ numberOfTemps++
+ tempsModel.append({serviceName: service.name})
+ }
+ break;;
+ }
+ }
+
+ // Check available services to find tank sesnsors
+//////// rewrite to always call addService, removing redundant service type checks
+ function discoverServices()
+ {
+ numberOfTemps = 0
+ numberOfPvChargers = 0
+ veDirectInverterService = ""
+ tempsModel.clear()
+ for (var i = 0; i < DBusServices.count; i++)
+ addService(DBusServices.at(i))
+ }
+
+ function notificationText()
+ {
+ if (activeNotifications.length === 0)
+ return qsTr("")
+
+ var descr = []
+ for (var n = 0; n < activeNotifications.length; n++) {
+ var notification = activeNotifications[n];
+
+ var text = notification.serviceName + " - " + notification.description;
+ if (notification.value !== "" )
+ text += ": " + notification.value
+
+ descr.push(text)
+ }
+
+ return descr.join(" | ")
+ }
+
+ VBusItem { id: dmc; bind: Utils.path(inverterService, "/Devices/Dmc/Version") }
+ VBusItem { id: bms; bind: Utils.path(inverterService, "/Devices/Bms/Version") }
+
+
+
+// Details targets
+////// display detail targets and help message when first displayed.
+ Timer {
+ id: helpTimer
+ running: false
+ repeat: false
+ interval: 5000
+ triggeredOnStart: true
+ }
+
+ // help message shown when menu is first drawn
+ Rectangle
+ {
+ id: helpBox
+ color: "white"
+ width: 150
+ height: 32
+ opacity: 0.7
+ anchors
+ {
+ horizontalCenter: infoArea.horizontalCenter
+ verticalCenter: infoArea.verticalCenter
+ }
+ visible: false
+ }
+ TileText
+ {
+ text: qsTr ( "Tap tile center for detail at any time" )
+ color: "black"
+ anchors.fill: helpBox
+ wrapMode: Text.WordWrap
+ font.pixelSize: 12
+ visible: helpBox.visible
+ }
+
+
+ //// hard key handler
+ // used to press buttons when touch isn't available
+ // UP and DOWN buttons cycle through the list of touch areas
+ // "space" button is used to simulate a touch on the area
+ // target must be highlighted so that other uses of "space"
+ // will still occur
+
+ // list of all details touchable areas
+ // pump button sets value locally, no details page
+ // so is hanelded differently
+ // it must be LAST in the list because target list index is used for special processing
+ property variant targetList:
+ [
+ multiTarget, batteryTarget, pvChargerTarget, dcSystemTarget,
+ acInputTarget, acLoadsOnOutputTarget, pumpButtonTarget // pump MUST BE LAST
+ ]
+
+ property int selectedTarget: 0
+
+ Timer
+ {
+ id: targetTimer
+ interval: 5000
+ repeat: false
+ running: false
+ onTriggered: { hideAllTargets () }
+ }
+
+ Keys.forwardTo: [keyHandler]
+ Item
+ {
+ id: keyHandler
+ Keys.onUpPressed:
+ {
+ nextTarget (-1)
+ event.accepted = true
+ }
+
+ Keys.onDownPressed:
+ {
+ nextTarget (+1)
+ event.accepted = true
+ }
+ Keys.onSpacePressed:
+ {
+ if (targetTimer.running)
+ {
+ var foo // hack to make clicked() work
+ if (selectedTarget == targetList.length - 1)
+ pumpButton.edit ()
+ else
+ bar.clicked (foo)
+ event.accepted = true
+ }
+ else
+ event.accepted = false
+ }
+ }
+ // hack to make clicked() work
+ property variant bar: targetList[selectedTarget]
+
+ function nextTarget (increment)
+ {
+ // make one pass through all possible targets to find an enabled one
+ // if found, that's the new selectedTarget,
+ // if not selectedTarget does not change
+ var newIndex = selectedTarget
+ for (var i = 0; i < targetList.length; i++)
+ {
+ if (( ! targetTimer.running || helpBox.visible) && targetList[newIndex].enabled)
+ {
+ highlightSelectedTarget ()
+ return
+ }
+ newIndex += increment
+ if (newIndex >= targetList.length)
+ newIndex = 0
+ else if (newIndex < 0)
+ newIndex = targetList.length - 1
+ var includeTarget
+ if (newIndex == targetList.length - 1)
+ includeTarget = pumpButton.pumpEnabled
+ else
+ includeTarget = targetList[newIndex].enabled
+ if (includeTarget)
+ {
+ selectedTarget = newIndex
+ highlightSelectedTarget ()
+ break
+ }
+ }
+ }
+
+ function showHelp ()
+ {
+ for (var i = 0; i < targetList.length; i++)
+ {
+ targetList[i].targetVisible = true
+ }
+ helpBox.visible = true
+ targetTimer.restart ()
+ }
+ function hideAllTargets ()
+ {
+ for (var i = 0; i < targetList.length; i++)
+ targetList[i].targetVisible = false
+ helpBox.visible = false
+ }
+ function highlightSelectedTarget ()
+ {
+ for (var i = 0; i < targetList.length; i++)
+ {
+ if (targetList[i] == targetList[selectedTarget])
+ targetList[i].targetVisible = true
+ else
+ targetList[i].targetVisible = false
+ }
+ targetTimer.restart ()
+ helpBox.visible = false
+ }
+}
diff --git a/FileSets/v3.60~25/OverviewMobileEnhanced.qml.orig b/FileSets/v3.60~25/OverviewMobileEnhanced.qml.orig
new file mode 100644
index 00000000..a1bccfb0
--- /dev/null
+++ b/FileSets/v3.60~25/OverviewMobileEnhanced.qml.orig
@@ -0,0 +1,642 @@
+import QtQuick 2
+import com.victron.velib 1.0
+import "utils.js" as Utils
+
+OverviewPage {
+ id: root
+
+ property variant sys: theSystem
+ property string settingsBindPreffix: "com.victronenergy.settings"
+ property string pumpBindPreffix: "com.victronenergy.pump.startstop0"
+ property variant activeNotifications: NotificationCenter.notifications.filter(
+ function isActive(obj) { return obj.active} )
+ property string noAdjustableByDmc: qsTr("This setting is disabled when a Digital Multi Control " +
+ "is connected. If it was recently disconnected execute " +
+ "\"Redetect system\" that is available on the inverter menu page.")
+ property string noAdjustableByBms: qsTr("This setting is disabled when a VE.Bus BMS " +
+ "is connected. If it was recently disconnected execute " +
+ "\"Redetect system\" that is available on the inverter menu page.")
+ property string noAdjustableTextByConfig: qsTr("This setting is disabled. " +
+ "Possible reasons are \"Overruled by remote\" is not enabled or " +
+ "an assistant is preventing the adjustment. Please, check " +
+ "the inverter configuration with VEConfigure.")
+ property int numberOfMultis: 0
+ property string vebusPrefix: ""
+
+ // Keeps track of which button on the bottom row is active
+ property int buttonIndex: 0
+ // Keep the focus on the buttons, not on the page
+ focus: false
+
+ title: qsTr("Mobile")
+
+ Component.onCompleted: discoverMulti()
+
+ ListView {
+ id: pwColumn
+
+ property int tilesCount: solarTile.visible || dcSystem.visible ? 3 : 2
+ property int tileHeight: Math.ceil(height / tilesCount)
+ interactive: false // static tiles
+
+ width: 136
+ anchors {
+ left: parent.left
+ top: parent.top;
+ bottom: acModeButton.top;
+ }
+
+ model: VisibleItemModel {
+ Tile {
+ width: pwColumn.width
+ height: visible ? pwColumn.tileHeight : 0
+ title: qsTr("AC INPUT")
+ color: "#82acde"
+ visible: !dcSystem.visible || !solarTile.visible
+ values: [
+ TileText {
+ text: sys.acInput.power.text
+ font.pixelSize: 30
+ }
+
+ ]
+ }
+
+ TileAcPower {
+ width: pwColumn.width
+ height: visible ? pwColumn.tileHeight : 0
+ title: qsTr("AC LOADS")
+ color: "#e68e8a"
+ values: [
+ TileText {
+ text: sys.acLoad.power.text
+ font.pixelSize: 30
+ }
+ ]
+ }
+
+ Tile {
+ id: solarTile
+ width: pwColumn.width
+ height: visible ? pwColumn.tileHeight : 0
+ title: qsTr("PV CHARGER")
+ color: "#2cc36b"
+ visible : sys.pvCharger.power.valid
+
+ values: [
+ TileText {
+ font.pixelSize: 30
+ text: sys.pvCharger.power.text
+ }
+ ]
+ }
+ Tile {
+ id: dcSystem
+ width: pwColumn.width
+ height: visible ? pwColumn.tileHeight : 0
+ title: qsTr("DC SYSTEM")
+ color: "#16a085"
+ visible: sys.dcSystem.power.valid
+
+ values: [
+ TileText {
+ font.pixelSize: 30
+ text: sys.dcSystem.power.format(0)
+ },
+ TileText {
+ text: !sys.dcSystem.power.valid ? "---" :
+ sys.dcSystem.power.value < 0 ? qsTr("to battery") : qsTr("from battery")
+ }
+ ]
+ }
+ }
+ }
+
+ Tile {
+ id: logoTile
+
+ color: "#575748"
+ height: 120
+ anchors {
+ left: pwColumn.right
+ right: tanksColum.left
+ top: parent.top
+ }
+
+ MbIcon {
+ x: 1
+ y: 1
+ // see below, so the svg instead of a png if there is a 1x1 image
+ visible: customImage.sourceSize.width === 1 && customImage.sourceSize.height === 1
+ iconId: "mobile-builder-logo-svg"
+ }
+
+ // The uploaded png, the default is a 1x1 transparent pixel now.
+ Image {
+ id: customImage
+ source: "image://theme/mobile-builder-logo"
+ anchors.centerIn: parent
+ }
+ }
+
+ Tile {
+ id: batteryTile
+ height: 112
+ title: qsTr("BATTERY")
+ anchors {
+ left: pwColumn.right
+ right: stateTile.left
+ top: logoTile.bottom
+ bottom: acModeButton.top
+ }
+
+ values: [
+ TileText {
+ text: sys.battery.soc.absFormat(0)
+ font.pixelSize: 30
+ height: 32
+ },
+ TileText {
+ text: {
+ if (!sys.battery.state.valid)
+ return "---"
+ switch(sys.battery.state.value) {
+ case sys.batteryStateIdle: return qsTr("idle")
+ case sys.batteryStateCharging : return qsTr("charging")
+ case sys.batteryStateDischarging : return qsTr("discharging")
+ }
+ }
+ },
+ TileText {
+ text: sys.battery.power.absFormat(0)
+ },
+ TileText {
+ text: sys.battery.voltage.format(1) + " " + sys.battery.current.format(1)
+ }
+ ]
+ }
+
+ Tile {
+ id: stateTile
+
+ width: 104
+ title: qsTr("STATUS")
+ color: "#4789d0"
+
+ anchors {
+ right: tanksColum.left
+ top: logoTile.bottom
+ bottom: acModeButton.top
+ }
+
+ Timer {
+ id: wallClock
+
+ running: true
+ repeat: true
+ interval: 1000
+ triggeredOnStart: true
+ onTriggered: time = Qt.formatDateTime(new Date(), "hh:mm")
+
+ property string time
+ }
+
+ values: [
+ TileText {
+ id: systemTile
+ text: wallClock.time
+ font.pixelSize: 30
+ },
+ TileText {
+ property VeQuickItem gpsService: VeQuickItem { uid: "dbus/com.victronenergy.system/GpsService" }
+ property VeQuickItem speed: VeQuickItem { uid: Utils.path("dbus/", gpsService.value, "/Speed") }
+ property VeQuickItem speedUnit: VeQuickItem { uid: "dbus/com.victronenergy.settings/Settings/Gps/SpeedUnit" }
+
+ text: speed.value === undefined ? "" : getValue()
+ visible: speed.value !== undefined && speedUnit.value !== undefined
+
+ function getValue()
+ {
+ if (speedUnit.value === "km/h")
+ return (speed.value * 3.6).toFixed(1) + speedUnit.value
+ if (speedUnit.value === "mph")
+ return (speed.value * 2.236936).toFixed(1) + speedUnit.value
+ if (speedUnit.value === "kt")
+ return (speed.value * (3600/1852)).toFixed(1) + speedUnit.value
+ return speed.value.toFixed(2) + "m/s"
+ }
+ },
+ Marquee {
+ text: notificationText()
+ width: stateTile.width
+ interval: 100
+ fontSize: 13
+ }
+ ]
+ }
+
+ ListView {
+ id: tanksColum
+
+ property int tileHeight: Math.ceil(height / Math.max(count, 2))
+ width: 134
+ interactive: false // static tiles
+ model: TankModel { id: tankModel }
+ delegate: TileTank {
+ // Without an intermediate assignment this will trigger a binding loop warning.
+ property variant theService: DBusServices.get(buddy.id)
+ service: theService
+ width: tanksColum.width
+ height: tanksColum.tileHeight
+ pumpBindPrefix: root.pumpBindPreffix
+ compact: tankModel.rowCount > (pumpButton.pumpEnabled ? 4 : 5)
+ Connections {
+ target: scrollTimer
+ onTriggered: doScroll()
+ }
+ }
+
+ anchors {
+ top: root.top
+ bottom: pumpButton.pumpEnabled ? acModeButton.top : acModeButton.bottom
+ right: root.right
+ }
+
+ // Synchronise tank name text scroll start
+ Timer {
+ id: scrollTimer
+ interval: 15000
+ repeat: true
+ running: root.active && tankModel.rowCount > 4
+ }
+
+ Tile {
+ title: qsTr("TANKS")
+ anchors.fill: parent
+ values: TileText {
+ text: qsTr("No tanks found")
+ width: parent.width
+ wrapMode: Text.WordWrap
+ }
+ z: -1
+ }
+ }
+
+ Keys.forwardTo: [keyHandler]
+
+ Item {
+ id: keyHandler
+ Keys.onLeftPressed: {
+ if (buttonIndex > 0)
+ buttonIndex--
+
+ event.accepted = true
+ }
+
+ Keys.onRightPressed: {
+ if (buttonIndex < (pumpButton.pumpEnabled ? 2 : 1))
+ buttonIndex++
+
+ event.accepted = true
+ }
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ enabled: parent.active
+ onPressed: mouse.accepted = acCurrentButton.expanded
+ onClicked: acCurrentButton.cancel()
+ }
+
+ TileSpinBox {
+ id: acCurrentButton
+
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
+ isCurrentItem: (buttonIndex == 0)
+ focus: root.active && isCurrentItem
+
+ bind: Utils.path(vebusPrefix, "/Ac/ActiveIn/CurrentLimit")
+ title: qsTr("AC CURRENT LIMIT")
+ color: containsMouse && !editMode ? "#d3d3d3" : "#A8A8A8"
+ width: pumpButton.pumpEnabled ? 160 : 173
+ fontPixelSize: 14
+ unit: "A"
+ readOnly: currentLimitIsAdjustable.value !== 1 || numberOfMultis > 1
+ buttonColor: "#979797"
+
+ VBusItem { id: currentLimitIsAdjustable; bind: Utils.path(vebusPrefix, "/Ac/ActiveIn/CurrentLimitIsAdjustable") }
+
+ Keys.onSpacePressed: showErrorToast(event)
+
+ function editIsAllowed() {
+ if (numberOfMultis > 1) {
+ toast.createToast(qsTr("It is not possible to change this setting when there are more than one inverter connected."), 5000)
+ return false
+ }
+
+ if (currentLimitIsAdjustable.value === 0) {
+ if (dmc.valid) {
+ toast.createToast(noAdjustableByDmc, 5000)
+ return false
+ }
+ if (bms.valid) {
+ toast.createToast(noAdjustableByBms, 5000)
+ return false
+ }
+ if (!dmc.valid && !bms.valid) {
+ toast.createToast(noAdjustableTextByConfig, 5000)
+ return false
+ }
+ }
+
+ return true
+ }
+
+ function showErrorToast(event) {
+ editIsAllowed()
+ event.accepted = true
+ }
+ }
+
+ Tile {
+ id: acModeButton
+ anchors.left: acCurrentButton.right
+ anchors.bottom: parent.bottom
+ property variant texts: { 4: qsTr("OFF"), 3: qsTr("ON"), 1: qsTr("CHARGER ONLY"), 2: qsTr("INVERTER ONLY") }
+ property int value: mode.valid ? mode.value : 3
+ property int shownValue: applyAnimation2.running ? applyAnimation2.pendingValue : value
+
+ isCurrentItem: (buttonIndex == 1)
+ focus: root.active && isCurrentItem
+
+ editable: true
+ readOnly: !modeIsAdjustable.valid || modeIsAdjustable.value !== 1 || numberOfMultis > 1
+ width: pumpButton.pumpEnabled ? 160 : 173
+ height: 45
+ color: acModeButtonMouseArea.containsPressed ? "#d3d3d3" : "#A8A8A8"
+ title: qsTr("AC MODE")
+
+ values: [
+ TileText {
+ text: modeIsAdjustable.valid && numberOfMultis === 1 ? qsTr("%1").arg(acModeButton.texts[acModeButton.shownValue]) : qsTr("NOT AVAILABLE")
+ }
+ ]
+
+ VBusItem { id: mode; bind: Utils.path(vebusPrefix, "/Mode") }
+ VBusItem { id: modeIsAdjustable; bind: Utils.path(vebusPrefix,"/ModeIsAdjustable") }
+
+ Keys.onSpacePressed: edit()
+
+ function edit() {
+ if (!mode.valid)
+ return
+
+ if (numberOfMultis > 1) {
+ toast.createToast(qsTr("It is not possible to change this setting when there are more than one inverter connected."), 5000)
+ return
+ }
+
+ if (modeIsAdjustable.value === 0) {
+ if (dmc.valid)
+ toast.createToast(noAdjustableByDmc, 5000)
+ if (bms.valid)
+ toast.createToast(noAdjustableByBms, 5000)
+ if (!dmc.valid && !bms.valid)
+ toast.createToast(noAdjustableTextByConfig, 5000)
+ return
+ }
+
+ switch (shownValue) {
+ case 4:
+ applyAnimation2.pendingValue = 3
+ break;
+ case 3:
+ applyAnimation2.pendingValue = 1
+ break;
+ case 2:
+ applyAnimation2.pendingValue = 4
+ break;
+ case 1:
+ applyAnimation2.pendingValue = 2
+ break;
+ }
+
+ applyAnimation2.restart()
+ }
+
+ MouseArea {
+ id: acModeButtonMouseArea
+ anchors.fill: parent
+ property bool containsPressed: containsMouse && pressed
+ onClicked: {
+ buttonIndex = 1
+ parent.edit()
+ }
+ }
+
+ Rectangle {
+ id: timerRect2
+ height: 2
+ width: acModeButton.width * 0.8
+ visible: applyAnimation2.running
+ anchors {
+ bottom: parent.bottom; bottomMargin: 5
+ horizontalCenter: parent.horizontalCenter
+ }
+ }
+
+ SequentialAnimation {
+ id: applyAnimation2
+
+ property int pendingValue
+
+ NumberAnimation {
+ target: timerRect2
+ property: "width"
+ from: 0
+ to: acModeButton.width * 0.8
+ duration: 3000
+ }
+
+ ColorAnimation {
+ target: acModeButton
+ property: "color"
+ from: "#A8A8A8"
+ to: "#4789d0"
+ duration: 200
+ }
+
+ ColorAnimation {
+ target: acModeButton
+ property: "color"
+ from: "#4789d0"
+ to: "#A8A8A8"
+ duration: 200
+ }
+ PropertyAction {
+ target: timerRect2
+ property: "width"
+ value: 0
+ }
+
+ ScriptAction { script: mode.setValue(applyAnimation2.pendingValue) }
+
+ PauseAnimation { duration: 1000 }
+ }
+ }
+
+ Tile {
+ id: pumpButton
+
+ anchors.left: acModeButton.right
+ anchors.bottom: parent.bottom
+
+ property variant texts: [ qsTr("AUTO"), qsTr("ON"), qsTr("OFF")]
+ property int value: 0
+ property bool reset: false
+ property bool pumpEnabled: pumpRelay.value === 3
+
+ show: pumpEnabled
+ isCurrentItem: (buttonIndex == 2)
+ focus: root.active && isCurrentItem
+
+ title: qsTr("PUMP")
+ width: show ? 160 : 0
+ height: 45
+ editable: true
+ readOnly: false
+ color: pumpButtonMouseArea.containsPressed ? "#d3d3d3" : "#A8A8A8"
+
+ VBusItem { id: pump; bind: Utils.path(settingsBindPreffix, "/Settings/Pump0/Mode") }
+ VBusItem { id: pumpRelay; bind: Utils.path(settingsBindPreffix, "/Settings/Relay/Function") }
+
+ values: [
+ TileText {
+ text: pumpButton.pumpEnabled ? qsTr("%1").arg(pumpButton.texts[pumpButton.value]) : qsTr("DISABLED")
+ }
+ ]
+
+ Keys.onSpacePressed: edit()
+
+ function edit() {
+ if (!pumpEnabled) {
+ toast.createToast(qsTr("Pump functionality is not enabled. To enable it go to the relay settings page and set function to \"Tank pump\""), 5000)
+ return
+ }
+
+ reset = true
+ applyAnimation.restart()
+ reset = false
+
+ if (value < 2)
+ value++
+ else
+ value = 0
+ }
+
+ MouseArea {
+ id: pumpButtonMouseArea
+ property bool containsPressed: containsMouse && pressed
+ anchors.fill: parent
+ onClicked: {
+ buttonIndex = 2
+ parent.edit()
+ }
+ }
+
+ Rectangle {
+ id: timerRect
+ height: 2
+ width: pumpButton.width * 0.8
+ visible: applyAnimation.running
+ anchors {
+ bottom: parent.bottom; bottomMargin: 5
+ horizontalCenter: parent.horizontalCenter
+ }
+ }
+
+ SequentialAnimation {
+ id: applyAnimation
+ alwaysRunToEnd: false
+ NumberAnimation {
+ target: timerRect
+ property: "width"
+ from: 0
+ to: pumpButton.width * 0.8
+ duration: 3000
+ }
+
+ ColorAnimation {
+ target: pumpButton
+ property: "color"
+ from: "#A8A8A8"
+ to: "#4789d0"
+ duration: 200
+ }
+
+ ColorAnimation {
+ target: pumpButton
+ property: "color"
+ from: "#4789d0"
+ to: "#A8A8A8"
+ duration: 200
+ }
+ PropertyAction {
+ target: timerRect
+ property: "width"
+ value: 0
+ }
+ // Do not set value if the animation is restarted by user pressing the button
+ // to move between options
+ onRunningChanged: if (!running && !pumpButton.reset) pump.setValue(pumpButton.value)
+ }
+ }
+
+ // When new service is found check if is a tank sensor
+ Connections {
+ target: DBusServices
+ onDbusServiceFound: addService(service)
+ }
+
+ function addService(service)
+ {
+ if (service.type === DBusService.DBUS_SERVICE_MULTI) {
+ numberOfMultis++
+ if (vebusPrefix === "")
+ vebusPrefix = service.name;
+ }
+ }
+
+ // Check available services to find tank sesnsors
+ function discoverMulti()
+ {
+ for (var i = 0; i < DBusServices.count; i++) {
+ if (DBusServices.at(i).type === DBusService.DBUS_SERVICE_MULTI) {
+ addService(DBusServices.at(i))
+ }
+ }
+ }
+
+ function notificationText()
+ {
+ if (activeNotifications.length === 0)
+ return qsTr("no alarms")
+
+ var descr = []
+ for (var n = 0; n < activeNotifications.length; n++) {
+ var notification = activeNotifications[n];
+
+ var text = notification.serviceName + " - " + notification.description;
+ if (notification.value !== "" )
+ text += ": " + notification.value
+
+ descr.push(text)
+ }
+
+ return descr.join(" | ")
+ }
+
+ VBusItem { id: dmc; bind: Utils.path(vebusPrefix, "/Devices/Dmc/Version") }
+ VBusItem { id: bms; bind: Utils.path(vebusPrefix, "/Devices/Bms/Version") }
+}
diff --git a/FileSets/v3.60~25/OverviewTanksTempsDigInputs.qml b/FileSets/v3.60~25/OverviewTanksTempsDigInputs.qml
new file mode 100644
index 00000000..b07a817a
--- /dev/null
+++ b/FileSets/v3.60~25/OverviewTanksTempsDigInputs.qml
@@ -0,0 +1,207 @@
+//// New overview page for tanks, temps and digital inputs
+//// part of GuiMods
+//// based on tank/temps column in mobile overview
+
+import QtQuick 2
+import com.victron.velib 1.0
+import "utils.js" as Utils
+import "timeToGo.js" as TTG
+
+OverviewPage {
+ title: qsTr("Tanks & Temps & Digital Inputs")
+ id: root
+
+ property variant sys: theSystem
+ property string systemPrefix: "com.victronenergy.system"
+ property string settingsBindPreffix: "com.victronenergy.settings"
+ property string pumpBindPreffix: "com.victronenergy.pump.startstop0"
+
+ property int numberOfTanks: tankModel.rowCount
+ property real tanksHeight: root.height
+ property real minTankHeight: 21 // use for temps also
+ property real maxTankHeight: 80 // use for temps also
+ property real tankTileHeight: Math.min (Math.max (tanksHeight / numberOfTanks, minTankHeight), maxTankHeight)
+ property bool tanksCompact: numberOfTanks > 6
+
+ property int numberOfTemps: 0
+ property real tempsHeight: root.height
+ property real tempsTileHeight: Math.min (Math.max (tempsHeight / numberOfTemps, minTankHeight), maxTankHeight)
+ property bool tempsCompact: numberOfTemps > 6
+
+ property int tankWidth: parent.width / 3
+ property int tempsWidth: tankWidth
+ property int digInWidth: tankWidth
+
+ property int numberOfDigIn: 0
+ property real digInHeight: root.height
+ property real digInTileHeight: Math.min (Math.max (digInHeight / numberOfDigIn, minTankHeight), maxTankHeight)
+
+ Component.onCompleted: { discoverServices() }
+
+ // Synchronise name text scroll start
+ Timer {
+ id: scrollTimer
+ interval: 15000
+ repeat: true
+ running: root.active
+ }
+
+ ListView {
+ id: tanksColum
+
+ anchors {
+ top: root.top
+ left: root.left
+ }
+ height: root.tanksHeight
+ width: root.tankWidth
+ interactive: root.tankTileHeight * count > (tanksColum.height + 1) ? true : false
+
+ model: TankModel { id: tankModel }
+ delegate: TileTankEnhanced {
+ // Without an intermediate assignment this will trigger a binding loop warning.
+ property variant theService: DBusServices.get(buddy.id)
+ service: theService
+ width: tanksColum.width
+ height: root.tankTileHeight
+ pumpBindPrefix: root.pumpBindPreffix
+ compact: root.tanksCompact
+ Connections {
+ target: scrollTimer
+ onTriggered: doScroll()
+ }
+ }
+ Tile {
+ title: numberOfTanks == 0 ? qsTr ("no tanks") : qsTr("Tanks")
+ anchors.fill: parent
+ color: "#b3b3b3"
+ values: TileText {
+ text: qsTr("")
+ width: parent.width
+ wrapMode: Text.WordWrap
+ }
+ z: -1
+ }
+ }
+
+ ListView {
+ id: tempsColumn
+
+ anchors {
+ top: root.top
+ left: tanksColum.right
+ }
+ height: root.tempsHeight
+ width: root.tempsWidth
+ // make list flickable if more tiles than will fit completely
+ interactive: root.tankTileHeight * count > (tempsColumn.height + 1) ? true : false
+
+ model: tempsModel
+ delegate: TileTemp
+ {
+ width: tempsColumn.width
+ height: root.tempsTileHeight
+ compact: root.tempsCompact
+ Connections
+ {
+ target: scrollTimer
+ onTriggered: doScroll()
+ }
+ }
+ Tile
+ {
+ title: numberOfTemps == 0 ? qsTr ("no temps") : qsTr("Temps")
+ anchors.fill: parent
+ color: "#b3b3b3"
+ values: TileText
+ {
+ text: qsTr("")
+ width: parent.width
+ wrapMode: Text.WordWrap
+ }
+ z: -1
+ }
+ }
+ ListModel { id: tempsModel }
+
+ ListView {
+ id: digInputsColumn
+
+ anchors {
+ top: root.top
+ right: root.right
+ }
+ height: root.digInHeight
+ width: root.digInWidth
+ // make list flickable if more tiles than will fit completely
+ interactive: root.digInTileHeight * count > (digInputsColumn.height + 1) ? true : false
+
+ model: digInModel
+ delegate: TileDigIn
+ {
+ width: digInputsColumn.width
+ height: root.digInTileHeight
+ }
+ Tile
+ {
+ title: numberOfDigIn == 0 ? qsTr ("no digital inputs") : qsTr("Digital Inputs")
+ anchors.fill: parent
+ color: "#b3b3b3"
+ values: TileText
+ {
+ text: qsTr("")
+ width: parent.width
+ wrapMode: Text.WordWrap
+ }
+ z: -1
+ }
+ }
+ ListModel { id: digInModel }
+
+
+ // When new service is found add resources as appropriate
+ Connections {
+ target: DBusServices
+ onDbusServiceFound: addService(service)
+ }
+
+ // hack to get value(s) from within a loop inside a function when service is changing
+ property string tempServiceName: ""
+ property VBusItem temperatureItem: VBusItem { bind: Utils.path(tempServiceName, "/Dc/0/Temperature") }
+
+ function addService(service)
+ {
+ switch (service.type)
+ {
+ case DBusService.DBUS_SERVICE_TEMPERATURE_SENSOR:
+ numberOfTemps++
+ tempsModel.append({serviceName: service.name})
+ break;;
+ case DBusService.DBUS_SERVICE_DIGITAL_INPUT:
+ case DBusService.DBUS_SERVICE_PULSE_COUNTER:
+ numberOfDigIn++
+ digInModel.append({serviceName: service.name})
+ break;;
+ case DBusService.DBUS_SERVICE_BATTERY:
+ case DBusService.DBUS_SERVICE_MULTI:
+ root.tempServiceName = service.name
+ if (temperatureItem.valid)
+ {
+ numberOfTemps++
+ tempsModel.append({serviceName: service.name})
+ }
+ break;;
+ }
+ }
+
+ // Check available services to find tank sesnsors
+ function discoverServices()
+ {
+ numberOfTemps = 0
+ tempsModel.clear()
+ numberOfDigIn = 0
+ digInModel.clear()
+ for (var i = 0; i < DBusServices.count; i++)
+ addService(DBusServices.at(i))
+ }
+}
diff --git a/FileSets/v3.60~25/OverviewTanksTempsDigInputs.qml.orig b/FileSets/v3.60~25/OverviewTanksTempsDigInputs.qml.orig
new file mode 100644
index 00000000..a1bccfb0
--- /dev/null
+++ b/FileSets/v3.60~25/OverviewTanksTempsDigInputs.qml.orig
@@ -0,0 +1,642 @@
+import QtQuick 2
+import com.victron.velib 1.0
+import "utils.js" as Utils
+
+OverviewPage {
+ id: root
+
+ property variant sys: theSystem
+ property string settingsBindPreffix: "com.victronenergy.settings"
+ property string pumpBindPreffix: "com.victronenergy.pump.startstop0"
+ property variant activeNotifications: NotificationCenter.notifications.filter(
+ function isActive(obj) { return obj.active} )
+ property string noAdjustableByDmc: qsTr("This setting is disabled when a Digital Multi Control " +
+ "is connected. If it was recently disconnected execute " +
+ "\"Redetect system\" that is available on the inverter menu page.")
+ property string noAdjustableByBms: qsTr("This setting is disabled when a VE.Bus BMS " +
+ "is connected. If it was recently disconnected execute " +
+ "\"Redetect system\" that is available on the inverter menu page.")
+ property string noAdjustableTextByConfig: qsTr("This setting is disabled. " +
+ "Possible reasons are \"Overruled by remote\" is not enabled or " +
+ "an assistant is preventing the adjustment. Please, check " +
+ "the inverter configuration with VEConfigure.")
+ property int numberOfMultis: 0
+ property string vebusPrefix: ""
+
+ // Keeps track of which button on the bottom row is active
+ property int buttonIndex: 0
+ // Keep the focus on the buttons, not on the page
+ focus: false
+
+ title: qsTr("Mobile")
+
+ Component.onCompleted: discoverMulti()
+
+ ListView {
+ id: pwColumn
+
+ property int tilesCount: solarTile.visible || dcSystem.visible ? 3 : 2
+ property int tileHeight: Math.ceil(height / tilesCount)
+ interactive: false // static tiles
+
+ width: 136
+ anchors {
+ left: parent.left
+ top: parent.top;
+ bottom: acModeButton.top;
+ }
+
+ model: VisibleItemModel {
+ Tile {
+ width: pwColumn.width
+ height: visible ? pwColumn.tileHeight : 0
+ title: qsTr("AC INPUT")
+ color: "#82acde"
+ visible: !dcSystem.visible || !solarTile.visible
+ values: [
+ TileText {
+ text: sys.acInput.power.text
+ font.pixelSize: 30
+ }
+
+ ]
+ }
+
+ TileAcPower {
+ width: pwColumn.width
+ height: visible ? pwColumn.tileHeight : 0
+ title: qsTr("AC LOADS")
+ color: "#e68e8a"
+ values: [
+ TileText {
+ text: sys.acLoad.power.text
+ font.pixelSize: 30
+ }
+ ]
+ }
+
+ Tile {
+ id: solarTile
+ width: pwColumn.width
+ height: visible ? pwColumn.tileHeight : 0
+ title: qsTr("PV CHARGER")
+ color: "#2cc36b"
+ visible : sys.pvCharger.power.valid
+
+ values: [
+ TileText {
+ font.pixelSize: 30
+ text: sys.pvCharger.power.text
+ }
+ ]
+ }
+ Tile {
+ id: dcSystem
+ width: pwColumn.width
+ height: visible ? pwColumn.tileHeight : 0
+ title: qsTr("DC SYSTEM")
+ color: "#16a085"
+ visible: sys.dcSystem.power.valid
+
+ values: [
+ TileText {
+ font.pixelSize: 30
+ text: sys.dcSystem.power.format(0)
+ },
+ TileText {
+ text: !sys.dcSystem.power.valid ? "---" :
+ sys.dcSystem.power.value < 0 ? qsTr("to battery") : qsTr("from battery")
+ }
+ ]
+ }
+ }
+ }
+
+ Tile {
+ id: logoTile
+
+ color: "#575748"
+ height: 120
+ anchors {
+ left: pwColumn.right
+ right: tanksColum.left
+ top: parent.top
+ }
+
+ MbIcon {
+ x: 1
+ y: 1
+ // see below, so the svg instead of a png if there is a 1x1 image
+ visible: customImage.sourceSize.width === 1 && customImage.sourceSize.height === 1
+ iconId: "mobile-builder-logo-svg"
+ }
+
+ // The uploaded png, the default is a 1x1 transparent pixel now.
+ Image {
+ id: customImage
+ source: "image://theme/mobile-builder-logo"
+ anchors.centerIn: parent
+ }
+ }
+
+ Tile {
+ id: batteryTile
+ height: 112
+ title: qsTr("BATTERY")
+ anchors {
+ left: pwColumn.right
+ right: stateTile.left
+ top: logoTile.bottom
+ bottom: acModeButton.top
+ }
+
+ values: [
+ TileText {
+ text: sys.battery.soc.absFormat(0)
+ font.pixelSize: 30
+ height: 32
+ },
+ TileText {
+ text: {
+ if (!sys.battery.state.valid)
+ return "---"
+ switch(sys.battery.state.value) {
+ case sys.batteryStateIdle: return qsTr("idle")
+ case sys.batteryStateCharging : return qsTr("charging")
+ case sys.batteryStateDischarging : return qsTr("discharging")
+ }
+ }
+ },
+ TileText {
+ text: sys.battery.power.absFormat(0)
+ },
+ TileText {
+ text: sys.battery.voltage.format(1) + " " + sys.battery.current.format(1)
+ }
+ ]
+ }
+
+ Tile {
+ id: stateTile
+
+ width: 104
+ title: qsTr("STATUS")
+ color: "#4789d0"
+
+ anchors {
+ right: tanksColum.left
+ top: logoTile.bottom
+ bottom: acModeButton.top
+ }
+
+ Timer {
+ id: wallClock
+
+ running: true
+ repeat: true
+ interval: 1000
+ triggeredOnStart: true
+ onTriggered: time = Qt.formatDateTime(new Date(), "hh:mm")
+
+ property string time
+ }
+
+ values: [
+ TileText {
+ id: systemTile
+ text: wallClock.time
+ font.pixelSize: 30
+ },
+ TileText {
+ property VeQuickItem gpsService: VeQuickItem { uid: "dbus/com.victronenergy.system/GpsService" }
+ property VeQuickItem speed: VeQuickItem { uid: Utils.path("dbus/", gpsService.value, "/Speed") }
+ property VeQuickItem speedUnit: VeQuickItem { uid: "dbus/com.victronenergy.settings/Settings/Gps/SpeedUnit" }
+
+ text: speed.value === undefined ? "" : getValue()
+ visible: speed.value !== undefined && speedUnit.value !== undefined
+
+ function getValue()
+ {
+ if (speedUnit.value === "km/h")
+ return (speed.value * 3.6).toFixed(1) + speedUnit.value
+ if (speedUnit.value === "mph")
+ return (speed.value * 2.236936).toFixed(1) + speedUnit.value
+ if (speedUnit.value === "kt")
+ return (speed.value * (3600/1852)).toFixed(1) + speedUnit.value
+ return speed.value.toFixed(2) + "m/s"
+ }
+ },
+ Marquee {
+ text: notificationText()
+ width: stateTile.width
+ interval: 100
+ fontSize: 13
+ }
+ ]
+ }
+
+ ListView {
+ id: tanksColum
+
+ property int tileHeight: Math.ceil(height / Math.max(count, 2))
+ width: 134
+ interactive: false // static tiles
+ model: TankModel { id: tankModel }
+ delegate: TileTank {
+ // Without an intermediate assignment this will trigger a binding loop warning.
+ property variant theService: DBusServices.get(buddy.id)
+ service: theService
+ width: tanksColum.width
+ height: tanksColum.tileHeight
+ pumpBindPrefix: root.pumpBindPreffix
+ compact: tankModel.rowCount > (pumpButton.pumpEnabled ? 4 : 5)
+ Connections {
+ target: scrollTimer
+ onTriggered: doScroll()
+ }
+ }
+
+ anchors {
+ top: root.top
+ bottom: pumpButton.pumpEnabled ? acModeButton.top : acModeButton.bottom
+ right: root.right
+ }
+
+ // Synchronise tank name text scroll start
+ Timer {
+ id: scrollTimer
+ interval: 15000
+ repeat: true
+ running: root.active && tankModel.rowCount > 4
+ }
+
+ Tile {
+ title: qsTr("TANKS")
+ anchors.fill: parent
+ values: TileText {
+ text: qsTr("No tanks found")
+ width: parent.width
+ wrapMode: Text.WordWrap
+ }
+ z: -1
+ }
+ }
+
+ Keys.forwardTo: [keyHandler]
+
+ Item {
+ id: keyHandler
+ Keys.onLeftPressed: {
+ if (buttonIndex > 0)
+ buttonIndex--
+
+ event.accepted = true
+ }
+
+ Keys.onRightPressed: {
+ if (buttonIndex < (pumpButton.pumpEnabled ? 2 : 1))
+ buttonIndex++
+
+ event.accepted = true
+ }
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ enabled: parent.active
+ onPressed: mouse.accepted = acCurrentButton.expanded
+ onClicked: acCurrentButton.cancel()
+ }
+
+ TileSpinBox {
+ id: acCurrentButton
+
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
+ isCurrentItem: (buttonIndex == 0)
+ focus: root.active && isCurrentItem
+
+ bind: Utils.path(vebusPrefix, "/Ac/ActiveIn/CurrentLimit")
+ title: qsTr("AC CURRENT LIMIT")
+ color: containsMouse && !editMode ? "#d3d3d3" : "#A8A8A8"
+ width: pumpButton.pumpEnabled ? 160 : 173
+ fontPixelSize: 14
+ unit: "A"
+ readOnly: currentLimitIsAdjustable.value !== 1 || numberOfMultis > 1
+ buttonColor: "#979797"
+
+ VBusItem { id: currentLimitIsAdjustable; bind: Utils.path(vebusPrefix, "/Ac/ActiveIn/CurrentLimitIsAdjustable") }
+
+ Keys.onSpacePressed: showErrorToast(event)
+
+ function editIsAllowed() {
+ if (numberOfMultis > 1) {
+ toast.createToast(qsTr("It is not possible to change this setting when there are more than one inverter connected."), 5000)
+ return false
+ }
+
+ if (currentLimitIsAdjustable.value === 0) {
+ if (dmc.valid) {
+ toast.createToast(noAdjustableByDmc, 5000)
+ return false
+ }
+ if (bms.valid) {
+ toast.createToast(noAdjustableByBms, 5000)
+ return false
+ }
+ if (!dmc.valid && !bms.valid) {
+ toast.createToast(noAdjustableTextByConfig, 5000)
+ return false
+ }
+ }
+
+ return true
+ }
+
+ function showErrorToast(event) {
+ editIsAllowed()
+ event.accepted = true
+ }
+ }
+
+ Tile {
+ id: acModeButton
+ anchors.left: acCurrentButton.right
+ anchors.bottom: parent.bottom
+ property variant texts: { 4: qsTr("OFF"), 3: qsTr("ON"), 1: qsTr("CHARGER ONLY"), 2: qsTr("INVERTER ONLY") }
+ property int value: mode.valid ? mode.value : 3
+ property int shownValue: applyAnimation2.running ? applyAnimation2.pendingValue : value
+
+ isCurrentItem: (buttonIndex == 1)
+ focus: root.active && isCurrentItem
+
+ editable: true
+ readOnly: !modeIsAdjustable.valid || modeIsAdjustable.value !== 1 || numberOfMultis > 1
+ width: pumpButton.pumpEnabled ? 160 : 173
+ height: 45
+ color: acModeButtonMouseArea.containsPressed ? "#d3d3d3" : "#A8A8A8"
+ title: qsTr("AC MODE")
+
+ values: [
+ TileText {
+ text: modeIsAdjustable.valid && numberOfMultis === 1 ? qsTr("%1").arg(acModeButton.texts[acModeButton.shownValue]) : qsTr("NOT AVAILABLE")
+ }
+ ]
+
+ VBusItem { id: mode; bind: Utils.path(vebusPrefix, "/Mode") }
+ VBusItem { id: modeIsAdjustable; bind: Utils.path(vebusPrefix,"/ModeIsAdjustable") }
+
+ Keys.onSpacePressed: edit()
+
+ function edit() {
+ if (!mode.valid)
+ return
+
+ if (numberOfMultis > 1) {
+ toast.createToast(qsTr("It is not possible to change this setting when there are more than one inverter connected."), 5000)
+ return
+ }
+
+ if (modeIsAdjustable.value === 0) {
+ if (dmc.valid)
+ toast.createToast(noAdjustableByDmc, 5000)
+ if (bms.valid)
+ toast.createToast(noAdjustableByBms, 5000)
+ if (!dmc.valid && !bms.valid)
+ toast.createToast(noAdjustableTextByConfig, 5000)
+ return
+ }
+
+ switch (shownValue) {
+ case 4:
+ applyAnimation2.pendingValue = 3
+ break;
+ case 3:
+ applyAnimation2.pendingValue = 1
+ break;
+ case 2:
+ applyAnimation2.pendingValue = 4
+ break;
+ case 1:
+ applyAnimation2.pendingValue = 2
+ break;
+ }
+
+ applyAnimation2.restart()
+ }
+
+ MouseArea {
+ id: acModeButtonMouseArea
+ anchors.fill: parent
+ property bool containsPressed: containsMouse && pressed
+ onClicked: {
+ buttonIndex = 1
+ parent.edit()
+ }
+ }
+
+ Rectangle {
+ id: timerRect2
+ height: 2
+ width: acModeButton.width * 0.8
+ visible: applyAnimation2.running
+ anchors {
+ bottom: parent.bottom; bottomMargin: 5
+ horizontalCenter: parent.horizontalCenter
+ }
+ }
+
+ SequentialAnimation {
+ id: applyAnimation2
+
+ property int pendingValue
+
+ NumberAnimation {
+ target: timerRect2
+ property: "width"
+ from: 0
+ to: acModeButton.width * 0.8
+ duration: 3000
+ }
+
+ ColorAnimation {
+ target: acModeButton
+ property: "color"
+ from: "#A8A8A8"
+ to: "#4789d0"
+ duration: 200
+ }
+
+ ColorAnimation {
+ target: acModeButton
+ property: "color"
+ from: "#4789d0"
+ to: "#A8A8A8"
+ duration: 200
+ }
+ PropertyAction {
+ target: timerRect2
+ property: "width"
+ value: 0
+ }
+
+ ScriptAction { script: mode.setValue(applyAnimation2.pendingValue) }
+
+ PauseAnimation { duration: 1000 }
+ }
+ }
+
+ Tile {
+ id: pumpButton
+
+ anchors.left: acModeButton.right
+ anchors.bottom: parent.bottom
+
+ property variant texts: [ qsTr("AUTO"), qsTr("ON"), qsTr("OFF")]
+ property int value: 0
+ property bool reset: false
+ property bool pumpEnabled: pumpRelay.value === 3
+
+ show: pumpEnabled
+ isCurrentItem: (buttonIndex == 2)
+ focus: root.active && isCurrentItem
+
+ title: qsTr("PUMP")
+ width: show ? 160 : 0
+ height: 45
+ editable: true
+ readOnly: false
+ color: pumpButtonMouseArea.containsPressed ? "#d3d3d3" : "#A8A8A8"
+
+ VBusItem { id: pump; bind: Utils.path(settingsBindPreffix, "/Settings/Pump0/Mode") }
+ VBusItem { id: pumpRelay; bind: Utils.path(settingsBindPreffix, "/Settings/Relay/Function") }
+
+ values: [
+ TileText {
+ text: pumpButton.pumpEnabled ? qsTr("%1").arg(pumpButton.texts[pumpButton.value]) : qsTr("DISABLED")
+ }
+ ]
+
+ Keys.onSpacePressed: edit()
+
+ function edit() {
+ if (!pumpEnabled) {
+ toast.createToast(qsTr("Pump functionality is not enabled. To enable it go to the relay settings page and set function to \"Tank pump\""), 5000)
+ return
+ }
+
+ reset = true
+ applyAnimation.restart()
+ reset = false
+
+ if (value < 2)
+ value++
+ else
+ value = 0
+ }
+
+ MouseArea {
+ id: pumpButtonMouseArea
+ property bool containsPressed: containsMouse && pressed
+ anchors.fill: parent
+ onClicked: {
+ buttonIndex = 2
+ parent.edit()
+ }
+ }
+
+ Rectangle {
+ id: timerRect
+ height: 2
+ width: pumpButton.width * 0.8
+ visible: applyAnimation.running
+ anchors {
+ bottom: parent.bottom; bottomMargin: 5
+ horizontalCenter: parent.horizontalCenter
+ }
+ }
+
+ SequentialAnimation {
+ id: applyAnimation
+ alwaysRunToEnd: false
+ NumberAnimation {
+ target: timerRect
+ property: "width"
+ from: 0
+ to: pumpButton.width * 0.8
+ duration: 3000
+ }
+
+ ColorAnimation {
+ target: pumpButton
+ property: "color"
+ from: "#A8A8A8"
+ to: "#4789d0"
+ duration: 200
+ }
+
+ ColorAnimation {
+ target: pumpButton
+ property: "color"
+ from: "#4789d0"
+ to: "#A8A8A8"
+ duration: 200
+ }
+ PropertyAction {
+ target: timerRect
+ property: "width"
+ value: 0
+ }
+ // Do not set value if the animation is restarted by user pressing the button
+ // to move between options
+ onRunningChanged: if (!running && !pumpButton.reset) pump.setValue(pumpButton.value)
+ }
+ }
+
+ // When new service is found check if is a tank sensor
+ Connections {
+ target: DBusServices
+ onDbusServiceFound: addService(service)
+ }
+
+ function addService(service)
+ {
+ if (service.type === DBusService.DBUS_SERVICE_MULTI) {
+ numberOfMultis++
+ if (vebusPrefix === "")
+ vebusPrefix = service.name;
+ }
+ }
+
+ // Check available services to find tank sesnsors
+ function discoverMulti()
+ {
+ for (var i = 0; i < DBusServices.count; i++) {
+ if (DBusServices.at(i).type === DBusService.DBUS_SERVICE_MULTI) {
+ addService(DBusServices.at(i))
+ }
+ }
+ }
+
+ function notificationText()
+ {
+ if (activeNotifications.length === 0)
+ return qsTr("no alarms")
+
+ var descr = []
+ for (var n = 0; n < activeNotifications.length; n++) {
+ var notification = activeNotifications[n];
+
+ var text = notification.serviceName + " - " + notification.description;
+ if (notification.value !== "" )
+ text += ": " + notification.value
+
+ descr.push(text)
+ }
+
+ return descr.join(" | ")
+ }
+
+ VBusItem { id: dmc; bind: Utils.path(vebusPrefix, "/Devices/Dmc/Version") }
+ VBusItem { id: bms; bind: Utils.path(vebusPrefix, "/Devices/Bms/Version") }
+}
diff --git a/FileSets/v3.60~25/PageGenerator.qml.USE_ORIGINAL b/FileSets/v3.60~25/PageGenerator.qml.USE_ORIGINAL
new file mode 100644
index 00000000..e69de29b
diff --git a/FileSets/v3.60~25/PageGenerator.qml.orig b/FileSets/v3.60~25/PageGenerator.qml.orig
new file mode 100644
index 00000000..3dd70448
--- /dev/null
+++ b/FileSets/v3.60~25/PageGenerator.qml.orig
@@ -0,0 +1,96 @@
+import QtQuick 2
+import "utils.js" as Utils
+
+MbPage {
+ id: root
+ title: qsTr("Generator start/stop")
+ property string settingsBindPrefix
+ property string startStopBindPrefix
+ property bool showRunTime: true
+ property alias startStopModel: _startStopModel
+ property VBusItem activeCondition: VBusItem { bind: Utils.path(startStopBindPrefix, "/RunningByCondition") }
+ property VBusItem generatorState: VBusItem { bind: Utils.path(startStopBindPrefix, "/State") }
+ property VBusItem runningTime: VBusItem { bind: Utils.path(startStopBindPrefix, "/Runtime") }
+
+ FnGeneratorStates {
+ id: genState
+ }
+
+ model: startStopModel
+
+ function formatError(text, value)
+ {
+ return "#" + value.toString() + " " + text
+ }
+
+ VisibleItemModel {
+ id: _startStopModel
+
+ MbSwitch {
+ name: qsTr("Auto start functionality")
+ bind: Utils.path(startStopBindPrefix, "/AutoStartEnabled")
+ show: startStopBindPrefix === "com.victronenergy.generator.startstop0"
+ }
+
+ MbSubMenu {
+ description: qsTr("Manual start")
+ show: startStopBindPrefix === "com.victronenergy.generator.startstop0"
+ subpage:
+ Component {
+ PageGeneratorManualStart {
+ startStopBindPrefix: root.startStopBindPrefix
+ }
+ }
+ }
+
+ MbItemValue {
+ description: qsTr("Current run time")
+ item.text: runningTime.valid ? Utils.secondsToNoSecsString(runningTime.value) : "0"
+ show: generatorState.value >= 1 && generatorState.value <= 3 // Running, Warm-up, Cool-down
+ }
+
+ MbItemValue {
+ description: qsTr("State")
+ show: startStopBindPrefix === "com.victronenergy.generator.startstop0"
+ item.text: activeCondition.valid ? genState.getState(generatorState.value, activeCondition.value) : '---'
+ }
+
+ MbItemOptions {
+ id: _gensetStatus
+ description: qsTr("Error")
+ bind: Utils.path(startStopBindPrefix, "/Error")
+ readonly: true
+ show: valid && startStopBindPrefix === "com.victronenergy.generator.startstop0"
+ possibleValues: [
+ MbOption { description: qsTr("No error"); value: 0 },
+ MbOption { description: formatError(qsTr("Remote switch control disabled"), 1); value: 1 },
+ MbOption { description: formatError(qsTr("Generator in fault condition"), 2); value: 2 },
+ MbOption { description: formatError(qsTr("Generator not detected at AC input"), 3); value: 3 }
+ ]
+ }
+
+ MbSubMenu {
+ id: conditions
+ description: qsTr("Settings")
+ subpage: Component {
+ PageSettingsGenerator {
+ settingsBindPrefix: root.settingsBindPrefix
+ startStopBindPrefix: root.startStopBindPrefix
+ }
+ }
+ }
+
+ MbSubMenu {
+ id: runtimePage
+ description: qsTr("Run time and service")
+ subpage:
+ Component {
+ PageGeneratorRuntimeService {
+ title: qsTr("Run time and service")
+ settingsBindPrefix: root.settingsBindPrefix
+ startStopBindPrefix: root.startStopBindPrefix
+ }
+ }
+ }
+ }
+}
diff --git a/FileSets/v3.60~25/PageSettingsGuiMods.qml b/FileSets/v3.60~25/PageSettingsGuiMods.qml
new file mode 100644
index 00000000..0ad96985
--- /dev/null
+++ b/FileSets/v3.60~25/PageSettingsGuiMods.qml
@@ -0,0 +1,290 @@
+/////// new menu for all Gui Mods
+
+import QtQuick 2
+import "utils.js" as Utils
+import com.victron.velib 1.0
+
+MbPage {
+ id: root
+ title: qsTr("Gui Mods")
+ property string bindPrefixGuiMods: "com.victronenergy.settings/Settings/GuiMods"
+ property string bindPrefix: "com.victronenergy.settings/Settings/Gui"
+ property VBusItem systemScaleItem: VBusItem { bind: "com.victronenergy.settings/Settings/System/Units/Temperature" }
+
+ property bool showFlowParams: flowOverview.item.valid && flowOverview.item.value >= 1
+ property bool showComplexParams: flowOverview.item.valid && flowOverview.item.value >= 2
+ property bool showAcCoupledParams: flowOverview.item.valid && flowOverview.item.value == 3
+
+ model: VisibleItemModel
+ {
+ MbSwitch
+ {
+ id: showTileOverview
+ bind: Utils.path (bindPrefixGuiMods, "/ShowTileOverview")
+ name: qsTr ("Show Tile Overview")
+ writeAccessLevel: User.AccessUser
+ }
+
+ MbSwitch
+ {
+ id: moveSettings
+ bind: Utils.path (bindPrefixGuiMods, "/MoveSettings")
+ name: qsTr ("Move Settings to top of Device List")
+ writeAccessLevel: User.AccessUser
+ }
+
+ MbSwitch {
+ id: relayOverview
+ bind: Utils.path (bindPrefixGuiMods, "/ShowRelayOverview")
+ name: qsTr ("Show Relay overview")
+ writeAccessLevel: User.AccessUser
+ }
+ MbSwitch {
+ id: tanksTempsOverview
+ bind: Utils.path (bindPrefixGuiMods, "/ShowTanksTempsDigIn")
+ name: qsTr ("Show Tanks, Temps, Digital Input overview")
+ writeAccessLevel: User.AccessUser
+ }
+
+ MbSwitch
+ {
+ id: useEnhGeneratorOverview
+ bind: Utils.path (bindPrefixGuiMods, "/UseEnhancedGeneratorOverview")
+ name: qsTr ("Use Enhanced Generator Overview")
+ writeAccessLevel: User.AccessUser
+ }
+
+ // duplicate mobile overview on/off here for convenience
+ MbSwitch {
+ id: mobileOverview
+ bind: Utils.path (bindPrefix, "/MobileOverview")
+ name: qsTr ("Show boat & motorhome overview")
+ writeAccessLevel: User.AccessUser
+ }
+ MbSwitch
+ {
+ id: useEnhMobileOverview
+ bind: Utils.path (bindPrefixGuiMods, "/UseEnhancedMobileOverview")
+ name: qsTr ("Use Enhanced Mobile Overview")
+ // When enabled set Enhanced OverviewMobile as default overview
+ onClicked:
+ {
+ if (!checked)
+ {
+ // also enable Mobile Overview when turning on use enhanced Mobile Overview
+ showMobileOverview.setValue (1)
+ }
+ }
+ VBusItem { id: showMobileOverview; bind: Utils.path (bindPrefix, "/MobileOverview") }
+ writeAccessLevel: User.AccessUser
+ }
+ MbItemOptions
+ {
+ id: flowOverview
+ description: qsTr("Flow overview")
+ bind: Utils.path (bindPrefixGuiMods, "/FlowOverview")
+ possibleValues:
+ [
+ MbOption {description: qsTr("Victron stock"); value: 0},
+ MbOption {description: qsTr("GuiMods simple"); value: 1},
+ MbOption {description: qsTr("GuiMods DC Coupled"); value: 2},
+ MbOption {description: qsTr("GuiMods AC Coupled"); value: 3}
+ ]
+ }
+
+ MbSwitch
+ {
+ id: combineLoads
+ bind: Utils.path (bindPrefixGuiMods, "/EnhancedFlowCombineLoads")
+ name: qsTr ("Combine AC input/ouput loads")
+ show: root.showAcCoupledParams
+ writeAccessLevel: User.AccessInstaller
+ }
+ MbSwitch
+ {
+ id: showLoadsOnInput
+ bind: Utils.path (bindPrefixGuiMods, "/ShowEnhancedFlowLoadsOnInput")
+ name: qsTr ("Show Loads On Input")
+ show: root.showAcCoupledParams && ! combineLoads.checked
+ writeAccessLevel: User.AccessInstaller
+ }
+
+ MbSwitch
+ {
+ id: showTanks
+ bind: Utils.path (bindPrefixGuiMods, "/ShowEnhancedFlowOverviewTanks")
+ name: qsTr ("Show tanks on Flow Overview")
+ show: root.showFlowParams
+ writeAccessLevel: User.AccessUser
+ }
+ MbItemOptions
+ {
+ id: tankFormat
+ description: qsTr("Tank bar format")
+ bind: Utils.path (bindPrefixGuiMods, "/TankBarFormat")
+ possibleValues:
+ [
+ MbOption {description: qsTr("%"); value: 1},
+ MbOption {description: qsTr("units"); value: 2},
+ MbOption {description: qsTr("% + units"); value: 0}
+ ]
+ }
+ MbSwitch
+ {
+ id: showTemps
+ bind: Utils.path (bindPrefixGuiMods, "/ShowEnhancedFlowOverviewTemps")
+ name: qsTr ("Show temperatures on Flow Overview")
+ show: root.showFlowParams
+ writeAccessLevel: User.AccessUser
+ }
+ MbSwitch
+ {
+ id: showBatteryTemps
+ bind: Utils.path (bindPrefixGuiMods, "/ShowBatteryTempOnFlows")
+ name: qsTr ("Show battery temperature on Flow Overview")
+ show: showTemps.item.value == 1
+ writeAccessLevel: User.AccessUser
+ }
+ MbSwitch
+ {
+ id: shortenTankNames
+ bind: Utils.path (bindPrefixGuiMods, "/ShortenTankNames")
+ name: qsTr ("Shorten tank names")
+ writeAccessLevel: User.AccessUser
+ }
+ MbEditBox {
+ id: dcSystemName
+ description: qsTr("DC System tile name")
+ item.bind: Utils.path (bindPrefixGuiMods, "/CustomDcSystemName")
+ maximumLength: 32
+ enableSpaceBar: true
+ }
+
+ MbSwitch
+ {
+ id: replaceInactiveAcIn
+ bind: Utils.path (bindPrefixGuiMods, "/ReplaceInactiveAcIn")
+ name: qsTr ("Replace AC in if inactive")
+ writeAccessLevel: User.AccessUser
+ }
+
+ MbSpinBox {
+ description: qsTr ("AC Input Limit Preset 1")
+ item
+ {
+ bind: Utils.path (bindPrefixGuiMods, "/AcCurrentLimit/Preset1")
+ unit: "A"
+ decimals: 0
+ step: 1
+ min: 0
+ max: 999
+ }
+ writeAccessLevel: User.AccessUser
+ }
+
+ MbSpinBox {
+ description: qsTr ("AC Input Limit Preset 2")
+ item
+ {
+ bind: Utils.path (bindPrefixGuiMods, "/AcCurrentLimit/Preset2")
+ unit: "A"
+ decimals: 0
+ step: 1
+ min: 0
+ max: 999
+ }
+ writeAccessLevel: User.AccessUser
+ }
+
+ MbSpinBox {
+ description: qsTr ("AC Input Limit Preset 3")
+ item
+ {
+ bind: Utils.path (bindPrefixGuiMods, "/AcCurrentLimit/Preset3")
+ unit: "A"
+ decimals: 0
+ step: 1
+ min: 0
+ max: 999
+ }
+ writeAccessLevel: User.AccessUser
+ }
+
+ MbSpinBox {
+ description: qsTr ("AC Input Limit Preset 4")
+ item
+ {
+ bind: Utils.path (bindPrefixGuiMods, "/AcCurrentLimit/Preset4")
+ unit: "A"
+ decimals: 0
+ step: 1
+ min: 0
+ max: 999
+ }
+ writeAccessLevel: User.AccessUser
+ }
+
+ MbItemOptions
+ {
+ id: tempScale
+ description: qsTr ("Temperature scale")
+ bind: Utils.path (bindPrefixGuiMods, "/TemperatureScale")
+ show: ! systemScaleItem.valid
+ possibleValues:
+ [
+ MbOption { description: "°C"; value: 1 },
+ MbOption { description: "°F"; value: 2 },
+ MbOption { description: qsTr("both °C & °F"); value: 0 }
+ ]
+ writeAccessLevel: User.AccessUser
+ }
+
+ MbSpinBox {
+ description: qsTr ("Watt / Kilowatt threshold")
+ item
+ {
+ bind: Utils.path (bindPrefixGuiMods, "/KilowattThreshold")
+ unit: "W"
+ decimals: 0
+ step: 100
+ min: 1000
+ max: 10000
+ }
+ writeAccessLevel: User.AccessUser
+ }
+
+ MbItemOptions
+ {
+ id: timeFormat
+ description: qsTr ("Time format")
+ bind: Utils.path (bindPrefixGuiMods, "/TimeFormat")
+ possibleValues:
+ [
+ MbOption { description: qsTr("24 hour"); value: 1 },
+ MbOption { description: qsTr("12 hour AM/PM"); value: 2 },
+ MbOption { description: qsTr("don't show time"); value: 0 }
+ ]
+ writeAccessLevel: User.AccessUser
+ }
+ MbItemOptions
+ {
+ id: inactiveFlowTiles
+ description: qsTr ("Inactive Tiles on Flow Overview")
+ bind: Utils.path (bindPrefixGuiMods, "/ShowInactiveFlowTiles")
+ show: root.showFlowParams
+ possibleValues:
+ [
+ MbOption { description: qsTr("Show Dimmed"); value: 1 },
+ MbOption { description: qsTr("Show Full"); value: 2 },
+ MbOption { description: qsTr("Hide"); value: 0 }
+ ]
+ writeAccessLevel: User.AccessUser
+ }
+ MbSubMenu
+ {
+ description: qsTr("Power Gauges")
+ subpage: Component { PageSettingsGuiModsGauges {} }
+ show: root.showFlowParams
+ }
+ }
+}
diff --git a/FileSets/v3.60~25/PageSettingsGuiMods.qml.orig b/FileSets/v3.60~25/PageSettingsGuiMods.qml.orig
new file mode 100644
index 00000000..99d438bc
--- /dev/null
+++ b/FileSets/v3.60~25/PageSettingsGuiMods.qml.orig
@@ -0,0 +1,373 @@
+import QtQuick 2
+import com.victron.velib 1.0
+import "utils.js" as Utils
+
+MbPage {
+ property string cgwacsPath: "com.victronenergy.settings/Settings/CGwacs"
+ property string settingsPrefix: "com.victronenergy.settings"
+ property string batteryLifePath: cgwacsPath + "/BatteryLife"
+ // Hub4Mode
+ property int hub4PhaseCompensation: 1
+ property int hub4PhaseSplit: 2
+ property int hub4Disabled: 3
+ // BatteryLifeState
+ property int batteryLifeStateDisabled: 0
+ property int batteryLifeStateRestart: 1
+ property int batteryLifeStateDefault: 2
+ property int batteryLifeStateAbsorption: 3
+ property int batteryLifeStateFloat: 4
+ property int batteryLifeStateDischarged: 5
+ property int batteryLifeStateForceCharge: 6
+ property int batteryLifeStateSustain: 7
+ property int batteryLifeStateLowSocCharge: 8
+ property int batteryKeepCharged: 9
+ property int batterySocGuardDefault: 10
+ property int batterySocGuardDischarged: 11
+ property int batterySocGuardLowSocCharge: 12
+
+ property VBusItem systemType: VBusItem { bind: "com.victronenergy.system/SystemType" }
+ property VBusItem maxChargePowerItem: VBusItem { bind: Utils.path(cgwacsPath, "/MaxChargePower") }
+ property VBusItem maxDischargePowerItem: VBusItem { bind: Utils.path(cgwacsPath, "/MaxDischargePower") }
+ property VBusItem socLimitItem: VBusItem { bind: Utils.path(batteryLifePath, "/SocLimit") }
+ property VBusItem minSocLimitItem: VBusItem { bind: Utils.path(batteryLifePath, "/MinimumSocLimit") }
+ property VBusItem stateItem: VBusItem { bind: Utils.path(batteryLifePath, "/State") }
+ property VBusItem hub4Mode: VBusItem { bind: Utils.path(cgwacsPath, "/Hub4Mode") }
+ property VBusItem maxChargeCurrentControl: VBusItem { bind: "com.victronenergy.system/Control/MaxChargeCurrent" }
+ property VBusItem scheduleSoc: VBusItem { bind: "com.victronenergy.system/Control/ScheduledSoc" }
+ property VBusItem dEssModeItem: VBusItem { bind: "com.victronenergy.settings/Settings/DynamicEss/Mode" }
+
+ title: systemType.value === "Hub-4" ? systemType.value : qsTr("ESS")
+ model: acSystems.rowCount > 0 ? hasAcSystem : (systemType.value === "ESS" || systemType.value === "Hub-4" ? hub4Settings : noHub4)
+
+ property VeQItemSortTableModel acSystems: VeQItemSortTableModel {
+ filterFlags: VeQItemSortTableModel.FilterOffline
+ dynamicSortFilter: true
+ filterRole: VeQItemTableModel.UniqueIdRole
+ filterRegExp: "^dbus/com\.victronenergy\.acsystem\."
+ model: DBusServices
+ }
+
+ VisibleItemModel {
+ id: noHub4
+
+ MbItemText {
+ text: qsTr("No ESS Assistant found")
+ }
+ }
+
+ VisibleItemModel {
+ id: hasAcSystem
+
+ MbItemText {
+ text: qsTr("For Multi-RS and HS19 devices, ESS settings\n" +
+ "can be accessed from the device list")
+ }
+ }
+
+ function isBatteryLifeActive(state) {
+ switch (state) {
+ case batteryLifeStateRestart:
+ case batteryLifeStateDefault:
+ case batteryLifeStateAbsorption:
+ case batteryLifeStateFloat:
+ case batteryLifeStateDischarged:
+ case batteryLifeStateForceCharge:
+ case batteryLifeStateSustain:
+ case batteryLifeStateLowSocCharge:
+ return true
+ default:
+ return false
+ }
+ }
+
+ function isBatterySocGuardActive(state) {
+ switch (state) {
+ case batterySocGuardDefault:
+ case batterySocGuardDischarged:
+ case batterySocGuardLowSocCharge:
+ return true
+ default:
+ return false
+ }
+ }
+
+ VisibleItemModel {
+ id: hub4Settings
+
+ MbItemOptions {
+ function getLocalValue(hub4Mode, state) {
+ if (hub4Mode === undefined || state === undefined)
+ return undefined
+ if (hub4Mode === hub4Disabled)
+ return 3
+ if (isBatteryLifeActive(state))
+ return 0
+ if (isBatterySocGuardActive(state))
+ return 1
+ if (state === batteryKeepCharged)
+ return 2
+ return 0
+ }
+
+ description: qsTr("Mode")
+ localValue: getLocalValue(hub4Mode.value, stateItem.value)
+ possibleValues:[
+ MbOption { description: qsTr("Optimized (with BatteryLife)"); value: 0 },
+ MbOption { description: qsTr("Optimized (without BatteryLife)"); value: 1 },
+ MbOption { description: qsTr("Keep batteries charged"); value: 2 },
+ MbOption { description: qsTr("External control"); value: 3 }
+ ]
+ onLocalValueChanged: {
+ if (localValue === undefined)
+ return
+ // Hub 4 mode
+ if (localValue === 3 && hub4Mode.value !== hub4Disabled) {
+ hub4Mode.setValue(hub4Disabled)
+ } else if (localValue !== 3 && hub4Mode.value === hub4Disabled) {
+ hub4Mode.setValue(hub4PhaseCompensation)
+ }
+ // BatteryLife state
+ switch (localValue) {
+ case 0:
+ if (!isBatteryLifeActive(stateItem.value))
+ stateItem.setValue(batteryLifeStateRestart)
+ break
+ case 1:
+ if (!isBatterySocGuardActive(stateItem.value))
+ stateItem.setValue(batterySocGuardDefault)
+ break
+ case 2:
+ stateItem.setValue(batteryKeepCharged)
+ break
+ case 3:
+ stateItem.setValue(batteryLifeStateDisabled)
+ break
+ }
+ }
+ }
+
+ MbItemOptions {
+ id: withoutGridMeter
+ description: qsTr("Grid metering")
+ bind: Utils.path(cgwacsPath, '/RunWithoutGridMeter')
+ show: hub4Mode.value !== hub4Disabled
+ enabled: userHasWriteAccess
+ possibleValues:[
+ MbOption { description: qsTr("External meter"); value: 0 },
+ MbOption { description: qsTr("Inverter/Charger"); value: 1 }
+ ]
+ }
+
+ MbSwitch {
+ id: acOutInUse
+ bind: Utils.path(settingsPrefix, "/Settings/SystemSetup/HasAcOutSystem")
+ name: qsTr("Inverter AC output in use")
+ show: withoutGridMeter.value == 0
+ }
+
+ MbItemOptions {
+ description: qsTr("Self-consumption from battery")
+ bind: Utils.path(cgwacsPath, "/BatteryUse")
+ show: withoutGridMeter.value == 0 && acOutInUse.item.value == 1
+ possibleValues:[
+ MbOption { description: qsTr("All system loads"); value: 0 },
+ MbOption { description: qsTr("Only critical loads"); value: 1 }
+ ]
+ }
+
+ MbItemOptions {
+ description: qsTr("Multiphase regulation")
+ bind: hub4Mode.bind
+ show: hub4Mode.value !== hub4Disabled && stateItem.value !== batteryKeepCharged
+ enabled: userHasWriteAccess
+ possibleValues:[
+ MbOption { description: qsTr("Total of all phases"); value: hub4PhaseCompensation },
+ MbOption { description: qsTr("Individual phase"); value: hub4PhaseSplit }
+ ]
+ onOptionSelected: {
+ if (newValue === hub4PhaseSplit) {
+ toast.createToast(qsTr("Each phase is regulated to individually achieve the grid setpoint (system efficiency is decreased).\n\n" +
+ "CAUTION: Use only if required by the utility provider"), 15000);
+ } else if (newValue === hub4PhaseCompensation) {
+ toast.createToast(qsTr("The total of all phases is intelligently regulated to achieve the grid setpoint (system efficiency is optimised).\n\n" +
+ "Use unless prohibited by the utility provider"), 15000);
+ }
+ }
+ }
+
+ MbSpinBox {
+ id: minSocLimit
+ description: qsTr("Minimum SOC (unless grid fails)")
+ enabled: userHasWriteAccess
+ show: hub4Mode.value !== hub4Disabled && stateItem.value !== batteryKeepCharged
+ item {
+ bind: Utils.path(batteryLifePath, "/MinimumSocLimit")
+ decimals: 0
+ unit: "%"
+ min: 0
+ max: 100
+ step: 5
+ }
+ }
+
+ MbItemValue {
+ id: socLimit
+ description: qsTr("Active SOC limit")
+ show: hub4Mode.value !== hub4Disabled && isBatteryLifeActive(stateItem.value)
+ item {
+ value: Math.max(minSocLimitItem.value, socLimitItem.value)
+ unit: '%'
+ }
+ }
+
+ MbItemOptions {
+ description: qsTr("BatteryLife state")
+ value: stateItem.value
+ readonly: true
+ show: hub4Mode.value !== hub4Disabled && isBatteryLifeActive(stateItem.value)
+ possibleValues:[
+ // Values below taken from MaintenanceState enum in dbus-cgwacs
+ MbOption { description: qsTr("Self-consumption"); value: 2 },
+ MbOption { description: qsTr("Self-consumption"); value: 3 },
+ MbOption { description: qsTr("Self-consumption"); value: 4 },
+ MbOption { description: qsTr("Discharge disabled"); value: 5 },
+ MbOption { description: qsTr("Slow charge"); value: 6 },
+ MbOption { description: qsTr("Sustain"); value: 7 },
+ MbOption { description: qsTr("Recharge"); value: 8 }
+ ]
+ }
+
+ MbSwitch {
+ id: maxChargePowerSwitch
+ name: qsTr("Limit charge power")
+ checked: maxChargePowerItem.value >= 0
+ enabled: userHasWriteAccess
+ show: hub4Mode.value !== hub4Disabled && !(maxChargeCurrentControl.valid && maxChargeCurrentControl.value)
+ onCheckedChanged: {
+ if (checked && maxChargePowerItem.value < 0)
+ maxChargePowerItem.setValue(1000)
+ else if (!checked && maxChargePowerItem.value >= 0)
+ maxChargePowerItem.setValue(-1)
+ }
+ }
+
+ MbSpinBox {
+ id: maxChargePower
+ description: qsTr("Maximum charge power")
+ enabled: userHasWriteAccess
+ show: maxChargePowerSwitch.show && maxChargePowerSwitch.checked
+ item {
+ bind: Utils.path(cgwacsPath, "/MaxChargePower")
+ decimals: 0
+ unit: "W"
+ min: 0
+ max: 200000
+ step: 50
+ }
+ }
+
+ MbSwitch {
+ id: maxInverterPowerSwitch
+ name: qsTr("Limit inverter power")
+ checked: maxDischargePowerItem.value >= 0
+ enabled: userHasWriteAccess
+ show: hub4Mode.value !== hub4Disabled && stateItem.value !== batteryKeepCharged
+ onCheckedChanged: {
+ if (checked && maxDischargePowerItem.value < 0)
+ maxDischargePowerItem.setValue(1000)
+ else if (!checked && maxDischargePowerItem.value >= 0)
+ maxDischargePowerItem.setValue(-1)
+ }
+ }
+
+ MbSpinBox {
+ id: maxDischargePower
+ description: qsTr("Maximum inverter power")
+ enabled: userHasWriteAccess
+ show: maxInverterPowerSwitch.show && maxInverterPowerSwitch.checked
+ item {
+ bind: Utils.path(cgwacsPath, "/MaxDischargePower")
+ decimals: 0
+ unit: "W"
+ min: 0
+ max: 300000
+ step: 50
+ }
+ }
+
+ MbSpinBox {
+ description: qsTr("Grid setpoint")
+ show: hub4Mode.value !== hub4Disabled
+ enabled: userHasWriteAccess
+ item {
+ bind: "com.victronenergy.settings/Settings/CGwacs/AcPowerSetPoint"
+ decimals: 0
+ unit: "W"
+ step: 10
+ }
+ }
+
+ MbSubMenu {
+ id: feedinSetupItem
+ description: qsTr("Grid feed-in")
+ show: hub4Mode.value !== hub4Disabled
+ subpage: Component {
+ PageSettingsHub4Feedin {
+ title: feedinSetupItem.description
+ }
+ }
+ }
+
+ MbSubMenu {
+ id: peakShaveSetupMenu
+ description: qsTr("Peak shaving")
+ show: hub4Mode.value !== hub4Disabled
+ subpage: Component {
+ PageSettingsHub4Peakshaving {
+ title: peakShaveSetupMenu.description
+ }
+ }
+ }
+
+ MbSubMenu {
+ id: scheduleSettings
+ property string bindPrefix: "com.victronenergy.settings/Settings/CGwacs/BatteryLife/Schedule/Charge/"
+ description: qsTr("Scheduled charge levels")
+ show: hub4Mode.value !== hub4Disabled && stateItem.value !== batteryKeepCharged
+ item: VBusItem { value: scheduleSoc.valid ? qsTr("Active (%1)").arg(scheduleSoc.text) : qsTr("Inactive") }
+ subpage: Component {
+ MbPage {
+ title: scheduleSettings.description
+ model: VisibleItemModel {
+ ChargeScheduleItem { bindPrefix: scheduleSettings.bindPrefix; scheduleNumber: 0 }
+ ChargeScheduleItem { bindPrefix: scheduleSettings.bindPrefix; scheduleNumber: 1 }
+ ChargeScheduleItem { bindPrefix: scheduleSettings.bindPrefix; scheduleNumber: 2 }
+ ChargeScheduleItem { bindPrefix: scheduleSettings.bindPrefix; scheduleNumber: 3 }
+ ChargeScheduleItem { bindPrefix: scheduleSettings.bindPrefix; scheduleNumber: 4 }
+ }
+ }
+ }
+ }
+
+ MbSubMenu {
+ id: dEssSetupItem
+ description: qsTr("Dynamic ESS")
+ show: (dEssModeItem.value > 0 || user.accessLevel >= User.AccessService) && hub4Mode.value !== hub4Disabled && stateItem.value !== batteryKeepCharged
+ subpage: Component {
+ PageSettingsDynamicEss {
+ title: dEssSetupItem.description
+ }
+ }
+ }
+
+ MbSubMenu {
+ id: deviceItem
+ description: qsTr("Debug")
+ show: hub4Mode.value !== hub4Disabled && user.accessLevel >= User.AccessService
+ backgroundColor: mbStyle.backgroundColorService
+ subpage: Component {
+ PageHub4Debug { }
+ }
+ }
+ }
+}
diff --git a/FileSets/v3.60~25/PowerGauge.qml b/FileSets/v3.60~25/PowerGauge.qml
new file mode 100644
index 00000000..9593259b
--- /dev/null
+++ b/FileSets/v3.60~25/PowerGauge.qml
@@ -0,0 +1,320 @@
+// displays value as a bar surrounded by three range regions
+// use for I/O, PV inverter & charger
+
+import QtQuick 2
+import "utils.js" as Utils
+import com.victron.velib 1.0
+
+Item {
+ id: root
+
+////// GuiMods — DarkMode
+ property VBusItem darkModeItem: VBusItem { bind: "com.victronenergy.settings/Settings/GuiMods/DarkMode" }
+ property bool darkMode: darkModeItem.valid && darkModeItem.value == 1
+
+ property variant connection
+ // connection2 accommodates combined PV inverter AC input and AC output
+ property variant connection2: undefined
+ property bool includeConnection2: connection2 != undefined
+
+ property bool reversePower: false
+ property bool useInputCurrentLimit: false
+ property variant endLabelFontSize: 16
+ property color endLabelBackgroundColor: "transparent"
+
+ property int reportedPhaseCount1: connection == undefined ? 0 : connection.phaseCount == undefined || ! connection.phaseCount.valid ? 1 : connection.phaseCount.value
+ property int phaseCount1: reportedPhaseCount1 < 2 ? reportedPhaseCount1 : (connection.isAcOutput ? connection.l2AndL1OutSummed : connection.splitPhaseL2PassthruDisabled) ? 1 : connection.phaseCount.value
+
+ property int reportedPhaseCount2: connection2 == undefined ? 0 : connection2.phaseCount == undefined || ! connection2.phaseCount.valid ? 1 : connection2.phaseCount.value
+ property int phaseCount2: reportedPhaseCount2 < 2 ? reportedPhaseCount2 : (connection2.isAcOutput ? connection2.l2AndL1OutSummed : connection2.splitPhaseL2PassthruDisabled) ? 1 : connection2.phaseCount.value
+
+ property int phaseCount: includeConnection2 ? Math.max (phaseCount1, phaseCount2) : phaseCount1
+
+ property string maxForwardPowerParameter: ""
+ VBusItem { id: maxForwardLimitItem; bind: root.maxForwardPowerParameter }
+ property string maxForwardPowerParameter2: ""
+ VBusItem { id: maxForwardLimitItem2; bind: root.maxForwardPowerParameter2 }
+
+ property string maxReversePowerParameter: ""
+ VBusItem { id: maxReverseLimitItem; bind: root.maxReversePowerParameter }
+
+ property real inPowerLimit: sys.acInput.inCurrentLimit.valid ? sys.acInput.inCurrentLimit.value * sys.acInput.voltageL1.value : 0
+
+ property real maxForwardPower1: maxForwardLimitItem.valid ? maxForwardLimitItem.value : 0
+ property real maxForwardPower2: maxForwardLimitItem2.valid ? maxForwardLimitItem2.value : 0
+ property real maxForwardLimit: useInputCurrentLimit ? inPowerLimit : maxForwardPower1 + maxForwardPower2
+ property real maxReverseLimit: maxReverseLimitItem.valid ? maxReverseLimitItem.value : 0
+ // overload range is 10% of forward to reverse limits
+ property real overload: (maxForwardLimit + maxReverseLimit) * 0.1
+ property real maxForwardDisplayed: maxForwardLimit > 0 ? maxForwardLimit + overload : 0
+ property real maxReverseDisplayed: maxReverseLimit > 0 ? maxReverseLimit + overload : 0
+ property real totalPowerDisplayed: maxForwardDisplayed + maxReverseDisplayed
+
+ property bool showLabels: false
+ property variant endLabelColor: "white"
+ property real labelOffset: 15
+ property real showLeftLabel: showGauge && showLabels && maxReverseLimit != 0
+ property bool showRightLabel: showGauge && showLabels && maxForwardLimit != 0
+ property int labelCount: (showLeftLabel ? 1 : 0) + (showRightLabel ? 1 : 0)
+
+ property bool showGauge: root.connection != undefined && totalPowerDisplayed > 0 && phaseCount > 0
+ property real scaleFactor: showGauge ? (root.width - (labelCount * labelOffset)) / totalPowerDisplayed : 0
+ property real zeroOffset: showGauge ? ( maxReverseDisplayed * scaleFactor + (showLeftLabel ? labelOffset : 0 )) : 0
+
+ property int barSpacing: phaseCount > 0 ? Math.max (height / (phaseCount + 1), 2) : 0
+ property int barHeight: barSpacing < 3 ? barSpacing : barSpacing - 1
+ property int firstBarVertPos: (height - barSpacing * phaseCount) / 2
+ property real bar1offset
+ property real bar2offset
+ property real bar3offset
+
+ property color bar1color: "black"
+ property color bar2color: "black"
+ property color bar3color: "black"
+
+ // left end label
+ Rectangle
+ {
+ anchors.fill: leftlabelText
+ color: endLabelBackgroundColor
+ visible: showLeftLabel
+ }
+ TileText
+ {
+ id: leftlabelText
+ text: "S"
+ color: endLabelColor
+ font.pixelSize: endLabelFontSize
+ width: labelOffset
+ anchors
+ {
+ verticalCenter: root.verticalCenter
+ verticalCenterOffset: 1
+ left: root.left
+ }
+ visible: showLeftLabel
+ }
+ // right end label
+ Rectangle
+ {
+ anchors.fill: rightLabelText
+ color: endLabelBackgroundColor
+ visible: showRightLabel
+ }
+ TileText
+ {
+ id: rightLabelText
+ text: "C"
+ color: endLabelColor
+ font.pixelSize: endLabelFontSize
+ width: labelOffset
+ anchors
+ {
+ verticalCenter: leftlabelText.verticalCenter
+ right: root.right
+ }
+ visible: showRightLabel
+ }
+ // overload range Left
+ Rectangle
+ {
+ id: overloadLeft
+ width: showGauge ? scaleFactor * (maxReverseDisplayed - maxReverseLimit) : 0
+ height: root.height
+ clip: true
+////// GuiMods — DarkMode
+ color: !darkMode ? "#ffb3b3" : "#bf8686"
+ visible: showGauge
+ anchors
+ {
+ top: root.top
+ left: root.left; leftMargin: showLeftLabel ? labelOffset : 0
+ }
+ }
+ // OK range (both left and right in a single rectangle)
+ Rectangle
+ {
+ id: okRange
+ width: showGauge ? scaleFactor * (maxForwardLimit + maxReverseLimit) : 0
+ height: root.height
+ clip: true
+////// GuiMods — DarkMode
+ color: !darkMode ? "#99ff99" : "#73bf73"
+ visible: showGauge
+ anchors
+ {
+ top: root.top
+ left: overloadLeft.right
+ }
+ }
+ // overload range right
+ Rectangle
+ {
+ id: overloadRight
+ width: showGauge ? scaleFactor * (maxForwardDisplayed - maxForwardLimit) : 0
+ height: root.height
+ clip: true
+////// GuiMods — DarkMode
+ color: !darkMode ? "#ffb3b3" : "#bf8686"
+ visible: showGauge
+ anchors
+ {
+ top: root.top
+ left: okRange.right
+ }
+ }
+
+ // actual bars
+ Rectangle
+ {
+ id: bar1
+ width: phaseCount >= 1 ? calculateBar1width () : 0
+ height: barHeight
+ clip: true
+ color: root.bar1color
+ anchors
+ {
+ top: root.top; topMargin: firstBarVertPos
+ left: root.left; leftMargin: root.bar1offset
+
+ }
+ visible: showGauge && phaseCount >= 1
+ }
+ Rectangle
+ {
+ id: bar2
+ width: phaseCount >= 2 ? calculateBar2width () : 0
+ height: barHeight
+ clip: true
+ color: root.bar2color
+ anchors
+ {
+ top: root.top; topMargin: firstBarVertPos + barSpacing
+ left: root.left; leftMargin: root.bar2offset
+ }
+ visible: showGauge && phaseCount >= 2
+ }
+ Rectangle
+ {
+ id: bar3
+ width: phaseCount >= 3 ? calculateBar3width () : 0
+ height: barHeight
+ clip: true
+ color: root.bar3color
+ anchors
+ {
+ top: root.top; topMargin: firstBarVertPos + barSpacing * 2
+ left: root.left; leftMargin: root.bar3offset
+ }
+ visible: showGauge && phaseCount >= 3
+ }
+
+ // zero line - draw last so it's on top
+ Rectangle
+ {
+ id: zeroLine
+ width: 1
+ height: root.height
+ clip: true
+ color: "black"
+ visible: showGauge && maxReverseLimit > 0
+ anchors
+ {
+ top: root.top
+ left: root.left
+ leftMargin: zeroOffset
+ }
+ }
+
+ function calculateBar1width ()
+ {
+ var currentValue = 0.0, barWidth
+ if (root.connection.powerL1 != undefined)
+ currentValue += root.connection.powerL1.valid ? root.connection.powerL1.value : 0
+ else if (root.connection.power != undefined)
+ currentValue += root.connection.power.valid ? root.connection.power.value : 0
+ if (includeConnection2)
+ {
+ if (root.connection2.powerL1 != undefined)
+ currentValue += root.connection2.powerL1.valid ? root.connection2.powerL1.value : 0
+ else if (root.connection2.power != undefined)
+ currentValue += root.connection2.power.valid ? root.connection2.power.value : 0
+ }
+
+ if (reversePower)
+ currentValue = -currentValue
+
+ root.bar1color = getBarColor (currentValue)
+ barWidth = Math.min ( Math.max (currentValue, -maxReverseDisplayed), maxForwardDisplayed) * scaleFactor
+ // left of bar is at 0 point
+ if (barWidth >= 0)
+ {
+ root.bar1offset = zeroOffset
+ return barWidth
+ }
+ // RIGHT of bar is at 0 point
+ else
+ {
+ root.bar1offset = zeroOffset + barWidth
+ return -barWidth
+ }
+ }
+ function calculateBar2width ()
+ {
+ var currentValue, barWidth
+ currentValue = root.connection.powerL2.valid ? root.connection.powerL2.value : 0
+ if (includeConnection2)
+ currentValue += root.connection2.powerL2.valid ? root.connection2.powerL2.value : 0
+
+ if (reversePower)
+ currentValue = -currentValue
+ root.bar2color = getBarColor (currentValue)
+ barWidth = Math.min ( Math.max (currentValue, -maxReverseDisplayed), maxForwardDisplayed) * scaleFactor
+ // left of bar is at 0 point
+ if (barWidth >= 0)
+ {
+ root.bar2offset = zeroOffset
+ return barWidth
+ }
+ // RIGHT of bar is at 0 point
+ else
+ {
+ root.bar2offset = zeroOffset + barWidth
+ return -barWidth
+ }
+ }
+ function calculateBar3width ()
+ {
+ var currentValue, barWidth
+ currentValue = root.connection.powerL3.valid ? root.connection.powerL3.value : 0
+ if (includeConnection2)
+ currentValue += root.connection2.powerL3.valid ? root.connection2.powerL3.value : 0
+
+ if (reversePower)
+ currentValue = -currentValue
+ root.bar3color = getBarColor (currentValue)
+ barWidth = Math.min ( Math.max (currentValue, -maxReverseDisplayed), maxForwardDisplayed) * scaleFactor
+ // left of bar is at 0 point
+ if (barWidth >= 0)
+ {
+ root.bar3offset = zeroOffset
+ return barWidth
+ }
+ // RIGHT of bar is at 0 point
+ else
+ {
+ root.bar3offset = zeroOffset + barWidth
+ return -barWidth
+ }
+ }
+
+ function getBarColor (currentValue)
+ {
+ if (currentValue > maxForwardLimit || currentValue < -maxReverseLimit)
+////// GuiMods — DarkMode
+ return !darkMode ? "#ff0000" : "#bf0000"
+ else
+////// GuiMods — DarkMode
+ return !darkMode ? "#008000" : "#006000"
+ }
+}
diff --git a/FileSets/v3.60~25/PowerGauge.qml.orig b/FileSets/v3.60~25/PowerGauge.qml.orig
new file mode 100644
index 00000000..6efa32bf
--- /dev/null
+++ b/FileSets/v3.60~25/PowerGauge.qml.orig
@@ -0,0 +1,170 @@
+import QtQuick 2
+import "utils.js" as Utils
+
+Item {
+ id: root
+
+ property string systemPrefix: "com.victronenergy.system"
+ property string vebusPrefix: _vebusService.valid ? _vebusService.value : ""
+
+ property variant battery: _battery
+ property alias dcSystem: _dcSystem
+ property alias pvCharger: _pvCharger
+ property alias pvOnAcIn1: _pvOnAcIn1
+ property alias pvOnAcIn2: _pvOnAcIn2
+ property alias pvOnAcOut: _pvOnAcOut
+ property alias inverterChargerDc: _inverterChargerDc
+ property alias acLoad: _acLoad
+ property alias acInLoad: _acInLoad
+ property alias acOutLoad: _acOutLoad
+ property alias grid: _grid
+ property alias genset: _genset
+ property alias acInput: _activein
+ property VBusItem systemType: VBusItem { bind: Utils.path(systemPrefix, "/SystemType") }
+ property bool hasGridMeter: _hasGridMeter.valid
+ property variant acSource: _acSource.value
+ property VBusItem preferRenewableEnergy: VBusItem { bind: Utils.path(vebusPrefix, "/Dc/0/PreferRenewableEnergy") }
+ property VBusItem remoteGeneratorSelected: VBusItem { bind: Utils.path(vebusPrefix, "/Ac/State/RemoteGeneratorSelected") }
+
+ property alias pvOnGrid: _pvOnAcIn2
+
+ property int batteryStateIdle: 0
+ property int batteryStateCharging: 1
+ property int batteryStateDischarging: 2
+
+ property int acSourceNotAvailable: 0
+ property int acSourceGrid: 1
+ property int acSourceGenset: 2
+ property int acSourceShore: 3 // same as grid
+
+ property alias pvInvertersProductIds: _pvInvertersProductIds
+ property alias batteryProductId: _batteryProductId
+
+ VBusItem {
+ id: _pvInvertersProductIds
+ bind: Utils.path(systemPrefix, "/PvInvertersProductIds")
+ }
+
+ VBusItem {
+ id: _batteryProductId
+ bind: Utils.path(systemPrefix, "/Dc/Battery/ProductId")
+ }
+
+ VBusItem {
+ id: _vebusService
+ bind: Utils.path(systemPrefix, "/VebusService")
+ }
+
+ QtObject {
+ id: _pvCharger
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Pv/Power"); unit: "W"}
+ }
+
+ ObjectAcConnection {
+ id: _pvOnAcOut
+ bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnOutput")
+ }
+
+ ObjectAcConnection {
+ id: _pvOnAcIn1
+ bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGenset")
+ }
+
+ ObjectAcConnection {
+ id: _pvOnAcIn2
+ bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGrid")
+ }
+
+ ObjectAcConnection {
+ id: _genset
+ splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0
+ bindPrefix: Utils.path(systemPrefix, "/Ac/Genset")
+ }
+
+ VBusItem {
+ id: _acSource
+ bind: Utils.path(systemPrefix, "/Ac/ActiveIn/Source")
+ }
+
+ VBusItem {
+ id: _hasGridMeter
+ bind: Utils.path(systemPrefix, "/Ac/Grid/DeviceType")
+ }
+
+ /*
+ * Single Multis that can be split-phase reports NrOfPhases of 2
+ * When L2 is disconnected from the input the output L1 and L2
+ * are shorted. This item indicates if L2 is passed through
+ * from AC-in to AC-out.
+ * 1: L2 is being passed through from AC-in to AC-out.
+ * 0: L1 and L2 are shorted together.
+ * invalid: The unit is configured in such way that its L2 output is not used.
+ */
+
+ VBusItem {
+ id: _splitPhaseL2Passthru
+ bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2Passthru")
+ }
+
+ VBusItem {
+ id: _l2L1OutSummed
+ bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2L1OutSummed")
+ }
+
+
+ ObjectAcConnection {
+ id: _grid
+ splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0
+ bindPrefix: Utils.path(systemPrefix, "/Ac/Grid")
+ }
+
+ ObjectAcConnection {
+ id: _activein
+ bindPrefix: Utils.path(systemPrefix, "/Ac/ActiveIn")
+ }
+
+ ObjectAcConnection {
+ id: _acLoad
+ l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0)
+ isAcOutput: true
+ bindPrefix: Utils.path(systemPrefix, "/Ac/Consumption")
+ }
+
+ ObjectAcConnection {
+ id: _acOutLoad
+ l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0)
+ isAcOutput: true
+ bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnOutput")
+ }
+
+ ObjectAcConnection {
+ id: _acInLoad
+ splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0
+ bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnInput")
+ }
+
+ ObjectAcConnection {
+ id: _acUnknown
+ }
+
+ QtObject {
+ id: _inverterChargerDc
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/InverterCharger/Power"); unit: "W"}
+ }
+
+ QtObject {
+ id: _battery
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Power"); unit: "W"}
+ property VBusItem voltage: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Voltage"); unit: "V"}
+ property VBusItem current: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Current"); unit: "A"}
+ property VBusItem soc: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Soc"); unit: "%"}
+
+ // Get the battery charge state, see batteryState properties
+ property VBusItem state: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/State")}
+ }
+
+ QtObject {
+ id: _dcSystem
+ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/System/Power"); unit: "W"}
+ }
+}
diff --git a/FileSets/v3.60~25/TileDigIn.qml b/FileSets/v3.60~25/TileDigIn.qml
new file mode 100644
index 00000000..7bea849c
--- /dev/null
+++ b/FileSets/v3.60~25/TileDigIn.qml
@@ -0,0 +1,133 @@
+// New for GuiMods to display digital inputs
+// based on TileTank.qml
+
+import QtQuick 2
+import "utils.js" as Utils
+import "tanksensor.js" as TankSensor
+
+Tile {
+ id: root
+
+ property string bindPrefix: serviceName
+ property VBusItem nameItem: VBusItem { bind: Utils.path(bindPrefix, "/CustomName") }
+ property VBusItem deviceItem: VBusItem { bind: Utils.path(bindPrefix, "/DeviceInstance") }
+ property VBusItem aggregateItem: VBusItem { bind: Utils.path(bindPrefix, "/Aggregate") }
+ property string digInName: nameItem.valid && nameItem.value != "" ? nameItem.value : getType (type)
+ property VBusItem typeItem: VBusItem { bind: Utils.path(bindPrefix, "/Type") }
+ property VBusItem stateItem: VBusItem { bind: Utils.path(bindPrefix, "/State") }
+ property bool isPulseCounter: aggregateItem.valid
+ // pulse counter doesn't have /Type so fill it in here
+ property int type: isPulseCounter ? 1 : typeItem.valid ? typeItem.value : 0
+
+ property variant bkgdColors: [ "#b3b3b3", "#4aa3df", "#1abc9c", "#F39C12", "#95a5a6", "#95a5a6","#dcc6e0", "#f1a9a0", "#7f8c8d", "#ebbc3a" ]
+ property color bkgdColor: type > 0 && type < 10 ? bkgdColors [type] : "#b3b3b3"
+ property variant units: ["m3", "L", "gal", "gal"]
+
+
+ function getType(type)
+ {
+ switch (type)
+ {
+ case 0:
+ return qsTr("Disabled")
+ case 1:
+ return qsTr("Pulse meter")
+ case 2:
+ return qsTr("Door alarm")
+ case 3:
+ return qsTr("Bilge pump")
+ case 4:
+ return qsTr("Bilge alarm")
+ case 5:
+ return qsTr("Burglar alarm")
+ case 6:
+ return qsTr("Smoke alarm")
+ case 7:
+ return qsTr("Fire alarm")
+ case 8:
+ return qsTr("CO2 alarm")
+ case 9:
+ return qsTr("Generator")
+ case 10:
+ return qsTr("Generic I/O")
+//// added for ExtTransferSwitch package
+ case 11:
+ return qsTr("Touch enable")
+ case 12:
+ return qsTr("Transfer switch")
+ default:
+ return "Unknown"
+ }
+ }
+
+ function getState(st)
+ {
+ switch (st)
+ {
+ case 0:
+ return qsTr("Low")
+ case 1:
+ return qsTr("High")
+ case 2:
+ return qsTr("Off")
+ case 3:
+ return qsTr("On")
+ case 4:
+ return qsTr("No")
+ case 5:
+ return qsTr("Yes")
+ case 6:
+ return qsTr("Open")
+ case 7:
+ return qsTr("Closed")
+ case 8:
+ return qsTr("Ok")
+ case 9:
+ return qsTr("Alarm")
+ case 10:
+ return qsTr("Running")
+ case 11:
+ return qsTr("Stopped")
+//// added for ExtTransferSwitch package
+ case 12:
+ return qsTr("On Generator")
+ case 13:
+ return qsTr("On Grid")
+ default:
+ return qsTr("Unknown")
+ }
+
+ }
+
+ title: digInName + " (In " + (deviceItem.valid ? (deviceItem.value.toString ()) : "?") + ")"
+
+ color: bkgdColor
+
+ VBusItem
+ {
+ id: unitItem
+ bind: Utils.path("com.victronenergy.settings/Settings/System/VolumeUnit")
+ }
+
+ values: Item
+ {
+ width: root.width - 10
+ height: 12
+ TileText
+ {
+ width: root.width
+ text:
+ {
+ if (isPulseCounter)
+ return aggregateItem.value.toString() + (unitItem.valid ? units[unitItem.value] : "??")
+ else
+ return stateItem.valid ? getState (stateItem.value) : "??"
+ }
+ horizontalAlignment: Text.AlignHCenter
+ anchors
+ {
+ horizontalCenter: parent.horizontalCenter
+ }
+ }
+ }
+}
diff --git a/FileSets/v3.60~17/TileDigIn.qml.orig b/FileSets/v3.60~25/TileDigIn.qml.orig
similarity index 100%
rename from FileSets/v3.60~17/TileDigIn.qml.orig
rename to FileSets/v3.60~25/TileDigIn.qml.orig
diff --git a/FileSets/v3.60~25/TileRelay.qml b/FileSets/v3.60~25/TileRelay.qml
new file mode 100644
index 00000000..d8b6feaa
--- /dev/null
+++ b/FileSets/v3.60~25/TileRelay.qml
@@ -0,0 +1,498 @@
+// New for GuiMods to display and control relays on separate overview page
+
+import QtQuick 2
+import "utils.js" as Utils
+
+Tile {
+ id: root
+
+ property string systemPrefix: "com.victronenergy.system"
+ property string settingsPrefix: "com.victronenergy.settings"
+ property string functionPath: relayNumber === 0 ? "/Settings/Relay/Function" : "/Settings/Relay/" + relayNumber + "/Function"
+ property string polarityPath: relayNumber === 0 ? "/Settings/Relay/Polarity" : "/Settings/Relay/" + relayNumber + "/Polarity"
+
+ property int relayFunction: 0
+ property bool relayInverted: polarityItem.valid ? polarityItem.value : false
+ property bool relayActive: flase
+
+ property string activeText: ""
+ property string inactiveText: ""
+ property string offButtonText: ""
+ property string onButtonText: ""
+ property string autoButtonText: ""
+ property string functionText: ""
+ property bool autoButtonActive: false
+ property bool offButtonActive: false
+ property bool onButtonActive: false
+
+////// GuiMods — DarkMode
+ property VBusItem darkModeItem: VBusItem { bind: "com.victronenergy.settings/Settings/GuiMods/DarkMode" }
+ property bool darkMode: darkModeItem.valid && darkModeItem.value == 1
+
+
+ VBusItem
+ {
+ id: stateItem
+ bind: Utils.path(systemPrefix, "/Relay/", relayNumber, "/State")
+ onValueChanged: updateButtons ()
+ }
+ VBusItem
+ {
+ id: nameItem
+ bind: Utils.path(settingsPrefix, "/Settings/Relay/", relayNumber, "/CustomName")
+ }
+ VBusItem
+ {
+ id: functionItem
+ bind: Utils.path(settingsPrefix, functionPath)
+ onValueChanged: updateFunction ()
+ }
+ VBusItem
+ {
+ id: polarityItem
+ bind: Utils.path(settingsPrefix, polarityPath)
+ }
+ VBusItem
+ {
+ id: generatorManualStartItem
+ bind: Utils.path("com.victronenergy.generator.startstop0" , "/ManualStart")
+ onValidChanged: updateButtons ()
+ onValueChanged: updateButtons ()
+ }
+ VBusItem
+ {
+ id: generatorAutoRunItem
+ bind: Utils.path(settingsPrefix, "/Settings/Generator0/AutoStartEnabled")
+ onValidChanged: updateButtons ()
+ onValueChanged: updateButtons ()
+ }
+ VBusItem
+ {
+ id: generatorStateItem
+ bind: Utils.path("com.victronenergy.generator.startstop0" , "/State")
+ }
+ VBusItem
+ {
+ id: generatorConditionItem
+ bind: Utils.path("com.victronenergy.generator.startstop0" , "/RunningByConditionCode")
+ }
+ VBusItem
+ {
+ id: generatorExternalOverrideItem
+ bind: Utils.path("com.victronenergy.generator.startstop0" , "/ExternalOverride")
+ }
+ VBusItem
+ {
+ id: pumpModeItem
+ bind: Utils.path(settingsPrefix, "/Settings/Pump0/Mode")
+ onValidChanged: updateButtons ()
+ onValueChanged: updateButtons ()
+ }
+
+ Component.onCompleted: updateFunction ()
+
+////// GuiMods — DarkMode
+ color: !darkMode ? "#d9d9d9" : "#202020"
+ border.color: !darkMode ? "#fff" : "#707070"
+
+ function doScroll()
+ {
+ relayName.doScroll ()
+ relayState.doScroll ()
+ }
+
+ values: Item
+ {
+ anchors.horizontalCenter: parent.horizontalCenter
+ Column
+ {
+ width: root.width
+ spacing: 4
+ visible: true
+ anchors
+ {
+ horizontalCenter: parent.horizontalCenter
+ top: parent.top
+ }
+ Text
+ {
+ font.pixelSize: 12
+ font.bold: true
+////// GuiMods DarkMode
+ color: !darkMode ? "black" : "gray"
+ anchors.horizontalCenter: parent.horizontalCenter
+ horizontalAlignment: Text.AlignHCenter
+ text: "Relay " + (relayNumber + 1)
+ }
+ MarqueeEnhanced
+ {
+ id: relayName
+ width: parent.width - 4
+ text: nameItem.valid && nameItem.value != "" ? nameItem.value : " "
+ fontSize: 12
+ bold: true
+////// GuiMods DarkMode
+ textColor: !darkMode ? "black" : "gray"
+ scroll: false
+ }
+ Text
+ {
+ font.pixelSize: 12
+ font.bold: true
+////// GuiMods DarkMode
+ color: !darkMode ? "black" : "gray"
+ anchors.horizontalCenter: parent.horizontalCenter
+ horizontalAlignment: Text.AlignHCenter
+ text: functionText
+ }
+
+ MarqueeEnhanced
+ {
+ id: relayState
+ width: parent.width - 4
+ fontSize: 12
+ bold: true
+////// GuiMods DarkMode
+ textColor: !darkMode ? "black" : "gray"
+ scroll: false
+ text:
+ {
+ // special handling for generator
+ if (relayFunction == 1)
+ {
+ if (generatorExternalOverrideItem.valid && generatorExternalOverrideItem.value == 1)
+ return qsTr ("External override - stopped")
+ else if (!generatorStateItem.valid)
+ return qsTr ("Error")
+ else if (generatorStateItem.value == 2)
+ return qsTr("Warm-up")
+ else if (generatorStateItem.value == 3)
+ return qsTr("Cool-down")
+ else if (generatorStateItem.value == 4)
+ return qsTr("Stopping")
+ else if (generatorConditionItem.valid)
+ {
+ switch (generatorConditionItem.value)
+ {
+ case 0:
+ return qsTr ("Stopped")
+ case 1:
+ return qsTr ("Man run")
+ case 2:
+ return qsTr ("Test run")
+ case 3:
+ return qsTr ("Loss of comms run")
+ case 4:
+ return qsTr ("SOC run")
+ case 5:
+ return qsTr ("Load run")
+ case 6:
+ return qsTr ("Battery current run")
+ case 7:
+ return qsTr ("Battery voltage run")
+ case 8:
+ return qsTr ("Inverter temperature run")
+ case 9:
+ return qsTr ("Inverter overload run")
+ default:
+ return "??"
+ }
+ }
+ else
+ return "??"
+ }
+ else if (stateItem.valid)
+ {
+ if (relayActive)
+ return activeText
+ else
+ return inactiveText
+ }
+ else
+ return "??"
+ }
+ }
+ // spacer
+ Text
+ {
+ font.pixelSize: 4
+ font.bold: true
+ color: "black"
+ height: 4
+ anchors.horizontalCenter: parent.horizontalCenter
+ horizontalAlignment: Text.AlignHCenter
+ text: " "
+ }
+ Button
+ {
+ id: onButton
+////// GuiMods - DarkMode
+ baseColor: !darkMode ? (onButtonActive ? "green" : "#e6ffe6") : (onButtonActive ? "green" : "#003000")
+ pressedColor: "#979797"
+ height: 40
+ width: parent.width - 6
+ anchors.horizontalCenter: parent.horizontalCenter
+ onClicked: buttonPress (1)
+ content: TileText
+ {
+ text: onButtonText; font.bold: true;
+ color: onButtonActive ? "white" : "black"
+ }
+ }
+ Button
+ {
+ id: offButton
+////// GuiMods - DarkMode
+ baseColor: !darkMode ? (offButtonActive ? "black" : "#e6e6e6") : (offButtonActive ? "black" : "gray")
+ pressedColor: "#979797"
+ height: 40
+ width: parent.width - 6
+ anchors.horizontalCenter: parent.horizontalCenter
+ onClicked: buttonPress (2)
+ content: TileText
+ {
+ text: offButtonText; font.bold: true;
+ color: offButtonActive ? "white" : "black"
+ }
+ }
+ Button
+ {
+ id: autoButton
+////// GuiMods - DarkMode
+ baseColor: !darkMode ? (autoButtonActive ? "orange" : "#ffedcc") : (autoButtonActive ? "orange" : "#3a2600")
+ pressedColor: "#979797"
+ height: 40
+ width: parent.width - 6
+ anchors.horizontalCenter: parent.horizontalCenter
+ onClicked: buttonPress (3)
+ content: TileText
+ {
+ text: autoButtonText; font.bold: true;
+ color: autoButtonActive ? "white" : "black"
+ }
+ }
+ }
+ }
+ function updateFunction ()
+ {
+ if (functionItem.valid)
+ {
+ relayFunction = functionItem.value
+ switch (relayFunction)
+ {
+ // Alarm - no buttons
+ case 0:
+ functionText = qsTr("Alarm")
+ activeText = qsTr("Alarm")
+ inactiveText = qsTr("No Alarm")
+ offButtonText = ""
+ onButtonText = ""
+ autoButtonText = ""
+ onButton.visible = false
+ offButton.visible = false
+ autoButton.visible = false
+ break;;
+ // Generator
+ case 1:
+ functionText = qsTr("Generator")
+ activeText = qsTr("") // generator state text handled below
+ inactiveText = qsTr("")
+ onButtonText = qsTr("Manual\nStart")
+ offButtonText = qsTr("Manual\nStop")
+ autoButtonText = qsTr("Auto\nEnable")
+ onButton.visible = true
+ offButton.visible = true
+ autoButton.visible = true
+ break;;
+ // pump
+ case 3:
+ functionText = qsTr("Pump")
+ activeText = qsTr("On")
+ inactiveText = qsTr("Off")
+ onButtonText = qsTr("On")
+ offButtonText = qsTr("Off")
+ autoButtonText = qsTr("Auto")
+ onButton.visible = true
+ offButton.visible = true
+ autoButton.visible = true
+ break;;
+ // temperature
+ case 4:
+ functionText = qsTr("Temp")
+ activeText = qsTr("Alarm")
+ inactiveText = qsTr("No Alarm")
+ onButtonText = "--"
+ offButtonText = "--"
+ autoButtonText = "--"
+ onButton.visible = false
+ offButton.visible = false
+ autoButton.visible = false
+ break;;
+ // manual (2) and undefined
+ default:
+ functionText = qsTr("Manual")
+ activeText = qsTr("On")
+ inactiveText = qsTr("Off")
+ onButtonText = qsTr("On")
+ offButtonText = qsTr("Off")
+ autoButtonText = ""
+ onButton.visible = true
+ offButton.visible = true
+ autoButton.visible = false
+ break;;
+ }
+ }
+ // only relay 1 has a function selector, so use manual settings for other relays
+ else
+ {
+ relayFunction = 2
+ functionText = qsTr("Manual")
+ activeText = qsTr("On")
+ inactiveText = qsTr("Off")
+ onButtonText = qsTr("On")
+ offButtonText = qsTr("Off")
+ autoButtonText = "--" // empty string causes interactions
+ autoButton.visible = false
+ }
+ updateButtons ()
+ }
+
+ function updateButtons ()
+ {
+ switch (relayFunction)
+ {
+ // alarm - no buttons
+ case 0:
+ break;;
+ // Generator
+ case 1:
+ if (generatorManualStartItem.valid)
+ {
+ onButtonActive = generatorManualStartItem.value === 1
+ offButtonActive = ! onButtonActive
+ }
+ else
+ {
+ offButtonActive = false
+ onButtonActive = false
+ }
+ if (generatorAutoRunItem.valid)
+ autoButtonActive = generatorAutoRunItem.value
+ else
+ autoButtonActive = false
+ break;;
+ // pump
+ case 3:
+ if (pumpModeItem.valid)
+ {
+ switch (pumpModeItem.value)
+ {
+ // Auto
+ case 0:
+ onButtonActive = false
+ offButtonActive = false
+ autoButtonActive = true
+ break;;
+ // On
+ case 1:
+ onButtonActive = true
+ offButtonActive = false
+ autoButtonActive = false
+ break;;
+ // Off
+ case 2:
+ onButtonActive = false
+ offButtonActive = true
+ autoButtonActive = false
+ break;;
+ default:
+ onButtonActive = false
+ offButtonActive = false
+ autoButtonActive = false
+ break;;
+ }
+ }
+ else
+ {
+ offButtonActive = false
+ onButtonActive = false
+ autoButtonActive = false
+ }
+ break;;
+ // manual (2) and undefined
+ default:
+ relayActive = stateItem.value === 1 != relayInverted
+ onButtonActive = relayActive
+ offButtonActive = ! onButtonActive
+ autoButtonActive = false
+ break;;
+ }
+ }
+
+ function buttonPress (button)
+ {
+ switch (relayFunction)
+ {
+ // Generator
+ case 1:
+ switch (button)
+ {
+ // on
+ case 1:
+ generatorManualStartItem.setValue (1)
+ break;;
+ // off
+ case 2:
+ generatorManualStartItem.setValue (0)
+ break;;
+ // auto
+ case 3:
+ // toggle value
+ generatorAutoRunItem.setValue (generatorAutoRunItem.value === 1 ? 0 : 1)
+ break;;
+ default:
+ break;;
+ }
+ break;;
+ // pump
+ case 3:
+ switch (button)
+ {
+ // on
+ case 1:
+ pumpModeItem.setValue (1)
+ break;;
+ // off
+ case 2:
+ pumpModeItem.setValue (2)
+ break;;
+ // auto
+ case 3:
+ pumpModeItem.setValue (0)
+ break;;
+ default:
+ break;;
+ }
+ break;;
+ // alarm - no buttons
+ case 0:
+ break;;
+ // manual (2) and undefined
+ default:
+ switch (button)
+ {
+ // on
+ case 1:
+ stateItem.setValue (1)
+ break;;
+ // off
+ case 2:
+ stateItem.setValue (0)
+ break;;
+ default:
+ break;;
+ }
+ break;;
+ }
+ }
+}
diff --git a/FileSets/v3.60~25/TileRelay.qml.orig b/FileSets/v3.60~25/TileRelay.qml.orig
new file mode 100644
index 00000000..b5d309b3
--- /dev/null
+++ b/FileSets/v3.60~25/TileRelay.qml.orig
@@ -0,0 +1,23 @@
+import QtQuick 2
+import com.victron.velib 1.0
+import "utils.js" as Utils
+
+PageGenerator {
+
+ title: qsTr("Generator start/stop")
+ settingsBindPrefix: "com.victronenergy.settings/Settings/Generator0"
+ startStopBindPrefix: "com.victronenergy.generator.startstop0"
+
+ property VBusItem relayFunction: VBusItem { bind: Utils.path("com.victronenergy.settings", "/Settings/Relay/Function") }
+
+ model: !relayFunction.valid || relayFunction.value === 1 ? startStopModel : disabledModel
+
+ VisibleItemModel {
+ id: disabledModel
+ MbItemText {
+ wrapMode: Text.WordWrap
+ text: qsTr("Generator start/stop function is not enabled, go to relay settings and set " +
+ "function to \"Generator start/stop\"")
+ }
+ }
+}
diff --git a/changes b/changes
index c807cf17..2f7d3cf2 100644
--- a/changes
+++ b/changes
@@ -1,3 +1,7 @@
+v10.81:
+ add support for v3.54, v3.60~25
+ remove unused symbolic links
+
v10.80:
add support for v3.60~17 and v3.53
moved more files from version dependent to patched
diff --git a/firstCompatibleVersion b/firstCompatibleVersion
index ecb23a01..6110b7f3 100644
--- a/firstCompatibleVersion
+++ b/firstCompatibleVersion
@@ -1 +1 @@
-v2.71
\ No newline at end of file
+v3.10
\ No newline at end of file
diff --git a/version b/version
index d2971577..b47fb695 100644
--- a/version
+++ b/version
@@ -1 +1 @@
-v10.80
+v10.81