From 9ad95ed5db5601f7812efb48705768ad42bd118d Mon Sep 17 00:00:00 2001 From: kwindrem <58538395+kwindrem@users.noreply.github.com> Date: Wed, 6 Nov 2024 20:04:43 -0800 Subject: [PATCH] add support for v3.52~1 --- FileSets/v3.10/ObjectAcConnection.qml | 2 +- FileSets/v3.10/OverviewAcValuesEnhanced.qml | 2 +- .../v3.10/OverviewGeneratorRelayEnhanced.qml | 2 +- FileSets/v3.10/TileRelay.qml | 2 +- FileSets/v3.11/ObjectAcConnection.qml | 2 +- FileSets/v3.11/OverviewAcValuesEnhanced.qml | 2 +- .../v3.11/OverviewGeneratorRelayEnhanced.qml | 2 +- FileSets/v3.11/TileRelay.qml | 2 +- FileSets/v3.12/ObjectAcConnection.qml | 2 +- FileSets/v3.12/OverviewAcValuesEnhanced.qml | 2 +- .../v3.12/OverviewGeneratorRelayEnhanced.qml | 2 +- FileSets/v3.12/TileRelay.qml | 2 +- FileSets/v3.13/ObjectAcConnection.qml | 2 +- FileSets/v3.13/OverviewAcValuesEnhanced.qml | 2 +- .../v3.13/OverviewGeneratorRelayEnhanced.qml | 2 +- FileSets/v3.13/TileRelay.qml | 2 +- FileSets/v3.14/ObjectAcConnection.qml | 2 +- FileSets/v3.14/OverviewAcValuesEnhanced.qml | 2 +- .../v3.14/OverviewGeneratorRelayEnhanced.qml | 2 +- FileSets/v3.14/TileRelay.qml | 2 +- FileSets/v3.20/ObjectAcConnection.qml | 2 +- FileSets/v3.20/OverviewAcValuesEnhanced.qml | 2 +- .../v3.20/OverviewGeneratorRelayEnhanced.qml | 2 +- FileSets/v3.20/TileRelay.qml | 2 +- FileSets/v3.21/ObjectAcConnection.qml | 2 +- FileSets/v3.21/OverviewAcValuesEnhanced.qml | 2 +- .../v3.21/OverviewGeneratorRelayEnhanced.qml | 2 +- FileSets/v3.21/TileRelay.qml | 2 +- FileSets/v3.22/ObjectAcConnection.qml | 2 +- FileSets/v3.22/OverviewAcValuesEnhanced.qml | 2 +- .../v3.22/OverviewGeneratorRelayEnhanced.qml | 2 +- FileSets/v3.22/TileRelay.qml | 2 +- FileSets/v3.30/ObjectAcConnection.qml | 2 +- FileSets/v3.30/OverviewAcValuesEnhanced.qml | 2 +- .../v3.30/OverviewGeneratorRelayEnhanced.qml | 2 +- FileSets/v3.30/TileRelay.qml | 2 +- FileSets/v3.31/ObjectAcConnection.qml | 2 +- FileSets/v3.31/OverviewAcValuesEnhanced.qml | 2 +- .../v3.31/OverviewGeneratorRelayEnhanced.qml | 2 +- FileSets/v3.31/TileRelay.qml | 2 +- FileSets/v3.33/ObjectAcConnection.qml | 2 +- FileSets/v3.33/OverviewAcValuesEnhanced.qml | 2 +- .../v3.33/OverviewGeneratorRelayEnhanced.qml | 2 +- FileSets/v3.33/TileRelay.qml | 2 +- FileSets/v3.34/ObjectAcConnection.qml | 2 +- FileSets/v3.34/OverviewAcValuesEnhanced.qml | 2 +- .../v3.34/OverviewGeneratorRelayEnhanced.qml | 2 +- FileSets/v3.34/TileRelay.qml | 2 +- FileSets/v3.40/DetailAcInput.qml | 2 +- FileSets/v3.40/DetailInverter.qml | 2 +- FileSets/v3.40/DetailLoadsCombined.qml | 2 +- FileSets/v3.40/DetailLoadsOnInput.qml | 2 +- FileSets/v3.40/DetailLoadsOnOutput.qml | 2 +- FileSets/v3.40/HubData.qml | 2 +- FileSets/v3.40/ObjectAcConnection.qml | 2 +- FileSets/v3.40/OverviewAcValuesEnhanced.qml | 2 +- FileSets/v3.40/OverviewFlowComplex.qml | 2 +- .../v3.40/OverviewGeneratorRelayEnhanced.qml | 2 +- FileSets/v3.40/OverviewGridParallel.qml | 2 +- FileSets/v3.40/OverviewHub.qml | 2 +- FileSets/v3.40/OverviewHubEnhanced.qml | 2 +- FileSets/v3.40/PowerGauge.qml | 2 +- FileSets/v3.40/TileRelay.qml | 2 +- FileSets/v3.41/DetailAcInput.qml | 2 +- FileSets/v3.41/DetailInverter.qml | 2 +- FileSets/v3.41/DetailLoadsCombined.qml | 2 +- FileSets/v3.41/DetailLoadsOnInput.qml | 2 +- FileSets/v3.41/DetailLoadsOnOutput.qml | 2 +- FileSets/v3.41/HubData.qml | 2 +- FileSets/v3.41/ObjectAcConnection.qml | 2 +- FileSets/v3.41/OverviewAcValuesEnhanced.qml | 2 +- FileSets/v3.41/OverviewFlowComplex.qml | 2 +- .../v3.41/OverviewGeneratorRelayEnhanced.qml | 2 +- FileSets/v3.41/OverviewGridParallel.qml | 2 +- FileSets/v3.41/OverviewHub.qml | 2 +- FileSets/v3.41/OverviewHubEnhanced.qml | 2 +- FileSets/v3.41/PowerGauge.qml | 2 +- FileSets/v3.41/TileRelay.qml | 2 +- FileSets/v3.50/DetailAcInput.qml | 2 +- FileSets/v3.50/DetailInverter.qml | 2 +- FileSets/v3.50/DetailLoadsCombined.qml | 2 +- FileSets/v3.50/DetailLoadsOnInput.qml | 2 +- FileSets/v3.50/DetailLoadsOnOutput.qml | 2 +- FileSets/v3.50/HubData.qml | 2 +- FileSets/v3.50/ObjectAcConnection.qml | 2 +- FileSets/v3.50/OverviewAcValuesEnhanced.qml | 2 +- FileSets/v3.50/OverviewFlowComplex.qml | 2 +- FileSets/v3.50/OverviewGeneratorEnhanced.qml | 2 +- .../v3.50/OverviewGeneratorRelayEnhanced.qml | 2 +- FileSets/v3.50/OverviewGridParallel.qml | 2 +- FileSets/v3.50/OverviewHub.qml | 2 +- FileSets/v3.50/OverviewHubEnhanced.qml | 2 +- FileSets/v3.50/OverviewMobileEnhanced.qml | 2 +- .../v3.50/OverviewTanksTempsDigInputs.qml | 2 +- FileSets/v3.50/PageSettingsGenerator.qml | 2 +- FileSets/v3.50/PageSettingsGuiMods.qml | 2 +- FileSets/v3.50/PageSettingsRelay.qml | 2 +- FileSets/v3.50/PowerGauge.qml | 2 +- FileSets/v3.50/TileDigIn.qml | 2 +- FileSets/v3.50/TileRelay.qml | 2 +- FileSets/v3.50/dbus_digitalinputs.py | 2 +- FileSets/v3.50/startstop.py | 2 +- FileSets/v3.51/DetailAcInput.qml | 607 +------ FileSets/v3.51/DetailInverter.qml | 651 +------ FileSets/v3.51/DetailLoadsCombined.qml | 147 +- FileSets/v3.51/DetailLoadsOnInput.qml | 147 +- FileSets/v3.51/DetailLoadsOnOutput.qml | 152 +- FileSets/v3.51/HubData.qml | 269 +-- FileSets/v3.51/LINKS_ONLY | 0 FileSets/v3.51/ObjectAcConnection.qml | 53 +- FileSets/v3.51/OverviewAcValuesEnhanced.qml | 95 +- FileSets/v3.51/OverviewFlowComplex.qml | 1550 +---------------- FileSets/v3.51/OverviewGeneratorEnhanced.qml | 550 +----- .../v3.51/OverviewGeneratorRelayEnhanced.qml | 9 +- FileSets/v3.51/OverviewGridParallel.qml | 491 +----- FileSets/v3.51/OverviewHub.qml | 316 +--- FileSets/v3.51/OverviewHubEnhanced.qml | 1505 +--------------- FileSets/v3.51/OverviewMobileEnhanced.qml | 990 +---------- .../v3.51/OverviewTanksTempsDigInputs.qml | 208 +-- FileSets/v3.51/PageSettingsGenerator.qml | 116 +- FileSets/v3.51/PageSettingsGuiMods.qml | 291 +--- FileSets/v3.51/PageSettingsRelay.qml | 517 +----- FileSets/v3.51/PowerGauge.qml | 321 +--- FileSets/v3.51/TileDigIn.qml | 134 +- FileSets/v3.51/TileRelay.qml | 499 +----- FileSets/v3.51/dbus_digitalinputs.py | 745 +------- FileSets/v3.51/startstop.py | 1483 +--------------- FileSets/v3.52~1/COMPLETE | 0 FileSets/v3.52~1/DetailAcInput.qml | 606 +++++++ .../{v3.51 => v3.52~1}/DetailAcInput.qml.orig | 0 FileSets/v3.52~1/DetailInverter.qml | 650 +++++++ .../DetailInverter.qml.orig | 0 FileSets/v3.52~1/DetailLoadsCombined.qml | 146 ++ .../DetailLoadsCombined.qml.orig | 0 FileSets/v3.52~1/DetailLoadsOnInput.qml | 146 ++ .../DetailLoadsOnInput.qml.orig | 0 FileSets/v3.52~1/DetailLoadsOnOutput.qml | 151 ++ .../DetailLoadsOnOutput.qml.orig | 0 FileSets/v3.52~1/HubData.qml | 268 +++ FileSets/{v3.51 => v3.52~1}/HubData.qml.orig | 0 FileSets/v3.52~1/ObjectAcConnection.qml | 52 + .../ObjectAcConnection.qml.orig | 0 FileSets/v3.52~1/OverviewAcValuesEnhanced.qml | 94 + .../OverviewAcValuesEnhanced.qml.orig | 0 FileSets/v3.52~1/OverviewFlowComplex.qml | 1549 ++++++++++++++++ .../OverviewFlowComplex.qml.orig | 0 .../v3.52~1/OverviewGeneratorEnhanced.qml | 549 ++++++ .../OverviewGeneratorEnhanced.qml.orig | 0 .../OverviewGeneratorRelayEnhanced.qml | 8 + .../OverviewGeneratorRelayEnhanced.qml.orig | 0 FileSets/v3.52~1/OverviewGridParallel.qml | 490 ++++++ .../OverviewGridParallel.qml.orig | 0 FileSets/v3.52~1/OverviewHub.qml | 315 ++++ .../{v3.51 => v3.52~1}/OverviewHub.qml.orig | 0 FileSets/v3.52~1/OverviewHubEnhanced.qml | 1504 ++++++++++++++++ .../OverviewHubEnhanced.qml.orig | 0 FileSets/v3.52~1/OverviewMobileEnhanced.qml | 989 +++++++++++ .../OverviewMobileEnhanced.qml.orig | 0 .../v3.52~1/OverviewTanksTempsDigInputs.qml | 207 +++ .../OverviewTanksTempsDigInputs.qml.orig | 0 .../v3.52~1/PageGenerator.qml.USE_ORIGINAL | 0 FileSets/v3.52~1/PageSettingsGenerator.qml | 115 ++ .../PageSettingsGenerator.qml.orig | 0 FileSets/v3.52~1/PageSettingsGuiMods.qml | 290 +++ .../PageSettingsGuiMods.qml.orig | 0 FileSets/v3.52~1/PageSettingsRelay.qml | 516 ++++++ .../PageSettingsRelay.qml.orig | 0 FileSets/v3.52~1/PowerGauge.qml | 320 ++++ .../{v3.51 => v3.52~1}/PowerGauge.qml.orig | 0 FileSets/v3.52~1/TileDigIn.qml | 133 ++ .../{v3.51 => v3.52~1}/TileDigIn.qml.orig | 0 FileSets/v3.52~1/TileRelay.qml | 498 ++++++ .../{v3.51 => v3.52~1}/TileRelay.qml.orig | 0 FileSets/v3.52~1/dbus_digitalinputs.py | 744 ++++++++ .../dbus_digitalinputs.py.orig | 0 FileSets/v3.52~1/startstop.py | 1482 ++++++++++++++++ FileSets/{v3.51 => v3.52~1}/startstop.py.orig | 0 changes | 3 + version | 2 +- 179 files changed, 11952 insertions(+), 11925 deletions(-) mode change 100644 => 120000 FileSets/v3.51/DetailAcInput.qml mode change 100644 => 120000 FileSets/v3.51/DetailInverter.qml mode change 100644 => 120000 FileSets/v3.51/DetailLoadsCombined.qml mode change 100644 => 120000 FileSets/v3.51/DetailLoadsOnInput.qml mode change 100644 => 120000 FileSets/v3.51/DetailLoadsOnOutput.qml mode change 100644 => 120000 FileSets/v3.51/HubData.qml create mode 100644 FileSets/v3.51/LINKS_ONLY mode change 100644 => 120000 FileSets/v3.51/ObjectAcConnection.qml mode change 100644 => 120000 FileSets/v3.51/OverviewAcValuesEnhanced.qml mode change 100644 => 120000 FileSets/v3.51/OverviewFlowComplex.qml mode change 100644 => 120000 FileSets/v3.51/OverviewGeneratorEnhanced.qml mode change 100644 => 120000 FileSets/v3.51/OverviewGeneratorRelayEnhanced.qml mode change 100644 => 120000 FileSets/v3.51/OverviewGridParallel.qml mode change 100644 => 120000 FileSets/v3.51/OverviewHub.qml mode change 100644 => 120000 FileSets/v3.51/OverviewHubEnhanced.qml mode change 100644 => 120000 FileSets/v3.51/OverviewMobileEnhanced.qml mode change 100644 => 120000 FileSets/v3.51/OverviewTanksTempsDigInputs.qml mode change 100644 => 120000 FileSets/v3.51/PageSettingsGenerator.qml mode change 100644 => 120000 FileSets/v3.51/PageSettingsGuiMods.qml mode change 100644 => 120000 FileSets/v3.51/PageSettingsRelay.qml mode change 100644 => 120000 FileSets/v3.51/PowerGauge.qml mode change 100644 => 120000 FileSets/v3.51/TileDigIn.qml mode change 100644 => 120000 FileSets/v3.51/TileRelay.qml mode change 100644 => 120000 FileSets/v3.51/dbus_digitalinputs.py mode change 100644 => 120000 FileSets/v3.51/startstop.py create mode 100644 FileSets/v3.52~1/COMPLETE create mode 100644 FileSets/v3.52~1/DetailAcInput.qml rename FileSets/{v3.51 => v3.52~1}/DetailAcInput.qml.orig (100%) create mode 100644 FileSets/v3.52~1/DetailInverter.qml rename FileSets/{v3.51 => v3.52~1}/DetailInverter.qml.orig (100%) create mode 100644 FileSets/v3.52~1/DetailLoadsCombined.qml rename FileSets/{v3.51 => v3.52~1}/DetailLoadsCombined.qml.orig (100%) create mode 100644 FileSets/v3.52~1/DetailLoadsOnInput.qml rename FileSets/{v3.51 => v3.52~1}/DetailLoadsOnInput.qml.orig (100%) create mode 100644 FileSets/v3.52~1/DetailLoadsOnOutput.qml rename FileSets/{v3.51 => v3.52~1}/DetailLoadsOnOutput.qml.orig (100%) create mode 100644 FileSets/v3.52~1/HubData.qml rename FileSets/{v3.51 => v3.52~1}/HubData.qml.orig (100%) create mode 100644 FileSets/v3.52~1/ObjectAcConnection.qml rename FileSets/{v3.51 => v3.52~1}/ObjectAcConnection.qml.orig (100%) create mode 100644 FileSets/v3.52~1/OverviewAcValuesEnhanced.qml rename FileSets/{v3.51 => v3.52~1}/OverviewAcValuesEnhanced.qml.orig (100%) create mode 100644 FileSets/v3.52~1/OverviewFlowComplex.qml rename FileSets/{v3.51 => v3.52~1}/OverviewFlowComplex.qml.orig (100%) create mode 100644 FileSets/v3.52~1/OverviewGeneratorEnhanced.qml rename FileSets/{v3.51 => v3.52~1}/OverviewGeneratorEnhanced.qml.orig (100%) create mode 100644 FileSets/v3.52~1/OverviewGeneratorRelayEnhanced.qml rename FileSets/{v3.51 => v3.52~1}/OverviewGeneratorRelayEnhanced.qml.orig (100%) create mode 100644 FileSets/v3.52~1/OverviewGridParallel.qml rename FileSets/{v3.51 => v3.52~1}/OverviewGridParallel.qml.orig (100%) create mode 100644 FileSets/v3.52~1/OverviewHub.qml rename FileSets/{v3.51 => v3.52~1}/OverviewHub.qml.orig (100%) create mode 100644 FileSets/v3.52~1/OverviewHubEnhanced.qml rename FileSets/{v3.51 => v3.52~1}/OverviewHubEnhanced.qml.orig (100%) create mode 100644 FileSets/v3.52~1/OverviewMobileEnhanced.qml rename FileSets/{v3.51 => v3.52~1}/OverviewMobileEnhanced.qml.orig (100%) create mode 100644 FileSets/v3.52~1/OverviewTanksTempsDigInputs.qml rename FileSets/{v3.51 => v3.52~1}/OverviewTanksTempsDigInputs.qml.orig (100%) create mode 100644 FileSets/v3.52~1/PageGenerator.qml.USE_ORIGINAL create mode 100644 FileSets/v3.52~1/PageSettingsGenerator.qml rename FileSets/{v3.51 => v3.52~1}/PageSettingsGenerator.qml.orig (100%) create mode 100644 FileSets/v3.52~1/PageSettingsGuiMods.qml rename FileSets/{v3.51 => v3.52~1}/PageSettingsGuiMods.qml.orig (100%) create mode 100644 FileSets/v3.52~1/PageSettingsRelay.qml rename FileSets/{v3.51 => v3.52~1}/PageSettingsRelay.qml.orig (100%) create mode 100644 FileSets/v3.52~1/PowerGauge.qml rename FileSets/{v3.51 => v3.52~1}/PowerGauge.qml.orig (100%) create mode 100644 FileSets/v3.52~1/TileDigIn.qml rename FileSets/{v3.51 => v3.52~1}/TileDigIn.qml.orig (100%) create mode 100644 FileSets/v3.52~1/TileRelay.qml rename FileSets/{v3.51 => v3.52~1}/TileRelay.qml.orig (100%) create mode 100644 FileSets/v3.52~1/dbus_digitalinputs.py rename FileSets/{v3.51 => v3.52~1}/dbus_digitalinputs.py.orig (100%) create mode 100644 FileSets/v3.52~1/startstop.py rename FileSets/{v3.51 => v3.52~1}/startstop.py.orig (100%) diff --git a/FileSets/v3.10/ObjectAcConnection.qml b/FileSets/v3.10/ObjectAcConnection.qml index aad59c8b..a955a0e5 120000 --- a/FileSets/v3.10/ObjectAcConnection.qml +++ b/FileSets/v3.10/ObjectAcConnection.qml @@ -1 +1 @@ -../v3.51/ObjectAcConnection.qml \ No newline at end of file +../v3.52~1/ObjectAcConnection.qml \ No newline at end of file diff --git a/FileSets/v3.10/OverviewAcValuesEnhanced.qml b/FileSets/v3.10/OverviewAcValuesEnhanced.qml index 7ef5c3ad..3752dd11 120000 --- a/FileSets/v3.10/OverviewAcValuesEnhanced.qml +++ b/FileSets/v3.10/OverviewAcValuesEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewAcValuesEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewAcValuesEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.10/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.10/OverviewGeneratorRelayEnhanced.qml index 4f3958c0..ab4d8a4a 120000 --- a/FileSets/v3.10/OverviewGeneratorRelayEnhanced.qml +++ b/FileSets/v3.10/OverviewGeneratorRelayEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.10/TileRelay.qml b/FileSets/v3.10/TileRelay.qml index 9c63a544..e02257ed 120000 --- a/FileSets/v3.10/TileRelay.qml +++ b/FileSets/v3.10/TileRelay.qml @@ -1 +1 @@ -../v3.51/TileRelay.qml \ No newline at end of file +../v3.52~1/TileRelay.qml \ No newline at end of file diff --git a/FileSets/v3.11/ObjectAcConnection.qml b/FileSets/v3.11/ObjectAcConnection.qml index aad59c8b..a955a0e5 120000 --- a/FileSets/v3.11/ObjectAcConnection.qml +++ b/FileSets/v3.11/ObjectAcConnection.qml @@ -1 +1 @@ -../v3.51/ObjectAcConnection.qml \ No newline at end of file +../v3.52~1/ObjectAcConnection.qml \ No newline at end of file diff --git a/FileSets/v3.11/OverviewAcValuesEnhanced.qml b/FileSets/v3.11/OverviewAcValuesEnhanced.qml index 7ef5c3ad..3752dd11 120000 --- a/FileSets/v3.11/OverviewAcValuesEnhanced.qml +++ b/FileSets/v3.11/OverviewAcValuesEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewAcValuesEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewAcValuesEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.11/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.11/OverviewGeneratorRelayEnhanced.qml index 4f3958c0..ab4d8a4a 120000 --- a/FileSets/v3.11/OverviewGeneratorRelayEnhanced.qml +++ b/FileSets/v3.11/OverviewGeneratorRelayEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.11/TileRelay.qml b/FileSets/v3.11/TileRelay.qml index 9c63a544..e02257ed 120000 --- a/FileSets/v3.11/TileRelay.qml +++ b/FileSets/v3.11/TileRelay.qml @@ -1 +1 @@ -../v3.51/TileRelay.qml \ No newline at end of file +../v3.52~1/TileRelay.qml \ No newline at end of file diff --git a/FileSets/v3.12/ObjectAcConnection.qml b/FileSets/v3.12/ObjectAcConnection.qml index aad59c8b..a955a0e5 120000 --- a/FileSets/v3.12/ObjectAcConnection.qml +++ b/FileSets/v3.12/ObjectAcConnection.qml @@ -1 +1 @@ -../v3.51/ObjectAcConnection.qml \ No newline at end of file +../v3.52~1/ObjectAcConnection.qml \ No newline at end of file diff --git a/FileSets/v3.12/OverviewAcValuesEnhanced.qml b/FileSets/v3.12/OverviewAcValuesEnhanced.qml index 7ef5c3ad..3752dd11 120000 --- a/FileSets/v3.12/OverviewAcValuesEnhanced.qml +++ b/FileSets/v3.12/OverviewAcValuesEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewAcValuesEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewAcValuesEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.12/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.12/OverviewGeneratorRelayEnhanced.qml index 4f3958c0..ab4d8a4a 120000 --- a/FileSets/v3.12/OverviewGeneratorRelayEnhanced.qml +++ b/FileSets/v3.12/OverviewGeneratorRelayEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.12/TileRelay.qml b/FileSets/v3.12/TileRelay.qml index 9c63a544..e02257ed 120000 --- a/FileSets/v3.12/TileRelay.qml +++ b/FileSets/v3.12/TileRelay.qml @@ -1 +1 @@ -../v3.51/TileRelay.qml \ No newline at end of file +../v3.52~1/TileRelay.qml \ No newline at end of file diff --git a/FileSets/v3.13/ObjectAcConnection.qml b/FileSets/v3.13/ObjectAcConnection.qml index aad59c8b..a955a0e5 120000 --- a/FileSets/v3.13/ObjectAcConnection.qml +++ b/FileSets/v3.13/ObjectAcConnection.qml @@ -1 +1 @@ -../v3.51/ObjectAcConnection.qml \ No newline at end of file +../v3.52~1/ObjectAcConnection.qml \ No newline at end of file diff --git a/FileSets/v3.13/OverviewAcValuesEnhanced.qml b/FileSets/v3.13/OverviewAcValuesEnhanced.qml index 7ef5c3ad..3752dd11 120000 --- a/FileSets/v3.13/OverviewAcValuesEnhanced.qml +++ b/FileSets/v3.13/OverviewAcValuesEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewAcValuesEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewAcValuesEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.13/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.13/OverviewGeneratorRelayEnhanced.qml index 4f3958c0..ab4d8a4a 120000 --- a/FileSets/v3.13/OverviewGeneratorRelayEnhanced.qml +++ b/FileSets/v3.13/OverviewGeneratorRelayEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.13/TileRelay.qml b/FileSets/v3.13/TileRelay.qml index 9c63a544..e02257ed 120000 --- a/FileSets/v3.13/TileRelay.qml +++ b/FileSets/v3.13/TileRelay.qml @@ -1 +1 @@ -../v3.51/TileRelay.qml \ No newline at end of file +../v3.52~1/TileRelay.qml \ No newline at end of file diff --git a/FileSets/v3.14/ObjectAcConnection.qml b/FileSets/v3.14/ObjectAcConnection.qml index aad59c8b..a955a0e5 120000 --- a/FileSets/v3.14/ObjectAcConnection.qml +++ b/FileSets/v3.14/ObjectAcConnection.qml @@ -1 +1 @@ -../v3.51/ObjectAcConnection.qml \ No newline at end of file +../v3.52~1/ObjectAcConnection.qml \ No newline at end of file diff --git a/FileSets/v3.14/OverviewAcValuesEnhanced.qml b/FileSets/v3.14/OverviewAcValuesEnhanced.qml index 7ef5c3ad..3752dd11 120000 --- a/FileSets/v3.14/OverviewAcValuesEnhanced.qml +++ b/FileSets/v3.14/OverviewAcValuesEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewAcValuesEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewAcValuesEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.14/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.14/OverviewGeneratorRelayEnhanced.qml index 4f3958c0..ab4d8a4a 120000 --- a/FileSets/v3.14/OverviewGeneratorRelayEnhanced.qml +++ b/FileSets/v3.14/OverviewGeneratorRelayEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.14/TileRelay.qml b/FileSets/v3.14/TileRelay.qml index 9c63a544..e02257ed 120000 --- a/FileSets/v3.14/TileRelay.qml +++ b/FileSets/v3.14/TileRelay.qml @@ -1 +1 @@ -../v3.51/TileRelay.qml \ No newline at end of file +../v3.52~1/TileRelay.qml \ No newline at end of file diff --git a/FileSets/v3.20/ObjectAcConnection.qml b/FileSets/v3.20/ObjectAcConnection.qml index aad59c8b..a955a0e5 120000 --- a/FileSets/v3.20/ObjectAcConnection.qml +++ b/FileSets/v3.20/ObjectAcConnection.qml @@ -1 +1 @@ -../v3.51/ObjectAcConnection.qml \ No newline at end of file +../v3.52~1/ObjectAcConnection.qml \ No newline at end of file diff --git a/FileSets/v3.20/OverviewAcValuesEnhanced.qml b/FileSets/v3.20/OverviewAcValuesEnhanced.qml index 7ef5c3ad..3752dd11 120000 --- a/FileSets/v3.20/OverviewAcValuesEnhanced.qml +++ b/FileSets/v3.20/OverviewAcValuesEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewAcValuesEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewAcValuesEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.20/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.20/OverviewGeneratorRelayEnhanced.qml index 4f3958c0..ab4d8a4a 120000 --- a/FileSets/v3.20/OverviewGeneratorRelayEnhanced.qml +++ b/FileSets/v3.20/OverviewGeneratorRelayEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.20/TileRelay.qml b/FileSets/v3.20/TileRelay.qml index 9c63a544..e02257ed 120000 --- a/FileSets/v3.20/TileRelay.qml +++ b/FileSets/v3.20/TileRelay.qml @@ -1 +1 @@ -../v3.51/TileRelay.qml \ No newline at end of file +../v3.52~1/TileRelay.qml \ No newline at end of file diff --git a/FileSets/v3.21/ObjectAcConnection.qml b/FileSets/v3.21/ObjectAcConnection.qml index aad59c8b..a955a0e5 120000 --- a/FileSets/v3.21/ObjectAcConnection.qml +++ b/FileSets/v3.21/ObjectAcConnection.qml @@ -1 +1 @@ -../v3.51/ObjectAcConnection.qml \ No newline at end of file +../v3.52~1/ObjectAcConnection.qml \ No newline at end of file diff --git a/FileSets/v3.21/OverviewAcValuesEnhanced.qml b/FileSets/v3.21/OverviewAcValuesEnhanced.qml index 7ef5c3ad..3752dd11 120000 --- a/FileSets/v3.21/OverviewAcValuesEnhanced.qml +++ b/FileSets/v3.21/OverviewAcValuesEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewAcValuesEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewAcValuesEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.21/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.21/OverviewGeneratorRelayEnhanced.qml index 4f3958c0..ab4d8a4a 120000 --- a/FileSets/v3.21/OverviewGeneratorRelayEnhanced.qml +++ b/FileSets/v3.21/OverviewGeneratorRelayEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.21/TileRelay.qml b/FileSets/v3.21/TileRelay.qml index 9c63a544..e02257ed 120000 --- a/FileSets/v3.21/TileRelay.qml +++ b/FileSets/v3.21/TileRelay.qml @@ -1 +1 @@ -../v3.51/TileRelay.qml \ No newline at end of file +../v3.52~1/TileRelay.qml \ No newline at end of file diff --git a/FileSets/v3.22/ObjectAcConnection.qml b/FileSets/v3.22/ObjectAcConnection.qml index aad59c8b..a955a0e5 120000 --- a/FileSets/v3.22/ObjectAcConnection.qml +++ b/FileSets/v3.22/ObjectAcConnection.qml @@ -1 +1 @@ -../v3.51/ObjectAcConnection.qml \ No newline at end of file +../v3.52~1/ObjectAcConnection.qml \ No newline at end of file diff --git a/FileSets/v3.22/OverviewAcValuesEnhanced.qml b/FileSets/v3.22/OverviewAcValuesEnhanced.qml index 7ef5c3ad..3752dd11 120000 --- a/FileSets/v3.22/OverviewAcValuesEnhanced.qml +++ b/FileSets/v3.22/OverviewAcValuesEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewAcValuesEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewAcValuesEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.22/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.22/OverviewGeneratorRelayEnhanced.qml index 4f3958c0..ab4d8a4a 120000 --- a/FileSets/v3.22/OverviewGeneratorRelayEnhanced.qml +++ b/FileSets/v3.22/OverviewGeneratorRelayEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.22/TileRelay.qml b/FileSets/v3.22/TileRelay.qml index 9c63a544..e02257ed 120000 --- a/FileSets/v3.22/TileRelay.qml +++ b/FileSets/v3.22/TileRelay.qml @@ -1 +1 @@ -../v3.51/TileRelay.qml \ No newline at end of file +../v3.52~1/TileRelay.qml \ No newline at end of file diff --git a/FileSets/v3.30/ObjectAcConnection.qml b/FileSets/v3.30/ObjectAcConnection.qml index aad59c8b..a955a0e5 120000 --- a/FileSets/v3.30/ObjectAcConnection.qml +++ b/FileSets/v3.30/ObjectAcConnection.qml @@ -1 +1 @@ -../v3.51/ObjectAcConnection.qml \ No newline at end of file +../v3.52~1/ObjectAcConnection.qml \ No newline at end of file diff --git a/FileSets/v3.30/OverviewAcValuesEnhanced.qml b/FileSets/v3.30/OverviewAcValuesEnhanced.qml index 7ef5c3ad..3752dd11 120000 --- a/FileSets/v3.30/OverviewAcValuesEnhanced.qml +++ b/FileSets/v3.30/OverviewAcValuesEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewAcValuesEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewAcValuesEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.30/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.30/OverviewGeneratorRelayEnhanced.qml index 4f3958c0..ab4d8a4a 120000 --- a/FileSets/v3.30/OverviewGeneratorRelayEnhanced.qml +++ b/FileSets/v3.30/OverviewGeneratorRelayEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.30/TileRelay.qml b/FileSets/v3.30/TileRelay.qml index 9c63a544..e02257ed 120000 --- a/FileSets/v3.30/TileRelay.qml +++ b/FileSets/v3.30/TileRelay.qml @@ -1 +1 @@ -../v3.51/TileRelay.qml \ No newline at end of file +../v3.52~1/TileRelay.qml \ No newline at end of file diff --git a/FileSets/v3.31/ObjectAcConnection.qml b/FileSets/v3.31/ObjectAcConnection.qml index aad59c8b..a955a0e5 120000 --- a/FileSets/v3.31/ObjectAcConnection.qml +++ b/FileSets/v3.31/ObjectAcConnection.qml @@ -1 +1 @@ -../v3.51/ObjectAcConnection.qml \ No newline at end of file +../v3.52~1/ObjectAcConnection.qml \ No newline at end of file diff --git a/FileSets/v3.31/OverviewAcValuesEnhanced.qml b/FileSets/v3.31/OverviewAcValuesEnhanced.qml index 7ef5c3ad..3752dd11 120000 --- a/FileSets/v3.31/OverviewAcValuesEnhanced.qml +++ b/FileSets/v3.31/OverviewAcValuesEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewAcValuesEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewAcValuesEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.31/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.31/OverviewGeneratorRelayEnhanced.qml index 4f3958c0..ab4d8a4a 120000 --- a/FileSets/v3.31/OverviewGeneratorRelayEnhanced.qml +++ b/FileSets/v3.31/OverviewGeneratorRelayEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.31/TileRelay.qml b/FileSets/v3.31/TileRelay.qml index 9c63a544..e02257ed 120000 --- a/FileSets/v3.31/TileRelay.qml +++ b/FileSets/v3.31/TileRelay.qml @@ -1 +1 @@ -../v3.51/TileRelay.qml \ No newline at end of file +../v3.52~1/TileRelay.qml \ No newline at end of file diff --git a/FileSets/v3.33/ObjectAcConnection.qml b/FileSets/v3.33/ObjectAcConnection.qml index aad59c8b..a955a0e5 120000 --- a/FileSets/v3.33/ObjectAcConnection.qml +++ b/FileSets/v3.33/ObjectAcConnection.qml @@ -1 +1 @@ -../v3.51/ObjectAcConnection.qml \ No newline at end of file +../v3.52~1/ObjectAcConnection.qml \ No newline at end of file diff --git a/FileSets/v3.33/OverviewAcValuesEnhanced.qml b/FileSets/v3.33/OverviewAcValuesEnhanced.qml index 7ef5c3ad..3752dd11 120000 --- a/FileSets/v3.33/OverviewAcValuesEnhanced.qml +++ b/FileSets/v3.33/OverviewAcValuesEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewAcValuesEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewAcValuesEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.33/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.33/OverviewGeneratorRelayEnhanced.qml index 4f3958c0..ab4d8a4a 120000 --- a/FileSets/v3.33/OverviewGeneratorRelayEnhanced.qml +++ b/FileSets/v3.33/OverviewGeneratorRelayEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.33/TileRelay.qml b/FileSets/v3.33/TileRelay.qml index 9c63a544..e02257ed 120000 --- a/FileSets/v3.33/TileRelay.qml +++ b/FileSets/v3.33/TileRelay.qml @@ -1 +1 @@ -../v3.51/TileRelay.qml \ No newline at end of file +../v3.52~1/TileRelay.qml \ No newline at end of file diff --git a/FileSets/v3.34/ObjectAcConnection.qml b/FileSets/v3.34/ObjectAcConnection.qml index aad59c8b..a955a0e5 120000 --- a/FileSets/v3.34/ObjectAcConnection.qml +++ b/FileSets/v3.34/ObjectAcConnection.qml @@ -1 +1 @@ -../v3.51/ObjectAcConnection.qml \ No newline at end of file +../v3.52~1/ObjectAcConnection.qml \ No newline at end of file diff --git a/FileSets/v3.34/OverviewAcValuesEnhanced.qml b/FileSets/v3.34/OverviewAcValuesEnhanced.qml index 7ef5c3ad..3752dd11 120000 --- a/FileSets/v3.34/OverviewAcValuesEnhanced.qml +++ b/FileSets/v3.34/OverviewAcValuesEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewAcValuesEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewAcValuesEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.34/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.34/OverviewGeneratorRelayEnhanced.qml index 4f3958c0..ab4d8a4a 120000 --- a/FileSets/v3.34/OverviewGeneratorRelayEnhanced.qml +++ b/FileSets/v3.34/OverviewGeneratorRelayEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.34/TileRelay.qml b/FileSets/v3.34/TileRelay.qml index 9c63a544..e02257ed 120000 --- a/FileSets/v3.34/TileRelay.qml +++ b/FileSets/v3.34/TileRelay.qml @@ -1 +1 @@ -../v3.51/TileRelay.qml \ No newline at end of file +../v3.52~1/TileRelay.qml \ No newline at end of file diff --git a/FileSets/v3.40/DetailAcInput.qml b/FileSets/v3.40/DetailAcInput.qml index 7ccb024e..453ab0e0 120000 --- a/FileSets/v3.40/DetailAcInput.qml +++ b/FileSets/v3.40/DetailAcInput.qml @@ -1 +1 @@ -../v3.51/DetailAcInput.qml \ No newline at end of file +../v3.52~1/DetailAcInput.qml \ No newline at end of file diff --git a/FileSets/v3.40/DetailInverter.qml b/FileSets/v3.40/DetailInverter.qml index 4b6c46da..d9da3b34 120000 --- a/FileSets/v3.40/DetailInverter.qml +++ b/FileSets/v3.40/DetailInverter.qml @@ -1 +1 @@ -../v3.51/DetailInverter.qml \ No newline at end of file +../v3.52~1/DetailInverter.qml \ No newline at end of file diff --git a/FileSets/v3.40/DetailLoadsCombined.qml b/FileSets/v3.40/DetailLoadsCombined.qml index 5782674a..cb049333 120000 --- a/FileSets/v3.40/DetailLoadsCombined.qml +++ b/FileSets/v3.40/DetailLoadsCombined.qml @@ -1 +1 @@ -../v3.51/DetailLoadsCombined.qml \ No newline at end of file +../v3.52~1/DetailLoadsCombined.qml \ No newline at end of file diff --git a/FileSets/v3.40/DetailLoadsOnInput.qml b/FileSets/v3.40/DetailLoadsOnInput.qml index 29728481..f6f413e0 120000 --- a/FileSets/v3.40/DetailLoadsOnInput.qml +++ b/FileSets/v3.40/DetailLoadsOnInput.qml @@ -1 +1 @@ -../v3.51/DetailLoadsOnInput.qml \ No newline at end of file +../v3.52~1/DetailLoadsOnInput.qml \ No newline at end of file diff --git a/FileSets/v3.40/DetailLoadsOnOutput.qml b/FileSets/v3.40/DetailLoadsOnOutput.qml index 5c5d66be..c368d1e3 120000 --- a/FileSets/v3.40/DetailLoadsOnOutput.qml +++ b/FileSets/v3.40/DetailLoadsOnOutput.qml @@ -1 +1 @@ -../v3.51/DetailLoadsOnOutput.qml \ No newline at end of file +../v3.52~1/DetailLoadsOnOutput.qml \ No newline at end of file diff --git a/FileSets/v3.40/HubData.qml b/FileSets/v3.40/HubData.qml index 05118dd8..218480be 120000 --- a/FileSets/v3.40/HubData.qml +++ b/FileSets/v3.40/HubData.qml @@ -1 +1 @@ -../v3.51/HubData.qml \ No newline at end of file +../v3.52~1/HubData.qml \ No newline at end of file diff --git a/FileSets/v3.40/ObjectAcConnection.qml b/FileSets/v3.40/ObjectAcConnection.qml index aad59c8b..a955a0e5 120000 --- a/FileSets/v3.40/ObjectAcConnection.qml +++ b/FileSets/v3.40/ObjectAcConnection.qml @@ -1 +1 @@ -../v3.51/ObjectAcConnection.qml \ No newline at end of file +../v3.52~1/ObjectAcConnection.qml \ No newline at end of file diff --git a/FileSets/v3.40/OverviewAcValuesEnhanced.qml b/FileSets/v3.40/OverviewAcValuesEnhanced.qml index 7ef5c3ad..3752dd11 120000 --- a/FileSets/v3.40/OverviewAcValuesEnhanced.qml +++ b/FileSets/v3.40/OverviewAcValuesEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewAcValuesEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewAcValuesEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.40/OverviewFlowComplex.qml b/FileSets/v3.40/OverviewFlowComplex.qml index f928901c..786bd349 120000 --- a/FileSets/v3.40/OverviewFlowComplex.qml +++ b/FileSets/v3.40/OverviewFlowComplex.qml @@ -1 +1 @@ -../v3.51/OverviewFlowComplex.qml \ No newline at end of file +../v3.52~1/OverviewFlowComplex.qml \ No newline at end of file diff --git a/FileSets/v3.40/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.40/OverviewGeneratorRelayEnhanced.qml index 4f3958c0..ab4d8a4a 120000 --- a/FileSets/v3.40/OverviewGeneratorRelayEnhanced.qml +++ b/FileSets/v3.40/OverviewGeneratorRelayEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.40/OverviewGridParallel.qml b/FileSets/v3.40/OverviewGridParallel.qml index 29c51682..ba32830a 120000 --- a/FileSets/v3.40/OverviewGridParallel.qml +++ b/FileSets/v3.40/OverviewGridParallel.qml @@ -1 +1 @@ -../v3.51/OverviewGridParallel.qml \ No newline at end of file +../v3.52~1/OverviewGridParallel.qml \ No newline at end of file diff --git a/FileSets/v3.40/OverviewHub.qml b/FileSets/v3.40/OverviewHub.qml index 9033848d..f651566a 120000 --- a/FileSets/v3.40/OverviewHub.qml +++ b/FileSets/v3.40/OverviewHub.qml @@ -1 +1 @@ -../v3.51/OverviewHub.qml \ No newline at end of file +../v3.52~1/OverviewHub.qml \ No newline at end of file diff --git a/FileSets/v3.40/OverviewHubEnhanced.qml b/FileSets/v3.40/OverviewHubEnhanced.qml index 421c62e5..b15b1888 120000 --- a/FileSets/v3.40/OverviewHubEnhanced.qml +++ b/FileSets/v3.40/OverviewHubEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewHubEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewHubEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.40/PowerGauge.qml b/FileSets/v3.40/PowerGauge.qml index d063e557..5ee65909 120000 --- a/FileSets/v3.40/PowerGauge.qml +++ b/FileSets/v3.40/PowerGauge.qml @@ -1 +1 @@ -../v3.51/PowerGauge.qml \ No newline at end of file +../v3.52~1/PowerGauge.qml \ No newline at end of file diff --git a/FileSets/v3.40/TileRelay.qml b/FileSets/v3.40/TileRelay.qml index 9c63a544..e02257ed 120000 --- a/FileSets/v3.40/TileRelay.qml +++ b/FileSets/v3.40/TileRelay.qml @@ -1 +1 @@ -../v3.51/TileRelay.qml \ No newline at end of file +../v3.52~1/TileRelay.qml \ No newline at end of file diff --git a/FileSets/v3.41/DetailAcInput.qml b/FileSets/v3.41/DetailAcInput.qml index 7ccb024e..453ab0e0 120000 --- a/FileSets/v3.41/DetailAcInput.qml +++ b/FileSets/v3.41/DetailAcInput.qml @@ -1 +1 @@ -../v3.51/DetailAcInput.qml \ No newline at end of file +../v3.52~1/DetailAcInput.qml \ No newline at end of file diff --git a/FileSets/v3.41/DetailInverter.qml b/FileSets/v3.41/DetailInverter.qml index 4b6c46da..d9da3b34 120000 --- a/FileSets/v3.41/DetailInverter.qml +++ b/FileSets/v3.41/DetailInverter.qml @@ -1 +1 @@ -../v3.51/DetailInverter.qml \ No newline at end of file +../v3.52~1/DetailInverter.qml \ No newline at end of file diff --git a/FileSets/v3.41/DetailLoadsCombined.qml b/FileSets/v3.41/DetailLoadsCombined.qml index 5782674a..cb049333 120000 --- a/FileSets/v3.41/DetailLoadsCombined.qml +++ b/FileSets/v3.41/DetailLoadsCombined.qml @@ -1 +1 @@ -../v3.51/DetailLoadsCombined.qml \ No newline at end of file +../v3.52~1/DetailLoadsCombined.qml \ No newline at end of file diff --git a/FileSets/v3.41/DetailLoadsOnInput.qml b/FileSets/v3.41/DetailLoadsOnInput.qml index 29728481..f6f413e0 120000 --- a/FileSets/v3.41/DetailLoadsOnInput.qml +++ b/FileSets/v3.41/DetailLoadsOnInput.qml @@ -1 +1 @@ -../v3.51/DetailLoadsOnInput.qml \ No newline at end of file +../v3.52~1/DetailLoadsOnInput.qml \ No newline at end of file diff --git a/FileSets/v3.41/DetailLoadsOnOutput.qml b/FileSets/v3.41/DetailLoadsOnOutput.qml index 5c5d66be..c368d1e3 120000 --- a/FileSets/v3.41/DetailLoadsOnOutput.qml +++ b/FileSets/v3.41/DetailLoadsOnOutput.qml @@ -1 +1 @@ -../v3.51/DetailLoadsOnOutput.qml \ No newline at end of file +../v3.52~1/DetailLoadsOnOutput.qml \ No newline at end of file diff --git a/FileSets/v3.41/HubData.qml b/FileSets/v3.41/HubData.qml index 05118dd8..218480be 120000 --- a/FileSets/v3.41/HubData.qml +++ b/FileSets/v3.41/HubData.qml @@ -1 +1 @@ -../v3.51/HubData.qml \ No newline at end of file +../v3.52~1/HubData.qml \ No newline at end of file diff --git a/FileSets/v3.41/ObjectAcConnection.qml b/FileSets/v3.41/ObjectAcConnection.qml index aad59c8b..a955a0e5 120000 --- a/FileSets/v3.41/ObjectAcConnection.qml +++ b/FileSets/v3.41/ObjectAcConnection.qml @@ -1 +1 @@ -../v3.51/ObjectAcConnection.qml \ No newline at end of file +../v3.52~1/ObjectAcConnection.qml \ No newline at end of file diff --git a/FileSets/v3.41/OverviewAcValuesEnhanced.qml b/FileSets/v3.41/OverviewAcValuesEnhanced.qml index 7ef5c3ad..3752dd11 120000 --- a/FileSets/v3.41/OverviewAcValuesEnhanced.qml +++ b/FileSets/v3.41/OverviewAcValuesEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewAcValuesEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewAcValuesEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.41/OverviewFlowComplex.qml b/FileSets/v3.41/OverviewFlowComplex.qml index f928901c..786bd349 120000 --- a/FileSets/v3.41/OverviewFlowComplex.qml +++ b/FileSets/v3.41/OverviewFlowComplex.qml @@ -1 +1 @@ -../v3.51/OverviewFlowComplex.qml \ No newline at end of file +../v3.52~1/OverviewFlowComplex.qml \ No newline at end of file diff --git a/FileSets/v3.41/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.41/OverviewGeneratorRelayEnhanced.qml index 4f3958c0..ab4d8a4a 120000 --- a/FileSets/v3.41/OverviewGeneratorRelayEnhanced.qml +++ b/FileSets/v3.41/OverviewGeneratorRelayEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.41/OverviewGridParallel.qml b/FileSets/v3.41/OverviewGridParallel.qml index 29c51682..ba32830a 120000 --- a/FileSets/v3.41/OverviewGridParallel.qml +++ b/FileSets/v3.41/OverviewGridParallel.qml @@ -1 +1 @@ -../v3.51/OverviewGridParallel.qml \ No newline at end of file +../v3.52~1/OverviewGridParallel.qml \ No newline at end of file diff --git a/FileSets/v3.41/OverviewHub.qml b/FileSets/v3.41/OverviewHub.qml index 9033848d..f651566a 120000 --- a/FileSets/v3.41/OverviewHub.qml +++ b/FileSets/v3.41/OverviewHub.qml @@ -1 +1 @@ -../v3.51/OverviewHub.qml \ No newline at end of file +../v3.52~1/OverviewHub.qml \ No newline at end of file diff --git a/FileSets/v3.41/OverviewHubEnhanced.qml b/FileSets/v3.41/OverviewHubEnhanced.qml index 421c62e5..b15b1888 120000 --- a/FileSets/v3.41/OverviewHubEnhanced.qml +++ b/FileSets/v3.41/OverviewHubEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewHubEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewHubEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.41/PowerGauge.qml b/FileSets/v3.41/PowerGauge.qml index d063e557..5ee65909 120000 --- a/FileSets/v3.41/PowerGauge.qml +++ b/FileSets/v3.41/PowerGauge.qml @@ -1 +1 @@ -../v3.51/PowerGauge.qml \ No newline at end of file +../v3.52~1/PowerGauge.qml \ No newline at end of file diff --git a/FileSets/v3.41/TileRelay.qml b/FileSets/v3.41/TileRelay.qml index 9c63a544..e02257ed 120000 --- a/FileSets/v3.41/TileRelay.qml +++ b/FileSets/v3.41/TileRelay.qml @@ -1 +1 @@ -../v3.51/TileRelay.qml \ No newline at end of file +../v3.52~1/TileRelay.qml \ No newline at end of file diff --git a/FileSets/v3.50/DetailAcInput.qml b/FileSets/v3.50/DetailAcInput.qml index 7ccb024e..453ab0e0 120000 --- a/FileSets/v3.50/DetailAcInput.qml +++ b/FileSets/v3.50/DetailAcInput.qml @@ -1 +1 @@ -../v3.51/DetailAcInput.qml \ No newline at end of file +../v3.52~1/DetailAcInput.qml \ No newline at end of file diff --git a/FileSets/v3.50/DetailInverter.qml b/FileSets/v3.50/DetailInverter.qml index 4b6c46da..d9da3b34 120000 --- a/FileSets/v3.50/DetailInverter.qml +++ b/FileSets/v3.50/DetailInverter.qml @@ -1 +1 @@ -../v3.51/DetailInverter.qml \ No newline at end of file +../v3.52~1/DetailInverter.qml \ No newline at end of file diff --git a/FileSets/v3.50/DetailLoadsCombined.qml b/FileSets/v3.50/DetailLoadsCombined.qml index 5782674a..cb049333 120000 --- a/FileSets/v3.50/DetailLoadsCombined.qml +++ b/FileSets/v3.50/DetailLoadsCombined.qml @@ -1 +1 @@ -../v3.51/DetailLoadsCombined.qml \ No newline at end of file +../v3.52~1/DetailLoadsCombined.qml \ No newline at end of file diff --git a/FileSets/v3.50/DetailLoadsOnInput.qml b/FileSets/v3.50/DetailLoadsOnInput.qml index 29728481..f6f413e0 120000 --- a/FileSets/v3.50/DetailLoadsOnInput.qml +++ b/FileSets/v3.50/DetailLoadsOnInput.qml @@ -1 +1 @@ -../v3.51/DetailLoadsOnInput.qml \ No newline at end of file +../v3.52~1/DetailLoadsOnInput.qml \ No newline at end of file diff --git a/FileSets/v3.50/DetailLoadsOnOutput.qml b/FileSets/v3.50/DetailLoadsOnOutput.qml index 5c5d66be..c368d1e3 120000 --- a/FileSets/v3.50/DetailLoadsOnOutput.qml +++ b/FileSets/v3.50/DetailLoadsOnOutput.qml @@ -1 +1 @@ -../v3.51/DetailLoadsOnOutput.qml \ No newline at end of file +../v3.52~1/DetailLoadsOnOutput.qml \ No newline at end of file diff --git a/FileSets/v3.50/HubData.qml b/FileSets/v3.50/HubData.qml index 05118dd8..218480be 120000 --- a/FileSets/v3.50/HubData.qml +++ b/FileSets/v3.50/HubData.qml @@ -1 +1 @@ -../v3.51/HubData.qml \ No newline at end of file +../v3.52~1/HubData.qml \ No newline at end of file diff --git a/FileSets/v3.50/ObjectAcConnection.qml b/FileSets/v3.50/ObjectAcConnection.qml index aad59c8b..a955a0e5 120000 --- a/FileSets/v3.50/ObjectAcConnection.qml +++ b/FileSets/v3.50/ObjectAcConnection.qml @@ -1 +1 @@ -../v3.51/ObjectAcConnection.qml \ No newline at end of file +../v3.52~1/ObjectAcConnection.qml \ No newline at end of file diff --git a/FileSets/v3.50/OverviewAcValuesEnhanced.qml b/FileSets/v3.50/OverviewAcValuesEnhanced.qml index 7ef5c3ad..3752dd11 120000 --- a/FileSets/v3.50/OverviewAcValuesEnhanced.qml +++ b/FileSets/v3.50/OverviewAcValuesEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewAcValuesEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewAcValuesEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.50/OverviewFlowComplex.qml b/FileSets/v3.50/OverviewFlowComplex.qml index f928901c..786bd349 120000 --- a/FileSets/v3.50/OverviewFlowComplex.qml +++ b/FileSets/v3.50/OverviewFlowComplex.qml @@ -1 +1 @@ -../v3.51/OverviewFlowComplex.qml \ No newline at end of file +../v3.52~1/OverviewFlowComplex.qml \ No newline at end of file diff --git a/FileSets/v3.50/OverviewGeneratorEnhanced.qml b/FileSets/v3.50/OverviewGeneratorEnhanced.qml index 48162d1b..f4c8f50b 120000 --- a/FileSets/v3.50/OverviewGeneratorEnhanced.qml +++ b/FileSets/v3.50/OverviewGeneratorEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewGeneratorEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewGeneratorEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.50/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.50/OverviewGeneratorRelayEnhanced.qml index 4f3958c0..ab4d8a4a 120000 --- a/FileSets/v3.50/OverviewGeneratorRelayEnhanced.qml +++ b/FileSets/v3.50/OverviewGeneratorRelayEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.50/OverviewGridParallel.qml b/FileSets/v3.50/OverviewGridParallel.qml index 29c51682..ba32830a 120000 --- a/FileSets/v3.50/OverviewGridParallel.qml +++ b/FileSets/v3.50/OverviewGridParallel.qml @@ -1 +1 @@ -../v3.51/OverviewGridParallel.qml \ No newline at end of file +../v3.52~1/OverviewGridParallel.qml \ No newline at end of file diff --git a/FileSets/v3.50/OverviewHub.qml b/FileSets/v3.50/OverviewHub.qml index 9033848d..f651566a 120000 --- a/FileSets/v3.50/OverviewHub.qml +++ b/FileSets/v3.50/OverviewHub.qml @@ -1 +1 @@ -../v3.51/OverviewHub.qml \ No newline at end of file +../v3.52~1/OverviewHub.qml \ No newline at end of file diff --git a/FileSets/v3.50/OverviewHubEnhanced.qml b/FileSets/v3.50/OverviewHubEnhanced.qml index 421c62e5..b15b1888 120000 --- a/FileSets/v3.50/OverviewHubEnhanced.qml +++ b/FileSets/v3.50/OverviewHubEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewHubEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewHubEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.50/OverviewMobileEnhanced.qml b/FileSets/v3.50/OverviewMobileEnhanced.qml index 8c8c4a88..26bd14f5 120000 --- a/FileSets/v3.50/OverviewMobileEnhanced.qml +++ b/FileSets/v3.50/OverviewMobileEnhanced.qml @@ -1 +1 @@ -../v3.51/OverviewMobileEnhanced.qml \ No newline at end of file +../v3.52~1/OverviewMobileEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.50/OverviewTanksTempsDigInputs.qml b/FileSets/v3.50/OverviewTanksTempsDigInputs.qml index 384bbb44..8aa1521f 120000 --- a/FileSets/v3.50/OverviewTanksTempsDigInputs.qml +++ b/FileSets/v3.50/OverviewTanksTempsDigInputs.qml @@ -1 +1 @@ -../v3.51/OverviewTanksTempsDigInputs.qml \ No newline at end of file +../v3.52~1/OverviewTanksTempsDigInputs.qml \ No newline at end of file diff --git a/FileSets/v3.50/PageSettingsGenerator.qml b/FileSets/v3.50/PageSettingsGenerator.qml index 02ea04a1..3c2de96b 120000 --- a/FileSets/v3.50/PageSettingsGenerator.qml +++ b/FileSets/v3.50/PageSettingsGenerator.qml @@ -1 +1 @@ -../v3.51/PageSettingsGenerator.qml \ No newline at end of file +../v3.52~1/PageSettingsGenerator.qml \ No newline at end of file diff --git a/FileSets/v3.50/PageSettingsGuiMods.qml b/FileSets/v3.50/PageSettingsGuiMods.qml index 02b84d3d..63e768f7 120000 --- a/FileSets/v3.50/PageSettingsGuiMods.qml +++ b/FileSets/v3.50/PageSettingsGuiMods.qml @@ -1 +1 @@ -../v3.51/PageSettingsGuiMods.qml \ No newline at end of file +../v3.52~1/PageSettingsGuiMods.qml \ No newline at end of file diff --git a/FileSets/v3.50/PageSettingsRelay.qml b/FileSets/v3.50/PageSettingsRelay.qml index 7545b2f2..1d6cbd1e 120000 --- a/FileSets/v3.50/PageSettingsRelay.qml +++ b/FileSets/v3.50/PageSettingsRelay.qml @@ -1 +1 @@ -../v3.51/PageSettingsRelay.qml \ No newline at end of file +../v3.52~1/PageSettingsRelay.qml \ No newline at end of file diff --git a/FileSets/v3.50/PowerGauge.qml b/FileSets/v3.50/PowerGauge.qml index d063e557..5ee65909 120000 --- a/FileSets/v3.50/PowerGauge.qml +++ b/FileSets/v3.50/PowerGauge.qml @@ -1 +1 @@ -../v3.51/PowerGauge.qml \ No newline at end of file +../v3.52~1/PowerGauge.qml \ No newline at end of file diff --git a/FileSets/v3.50/TileDigIn.qml b/FileSets/v3.50/TileDigIn.qml index 1066674e..cadf4d9c 120000 --- a/FileSets/v3.50/TileDigIn.qml +++ b/FileSets/v3.50/TileDigIn.qml @@ -1 +1 @@ -../v3.51/TileDigIn.qml \ No newline at end of file +../v3.52~1/TileDigIn.qml \ No newline at end of file diff --git a/FileSets/v3.50/TileRelay.qml b/FileSets/v3.50/TileRelay.qml index 9c63a544..e02257ed 120000 --- a/FileSets/v3.50/TileRelay.qml +++ b/FileSets/v3.50/TileRelay.qml @@ -1 +1 @@ -../v3.51/TileRelay.qml \ No newline at end of file +../v3.52~1/TileRelay.qml \ No newline at end of file diff --git a/FileSets/v3.50/dbus_digitalinputs.py b/FileSets/v3.50/dbus_digitalinputs.py index 010bcb6c..8dbac81a 120000 --- a/FileSets/v3.50/dbus_digitalinputs.py +++ b/FileSets/v3.50/dbus_digitalinputs.py @@ -1 +1 @@ -../v3.51/dbus_digitalinputs.py \ No newline at end of file +../v3.52~1/dbus_digitalinputs.py \ No newline at end of file diff --git a/FileSets/v3.50/startstop.py b/FileSets/v3.50/startstop.py index aec9d0d1..263438af 120000 --- a/FileSets/v3.50/startstop.py +++ b/FileSets/v3.50/startstop.py @@ -1 +1 @@ -../v3.51/startstop.py \ No newline at end of file +../v3.52~1/startstop.py \ No newline at end of file diff --git a/FileSets/v3.51/DetailAcInput.qml b/FileSets/v3.51/DetailAcInput.qml deleted file mode 100644 index d2865e4a..00000000 --- a/FileSets/v3.51/DetailAcInput.qml +++ /dev/null @@ -1,606 +0,0 @@ -////// detail page for setting input current limit -////// and displaying AC input details -////// pushed from Flow overview - -import QtQuick 1.1 -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.51/DetailAcInput.qml b/FileSets/v3.51/DetailAcInput.qml new file mode 120000 index 00000000..453ab0e0 --- /dev/null +++ b/FileSets/v3.51/DetailAcInput.qml @@ -0,0 +1 @@ +../v3.52~1/DetailAcInput.qml \ No newline at end of file diff --git a/FileSets/v3.51/DetailInverter.qml b/FileSets/v3.51/DetailInverter.qml deleted file mode 100644 index 42fab4e7..00000000 --- a/FileSets/v3.51/DetailInverter.qml +++ /dev/null @@ -1,650 +0,0 @@ -////// detail page for setting inverter mode -////// and displaying inverter details -////// pushed from Flow overview - -import QtQuick 1.1 -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.51/DetailInverter.qml b/FileSets/v3.51/DetailInverter.qml new file mode 120000 index 00000000..d9da3b34 --- /dev/null +++ b/FileSets/v3.51/DetailInverter.qml @@ -0,0 +1 @@ +../v3.52~1/DetailInverter.qml \ No newline at end of file diff --git a/FileSets/v3.51/DetailLoadsCombined.qml b/FileSets/v3.51/DetailLoadsCombined.qml deleted file mode 100644 index 5909cb10..00000000 --- a/FileSets/v3.51/DetailLoadsCombined.qml +++ /dev/null @@ -1,146 +0,0 @@ -////// detail page for displaying critical AC output details -////// pushed from Flow overview - -import QtQuick 1.1 -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.51/DetailLoadsCombined.qml b/FileSets/v3.51/DetailLoadsCombined.qml new file mode 120000 index 00000000..cb049333 --- /dev/null +++ b/FileSets/v3.51/DetailLoadsCombined.qml @@ -0,0 +1 @@ +../v3.52~1/DetailLoadsCombined.qml \ No newline at end of file diff --git a/FileSets/v3.51/DetailLoadsOnInput.qml b/FileSets/v3.51/DetailLoadsOnInput.qml deleted file mode 100644 index 999a62ac..00000000 --- a/FileSets/v3.51/DetailLoadsOnInput.qml +++ /dev/null @@ -1,146 +0,0 @@ -////// detail page for displaying non-critical AC output details -////// pushed from Flow overview - -import QtQuick 1.1 -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.51/DetailLoadsOnInput.qml b/FileSets/v3.51/DetailLoadsOnInput.qml new file mode 120000 index 00000000..f6f413e0 --- /dev/null +++ b/FileSets/v3.51/DetailLoadsOnInput.qml @@ -0,0 +1 @@ +../v3.52~1/DetailLoadsOnInput.qml \ No newline at end of file diff --git a/FileSets/v3.51/DetailLoadsOnOutput.qml b/FileSets/v3.51/DetailLoadsOnOutput.qml deleted file mode 100644 index 2092989c..00000000 --- a/FileSets/v3.51/DetailLoadsOnOutput.qml +++ /dev/null @@ -1,151 +0,0 @@ -////// detail page for displaying critical AC output details -////// pushed from Flow overview - -import QtQuick 1.1 -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.51/DetailLoadsOnOutput.qml b/FileSets/v3.51/DetailLoadsOnOutput.qml new file mode 120000 index 00000000..c368d1e3 --- /dev/null +++ b/FileSets/v3.51/DetailLoadsOnOutput.qml @@ -0,0 +1 @@ +../v3.52~1/DetailLoadsOnOutput.qml \ No newline at end of file diff --git a/FileSets/v3.51/HubData.qml b/FileSets/v3.51/HubData.qml deleted file mode 100644 index 6c0413dc..00000000 --- a/FileSets/v3.51/HubData.qml +++ /dev/null @@ -1,268 +0,0 @@ -//////// 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 - -Item { - id: root - - property variant sys: theSystem - - property string systemPrefix: "com.victronenergy.system" - property string settingsPrefix: "com.victronenergy.settings" - property string vebusPrefix: _vebusService.valid ? _vebusService.value : "" - -//////// add to support VE.Direct inverters - property string inverterService: "" -//////// add for grid/genset meters - property string gridMeterService: "" - property string gensetService: "" - - property variant battery: _battery - property alias dcSystem: _dcSystem - property alias alternator: _alternator - property alias windGenerator: _windGenerator - property alias fuelCell: _fuelCell - property alias acCharger: _acCharger - 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 acInput: _activein - property alias genset: _genset - 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"} - } - -//////// added alternator - QtObject { - id: _alternator - property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Alternator/Power"); unit: "W"} - } - -//////// added AC charger - QtObject { - id: _acCharger - property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Charger/Power"); unit: "W"} - } - -//////// added wind generator - QtObject { - id: _windGenerator - property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/WindGenerator/Power"); unit: "W"} - } - -//////// added fuel cell - QtObject { - id: _fuelCell - property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/FuelCell/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") -//////// modified for VE.Direct inverter support - inverterSource: "/Ac/ActiveIn" - inverterService: sys.vebusPrefix != "" ? sys.vebusPrefix : root.inverterService - } - - 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") -//////// modified for VE.Direct inverter support - inverterSource: "/Ac/ActiveIn" - inverterService: sys.vebusPrefix != "" ? sys.vebusPrefix : root.inverterService - } - - ObjectAcConnection { - id: _activein - splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0 - bindPrefix: Utils.path(systemPrefix, "/Ac/ActiveIn") -//////// modified for VE.Direct inverter support - inverterSource: "/Ac/ActiveIn" - inverterService: sys.vebusPrefix != "" ? sys.vebusPrefix : root.inverterService - } - - ObjectAcConnection { - id: _acLoad - l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0) - isAcOutput: true - bindPrefix: Utils.path(systemPrefix, "/Ac/Consumption") -//////// modified for VE.Direct inverter support - inverterSource: "/Ac/Out" - inverterService: sys.vebusPrefix != "" ? sys.vebusPrefix : root.inverterService - } - - 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"} - } - -//////// add to support for adjustable watt / killowatt display switching - VBusItem { id: kwThresholdItem; bind: Utils.path(settingsPrefix, "/Settings/GuiMods/KilowattThreshold") } - property int kilowattThreshold: kwThresholdItem.valid ? kwThresholdItem.value : 1000 - -//////// add to support VE.Direct inverters -//////// and grid/genset meters - Component.onCompleted: discoverServices() - - // 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_INVERTER: - if (inverterService === "") - inverterService = service.name; - break;; - case DBusService.DBUS_SERVICE_GRIDMETER: - if (gridMeterService === "") - gridMeterService = service.name; - break;; - case DBusService.DBUS_SERVICE_GENSET: - if (gensetService === "") - gensetService = service.name; - break;; - } - } - - // Check available services inverter services - function discoverServices() - { - inverterService = "" - gridMeterService = "" - gensetService = "" - for (var i = 0; i < DBusServices.count; i++) - addService(DBusServices.at(i)) - } -} diff --git a/FileSets/v3.51/HubData.qml b/FileSets/v3.51/HubData.qml new file mode 120000 index 00000000..218480be --- /dev/null +++ b/FileSets/v3.51/HubData.qml @@ -0,0 +1 @@ +../v3.52~1/HubData.qml \ No newline at end of file diff --git a/FileSets/v3.51/LINKS_ONLY b/FileSets/v3.51/LINKS_ONLY new file mode 100644 index 00000000..e69de29b diff --git a/FileSets/v3.51/ObjectAcConnection.qml b/FileSets/v3.51/ObjectAcConnection.qml deleted file mode 100644 index 9e87e2db..00000000 --- a/FileSets/v3.51/ObjectAcConnection.qml +++ /dev/null @@ -1,52 +0,0 @@ -////// 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 - -QtObject { - property string bindPrefix - property string inverterService: "" - property string inverterSource: "" - - property VBusItem powerL1: VBusItem { bind: Utils.path(bindPrefix, "/L1/Power"); unit: "W"} - property VBusItem powerL2: VBusItem { bind: Utils.path(bindPrefix, "/L2/Power"); unit: "W"} - property VBusItem powerL3: VBusItem { bind: Utils.path(bindPrefix, "/L3/Power"); unit: "W"} - property VBusItem power: VBusItem { unit: "W" } - property VBusItem phaseCount: VBusItem { bind: Utils.path(bindPrefix, "/NumberOfPhases") } - property bool splitPhaseL2PassthruDisabled: false - property bool isAcOutput: false - property bool l2AndL1OutSummed: false -////// added to show bar graphs - property VBusItem inverterState: VBusItem { bind: Utils.path(systemPrefix, "/SystemState/State" ) } - - ////// add to show voltage, current, frequency and bar graphs and use grid/genset meter - property VBusItem voltageL1: VBusItem { bind: Utils.path (bindPrefix, "/L1/Voltage"); unit: "V"} - property VBusItem voltageL2: VBusItem { bind: Utils.path (bindPrefix, "/L2/Voltage"); unit: "V"} - property VBusItem voltageL3: VBusItem { bind: Utils.path (bindPrefix, "/L3/Voltage"); unit: "V"} - - property VBusItem currentL1: VBusItem { bind: Utils.path (bindPrefix, "/L1/Current"); unit: "A"} - property VBusItem currentL2: VBusItem { bind: Utils.path (bindPrefix, "/L2/Current"); unit: "A"} - property VBusItem currentL3: VBusItem { bind: Utils.path (bindPrefix, "/L3/Current"); unit: "A"} - - property VBusItem frequency: VBusItem { bind: Utils.path (bindPrefix, "/Frequency"); unit: "Hz"} - - property VBusItem inCurrentLimit: VBusItem { bind: Utils.path(inverterService, inverterSource, "/CurrentLimit"); unit: "A"} - ////// end add to show voltage, current and frequency - - // As systemcalc doesn't provide the totals anymore we calculate it here. - // Timer is needed because the values are not received in once and then the total - // changes too often on system with more than one phase - property Timer timer: Timer { - interval: 1000 - running: true - repeat: true - onTriggered: { - power.value = powerL1.valid || powerL2.valid || powerL3.valid ? (powerL1.valid ? powerL1.value : 0) + - (powerL2.valid ? powerL2.value : 0) + - (powerL3.valid ? powerL3.value : 0) : undefined - } - } -} diff --git a/FileSets/v3.51/ObjectAcConnection.qml b/FileSets/v3.51/ObjectAcConnection.qml new file mode 120000 index 00000000..a955a0e5 --- /dev/null +++ b/FileSets/v3.51/ObjectAcConnection.qml @@ -0,0 +1 @@ +../v3.52~1/ObjectAcConnection.qml \ No newline at end of file diff --git a/FileSets/v3.51/OverviewAcValuesEnhanced.qml b/FileSets/v3.51/OverviewAcValuesEnhanced.qml deleted file mode 100644 index dc36d2ea..00000000 --- a/FileSets/v3.51/OverviewAcValuesEnhanced.qml +++ /dev/null @@ -1,94 +0,0 @@ -////// 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 1.1 -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.51/OverviewAcValuesEnhanced.qml b/FileSets/v3.51/OverviewAcValuesEnhanced.qml new file mode 120000 index 00000000..3752dd11 --- /dev/null +++ b/FileSets/v3.51/OverviewAcValuesEnhanced.qml @@ -0,0 +1 @@ +../v3.52~1/OverviewAcValuesEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.51/OverviewFlowComplex.qml b/FileSets/v3.51/OverviewFlowComplex.qml deleted file mode 100644 index 9a6d20ab..00000000 --- a/FileSets/v3.51/OverviewFlowComplex.qml +++ /dev/null @@ -1,1549 +0,0 @@ -///// Enhanced DC Coupled / AC Coupled Overview for GuiMods - -import QtQuick 1.1 -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.51/OverviewFlowComplex.qml b/FileSets/v3.51/OverviewFlowComplex.qml new file mode 120000 index 00000000..786bd349 --- /dev/null +++ b/FileSets/v3.51/OverviewFlowComplex.qml @@ -0,0 +1 @@ +../v3.52~1/OverviewFlowComplex.qml \ No newline at end of file diff --git a/FileSets/v3.51/OverviewGeneratorEnhanced.qml b/FileSets/v3.51/OverviewGeneratorEnhanced.qml deleted file mode 100644 index 8c8c679a..00000000 --- a/FileSets/v3.51/OverviewGeneratorEnhanced.qml +++ /dev/null @@ -1,549 +0,0 @@ -// 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 1.1 -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.51/OverviewGeneratorEnhanced.qml b/FileSets/v3.51/OverviewGeneratorEnhanced.qml new file mode 120000 index 00000000..f4c8f50b --- /dev/null +++ b/FileSets/v3.51/OverviewGeneratorEnhanced.qml @@ -0,0 +1 @@ +../v3.52~1/OverviewGeneratorEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.51/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.51/OverviewGeneratorRelayEnhanced.qml deleted file mode 100644 index 83326226..00000000 --- a/FileSets/v3.51/OverviewGeneratorRelayEnhanced.qml +++ /dev/null @@ -1,8 +0,0 @@ -import QtQuick 1.1 -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.51/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.51/OverviewGeneratorRelayEnhanced.qml new file mode 120000 index 00000000..ab4d8a4a --- /dev/null +++ b/FileSets/v3.51/OverviewGeneratorRelayEnhanced.qml @@ -0,0 +1 @@ +../v3.52~1/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.51/OverviewGridParallel.qml b/FileSets/v3.51/OverviewGridParallel.qml deleted file mode 100644 index 8e11752d..00000000 --- a/FileSets/v3.51/OverviewGridParallel.qml +++ /dev/null @@ -1,490 +0,0 @@ -import QtQuick 1.1 -import "utils.js" as Utils - -OverviewPage { - 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 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) -////// GuiMods — DarkMode - titleColor: !darkMode ? "#E74c3c" : "#73261E" - color: !darkMode ? "#C0392B" : "#601C15" - 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") -////// GuiMods — DarkMode - color: !darkMode ? "#27AE60" : "#135730" - titleColor: !darkMode ? "#2ECC71" : "#176638" - 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") -////// GuiMods — DarkMode - color: !darkMode ? "#157894" : "#0a3c4a" - titleColor: !darkMode ? "#419FB9" : "#204f5c" - 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 - 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.51/OverviewGridParallel.qml b/FileSets/v3.51/OverviewGridParallel.qml new file mode 120000 index 00000000..ba32830a --- /dev/null +++ b/FileSets/v3.51/OverviewGridParallel.qml @@ -0,0 +1 @@ +../v3.52~1/OverviewGridParallel.qml \ No newline at end of file diff --git a/FileSets/v3.51/OverviewHub.qml b/FileSets/v3.51/OverviewHub.qml deleted file mode 100644 index 7c04b763..00000000 --- a/FileSets/v3.51/OverviewHub.qml +++ /dev/null @@ -1,315 +0,0 @@ -import QtQuick 1.1 -import "utils.js" as Utils - -OverviewPage { - 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 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) -////// GuiMods — DarkMode - titleColor: !darkMode ? "#E74c3c" : "#73261E" - color: !darkMode ? "#C0392B" : "#601C15" - - 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") -////// GuiMods — DarkMode - color: !darkMode ? "#27AE60" : "#135730" - titleColor: !darkMode ? "#2ECC71" : "#176638" - 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 - - 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.51/OverviewHub.qml b/FileSets/v3.51/OverviewHub.qml new file mode 120000 index 00000000..f651566a --- /dev/null +++ b/FileSets/v3.51/OverviewHub.qml @@ -0,0 +1 @@ +../v3.52~1/OverviewHub.qml \ No newline at end of file diff --git a/FileSets/v3.51/OverviewHubEnhanced.qml b/FileSets/v3.51/OverviewHubEnhanced.qml deleted file mode 100644 index 9e2b2c65..00000000 --- a/FileSets/v3.51/OverviewHubEnhanced.qml +++ /dev/null @@ -1,1504 +0,0 @@ -////// 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 1.1 -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.51/OverviewHubEnhanced.qml b/FileSets/v3.51/OverviewHubEnhanced.qml new file mode 120000 index 00000000..b15b1888 --- /dev/null +++ b/FileSets/v3.51/OverviewHubEnhanced.qml @@ -0,0 +1 @@ +../v3.52~1/OverviewHubEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.51/OverviewMobileEnhanced.qml b/FileSets/v3.51/OverviewMobileEnhanced.qml deleted file mode 100644 index 664344ff..00000000 --- a/FileSets/v3.51/OverviewMobileEnhanced.qml +++ /dev/null @@ -1,989 +0,0 @@ -// 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 1.1 -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.51/OverviewMobileEnhanced.qml b/FileSets/v3.51/OverviewMobileEnhanced.qml new file mode 120000 index 00000000..26bd14f5 --- /dev/null +++ b/FileSets/v3.51/OverviewMobileEnhanced.qml @@ -0,0 +1 @@ +../v3.52~1/OverviewMobileEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.51/OverviewTanksTempsDigInputs.qml b/FileSets/v3.51/OverviewTanksTempsDigInputs.qml deleted file mode 100644 index 781e163c..00000000 --- a/FileSets/v3.51/OverviewTanksTempsDigInputs.qml +++ /dev/null @@ -1,207 +0,0 @@ -//// New overview page for tanks, temps and digital inputs -//// part of GuiMods -//// based on tank/temps column in mobile overview - -import QtQuick 1.1 -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.51/OverviewTanksTempsDigInputs.qml b/FileSets/v3.51/OverviewTanksTempsDigInputs.qml new file mode 120000 index 00000000..8aa1521f --- /dev/null +++ b/FileSets/v3.51/OverviewTanksTempsDigInputs.qml @@ -0,0 +1 @@ +../v3.52~1/OverviewTanksTempsDigInputs.qml \ No newline at end of file diff --git a/FileSets/v3.51/PageSettingsGenerator.qml b/FileSets/v3.51/PageSettingsGenerator.qml deleted file mode 100644 index 9b2625a6..00000000 --- a/FileSets/v3.51/PageSettingsGenerator.qml +++ /dev/null @@ -1,115 +0,0 @@ -//// GuiMods -//// added link to external state enable - -import QtQuick 1.1 -import com.victron.velib 1.0 -import "utils.js" as Utils - -MbPage { - id: root - title: qsTr("Generator start/stop settings") - property string settingsBindPrefix - property string startStopBindPrefix - property VBusItem acIn1Source: VBusItem { bind: "com.victronenergy.settings/Settings/SystemSetup/AcInput1" } - property VBusItem acIn2Source: VBusItem { bind: "com.victronenergy.settings/Settings/SystemSetup/AcInput2" } - property VBusItem capabilities: VBusItem { bind: Utils.path(startStopBindPrefix, "/Capabilities") } - property int warmupCapability: 1 - - model: VisibleItemModel { - - MbSubMenu { - id: conditions - description: qsTr("Conditions") - subpage: - Component { - PageGeneratorConditions { - title: qsTr("Conditions") - bindPrefix: root.settingsBindPrefix - startStopBindPrefix: root.startStopBindPrefix - } - } - } - - MbSpinBox { - description: qsTr("Minimum run time") - item { - bind: Utils.path(settingsBindPrefix, "/MinimumRuntime") - unit: "m" - decimals: 0 - step: 1 - } - } - - MbSubMenu { - show: capabilities.value & warmupCapability - description: qsTr("Warm-up & cool-down") - subpage: - Component { - PageSettingsGeneratorWarmup { - title: qsTr("Warm-up & cool-down") - } - } - } - - MbSwitch { - property bool generatorIsSet: acIn1Source.value === 2 || acIn2Source.value === 2 - name: qsTr("Detect generator at AC input") - bind: Utils.path(settingsBindPrefix, "/Alarms/NoGeneratorAtAcIn") - enabled: valid && (generatorIsSet || checked) - onClicked: { - if (!checked) { - if (!generatorIsSet) { - toast.createToast(qsTr("None of the AC inputs is set to generator. Go to the system setup page and set the correct " + - "AC input to generator in order to enable this functionality."), 10000, "icon-info-active") - } else { - toast.createToast(qsTr("An alarm will be triggered when no power from the generator is detected at the inverter AC input. " + - "Make sure that the correct AC input is set to generator on the system setup page."), 12000, "icon-info-active") - } - } - } - } - -//// GuiMods - MbSwitch { - name: qsTr("Link to external running state") - bind: Utils.path(settingsBindPrefix, "/LinkToExternalStatus") - onClicked: - { - if (!checked) - toast.createToast(qsTr("Manual run will be synchronized with the generaror 'is running digital input' or AC input"), 10000, "icon-info-active") - } - } - - MbSwitch { - name: qsTr("Alarm when generator is not in auto start mode") - bind: Utils.path(settingsBindPrefix, "/Alarms/AutoStartDisabled") - onClicked: { - if (!checked) { - toast.createToast(qsTr("An alarm will be triggered when auto start function is left disabled for more than 10 minutes."), 12000, "icon-info-active") - } - } - } - - MbSwitch { - id: timeZones - name: qsTr("Quiet hours") - bind: Utils.path(settingsBindPrefix, "/QuietHours/Enabled") - enabled: valid - writeAccessLevel: User.AccessUser - } - - MbEditBoxTime { - description: qsTr("Quiet hours start time") - item.bind: Utils.path(settingsBindPrefix, "/QuietHours/StartTime") - show: timeZones.checked - writeAccessLevel: User.AccessUser - } - - MbEditBoxTime { - description: qsTr("Quiet hours end time") - item.bind: Utils.path(settingsBindPrefix, "/QuietHours/EndTime") - show: timeZones.checked - writeAccessLevel: User.AccessUser - } - } -} diff --git a/FileSets/v3.51/PageSettingsGenerator.qml b/FileSets/v3.51/PageSettingsGenerator.qml new file mode 120000 index 00000000..3c2de96b --- /dev/null +++ b/FileSets/v3.51/PageSettingsGenerator.qml @@ -0,0 +1 @@ +../v3.52~1/PageSettingsGenerator.qml \ No newline at end of file diff --git a/FileSets/v3.51/PageSettingsGuiMods.qml b/FileSets/v3.51/PageSettingsGuiMods.qml deleted file mode 100644 index 420a42ac..00000000 --- a/FileSets/v3.51/PageSettingsGuiMods.qml +++ /dev/null @@ -1,290 +0,0 @@ -/////// new menu for all Gui Mods - -import QtQuick 1.1 -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.51/PageSettingsGuiMods.qml b/FileSets/v3.51/PageSettingsGuiMods.qml new file mode 120000 index 00000000..63e768f7 --- /dev/null +++ b/FileSets/v3.51/PageSettingsGuiMods.qml @@ -0,0 +1 @@ +../v3.52~1/PageSettingsGuiMods.qml \ No newline at end of file diff --git a/FileSets/v3.51/PageSettingsRelay.qml b/FileSets/v3.51/PageSettingsRelay.qml deleted file mode 100644 index 44c08871..00000000 --- a/FileSets/v3.51/PageSettingsRelay.qml +++ /dev/null @@ -1,516 +0,0 @@ -//////// 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 - -MbPage { - id: pageRelaySettings - title: qsTr("Relay") - property string bindPrefix: "com.victronenergy.settings" - property VBusItem relay1Item: VBusItem { bind: "com.victronenergy.system/Relay/0/State" } - property bool hasRelay1: relay1Item.valid - property VBusItem relay2Item: VBusItem { bind: "com.victronenergy.system/Relay/1/State" } - property bool hasRelay2: relay2Item.valid - property VBusItem relay3Item: VBusItem { bind: "com.victronenergy.system/Relay/2/State" } - property bool hasRelay3: relay3Item.valid - property VBusItem relay4Item: VBusItem { bind: "com.victronenergy.system/Relay/3/State" } - property bool hasRelay4: relay4Item.valid - property VBusItem relay5Item: VBusItem { bind: "com.victronenergy.system/Relay/4/State" } - property bool hasRelay5: relay5Item.valid - property VBusItem relay6Item: VBusItem { bind: "com.victronenergy.system/Relay/5/State" } - property bool hasRelay6: relay6Item.valid - property VBusItem relay7Item: VBusItem { bind: "com.victronenergy.system/Relay/6/State" } - property bool hasRelay7: relay7Item.valid - property VBusItem relay8Item: VBusItem { bind: "com.victronenergy.system/Relay/7/State" } - property bool hasRelay8: relay8Item.valid - property VBusItem relay9Item: VBusItem { bind: "com.victronenergy.system/Relay/8/State" } - property bool hasRelay9: relay9Item.valid - property VBusItem relay10Item: VBusItem { bind: "com.victronenergy.system/Relay/9/State" } - property bool hasRelay10: relay10Item.valid - property VBusItem relay11Item: VBusItem { bind: "com.victronenergy.system/Relay/10/State" } - property bool hasRelay11: relay11Item.valid - property VBusItem relay12Item: VBusItem { bind: "com.victronenergy.system/Relay/11/State" } - property bool hasRelay12: relay12Item.valid - property VBusItem relay13Item: VBusItem { bind: "com.victronenergy.system/Relay/12/State" } - property bool hasRelay13: relay13Item.valid - property VBusItem relay14Item: VBusItem { bind: "com.victronenergy.system/Relay/13/State" } - property bool hasRelay14: relay14Item.valid - property VBusItem relay15Item: VBusItem { bind: "com.victronenergy.system/Relay/14/State" } - property bool hasRelay15: relay15Item.valid - property VBusItem relay16Item: VBusItem { bind: "com.victronenergy.system/Relay/15/State" } - property bool hasRelay16: relay16Item.valid - property VBusItem relay17Item: VBusItem { bind: "com.victronenergy.system/Relay/16/State" } - property bool hasRelay17: relay17Item.valid - property VBusItem relay18Item: VBusItem { bind: "com.victronenergy.system/Relay/17/State" } - property bool hasRelay18: relay18Item.valid - - property VBusItem relay1NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/0/CustomName") } - property VBusItem relay2NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/1/CustomName") } - property VBusItem relay3NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/2/CustomName") } - property VBusItem relay4NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/3/CustomName") } - property VBusItem relay5NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/4/CustomName") } - property VBusItem relay6NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/5/CustomName") } - property VBusItem relay7NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/6/CustomName") } - property VBusItem relay8NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/7/CustomName") } - property VBusItem relay9NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/8/CustomName") } - property VBusItem relay10NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/9/CustomName") } - property VBusItem relay11NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/10/CustomName") } - property VBusItem relay12NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/11/CustomName") } - property VBusItem relay13NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/12/CustomName") } - property VBusItem relay14NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/13/CustomName") } - property VBusItem relay15NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/14/CustomName") } - property VBusItem relay16NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/15/CustomName") } - property VBusItem relay17NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/16/CustomName") } - property VBusItem relay18NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/17/CustomName") } - - function relayName (nameItem, relayNumber) - { - var prefix, suffix - if (nameItem.valid && nameItem.value != "") - { - prefix = nameItem.value + " (" - suffix = ")" - } - else - { - prefix = "" - suffix = "" - } - if (relayNumber == 1) - return prefix + (hasRelay2 ? qsTr("Relay 1") : qsTr("Relay")) + suffix + " " + qsTr("On") - else - return prefix + qsTr("Relay") + " " + relayNumber + suffix + " " + qsTr("On") - } - - model: VisibleItemModel { - MbItemOptions { - id: relay1Function - description: hasRelay2 ? qsTr("Function (Relay 1)") : qsTr("Function") - bind: Utils.path(bindPrefix, "/Settings/Relay/Function") - possibleValues:[ - MbOption { description: qsTr("Alarm relay"); value: 0 }, - MbOption { description: qsTr("Genset start/stop"); value: 1 }, - MbOption { description: qsTr("Connected genset helper relay"); value: 5 }, - MbOption { description: qsTr("Tank pump"); value: 3 }, - MbOption { description: qsTr("Manual"); value: 2 }, - MbOption { description: qsTr("Temperature"); value: 4 } - ] - show: hasRelay1 - } - - MbItemOptions { - description: qsTr("Alarm relay polarity") - bind: Utils.path(bindPrefix, "/Settings/Relay/Polarity") - show: hasRelay1 && relay1Function.value === 0 - possibleValues: [ - MbOption { description: qsTr("Normally open"); value: 0 }, - MbOption { description: qsTr("Normally closed"); value: 1 } - ] - } - - MbSwitch { - id: manualSwitch1 - name: relayName (relay1NameItem, 1) - bind: "com.victronenergy.system/Relay/0/State" - show: hasRelay1 && relay1Function.value === 2 // manual mode - } - - MbItemOptions { - id: relay2Function - description: hasRelay2 ? qsTr("Function (Relay 2)") : qsTr("Function") - bind: Utils.path(bindPrefix, "/Settings/Relay/1/Function") - show: hasRelay2 - possibleValues:[ - MbOption { description: qsTr("Manual"); value: 2 }, - MbOption { description: qsTr("Temperature"); value: 4 } - ] - } - MbSwitch { - id: manualSwitch2 - name: relayName (relay2NameItem, 2) - bind: "com.victronenergy.system/Relay/1/State" - show: hasRelay2 && relay2Function.value === 2 - } - MbSwitch { - id: manualSwitch3 - name: relayName (relay3NameItem, 3) - bind: "com.victronenergy.system/Relay/2/State" - show: hasRelay3 - } - MbSwitch { - id: manualSwitch4 - name: relayName (relay4NameItem, 4) - bind: "com.victronenergy.system/Relay/3/State" - show: hasRelay4 - } - MbSwitch { - id: manualSwitch5 - name: relayName (relay5NameItem, 5) - bind: "com.victronenergy.system/Relay/4/State" - show: hasRelay5 - } - MbSwitch { - id: manualSwitch6 - name: relayName (relay6NameItem, 6) - bind: "com.victronenergy.system/Relay/5/State" - show: hasRelay6 - } - MbSwitch { - id: manualSwitch7 - name: relayName (relay7NameItem, 7) - bind: "com.victronenergy.system/Relay/6/State" - show: hasRelay7 - } - MbSwitch { - id: manualSwitch8 - name: relayName (relay8NameItem, 8) - bind: "com.victronenergy.system/Relay/7/State" - show: hasRelay8 - } - MbSwitch { - id: manualSwitch9 - name: relayName (relay9NameItem, 9) - bind: "com.victronenergy.system/Relay/8/State" - show: hasRelay9 - } - MbSwitch { - id: manualSwitch10 - name: relayName (relay10NameItem, 10) - bind: "com.victronenergy.system/Relay/9/State" - show: hasRelay10 - } - MbSwitch { - id: manualSwitch11 - name: relayName (relay11NameItem, 11) - bind: "com.victronenergy.system/Relay/10/State" - show: hasRelay11 - } - MbSwitch { - id: manualSwitch12 - name: relayName (relay12NameItem, 12) - bind: "com.victronenergy.system/Relay/11/State" - show: hasRelay12 - } - MbSwitch { - id: manualSwitch13 - name: relayName (relay13NameItem, 13) - bind: "com.victronenergy.system/Relay/12/State" - show: hasRelay13 - } - MbSwitch { - id: manualSwitch14 - name: relayName (relay14NameItem, 14) - bind: "com.victronenergy.system/Relay/13/State" - show: hasRelay14 - } - MbSwitch { - id: manualSwitch15 - name: relayName (relay15NameItem, 15) - bind: "com.victronenergy.system/Relay/14/State" - show: hasRelay15 - } - MbSwitch { - id: manualSwitch16 - name: relayName (relay16NameItem, 16) - bind: "com.victronenergy.system/Relay/15/State" - show: hasRelay16 - } - MbSwitch { - id: manualSwitch17 - name: relayName (relay17NameItem, 17) - bind: "com.victronenergy.system/Relay/16/State" - show: hasRelay17 - } - MbSwitch { - id: manualSwitch18 - name: relayName (relay18NameItem, 18) - bind: "com.victronenergy.system/Relay/17/State" - show: hasRelay18 - } - - MbSubMenu { - id: conditions - description: qsTr("Temperature control rules") - show: relay1Function.value === 4 || relay2Function.value === 4 - subpage: Component { - PageSettingsRelayTempSensors { - id: relayPage - title: qsTr("Temperature control rules") - } - } - } - - MbEditBox { - id: relay1name - description: qsTr("Relay 1 Name") - item.bind: "com.victronenergy.settings/Settings/Relay/0/CustomName" - show: hasRelay1 && item.valid && relay1Function.value === 2 // manual mode - maximumLength: 32 - enableSpaceBar: true - } - MbSwitch { - id: showRelay1 - name: qsTr("Show Relay 1 in overview") - bind: "com.victronenergy.settings/Settings/Relay/0/Show" - show: hasRelay1 - } - - MbEditBox { - id: relay2name - description: qsTr("Relay 2 Name") - item.bind: "com.victronenergy.settings/Settings/Relay/1/CustomName" - show: hasRelay2 && item.valid - maximumLength: 32 - enableSpaceBar: true - } - MbSwitch { - id: showRelay2 - name: qsTr("Show Relay 2 in overview") - bind: "com.victronenergy.settings/Settings/Relay/1/Show" - show: hasRelay2 - } - - MbEditBox { - id: relay3name - description: qsTr("Relay 3 Name") - item.bind: "com.victronenergy.settings/Settings/Relay/2/CustomName" - show: hasRelay3 && item.valid - maximumLength: 32 - enableSpaceBar: true - } - MbSwitch { - id: showRelay3 - name: qsTr("Show Relay 3 in overview") - bind: "com.victronenergy.settings/Settings/Relay/2/Show" - show: hasRelay3 - } - - MbEditBox { - id: relay4name - description: qsTr("Relay 4 Name") - item.bind: "com.victronenergy.settings/Settings/Relay/3/CustomName" - show: hasRelay4 && item.valid - maximumLength: 32 - enableSpaceBar: true - } - MbSwitch { - id: showRelay4 - name: qsTr("Show Relay 4 in overview") - bind: "com.victronenergy.settings/Settings/Relay/3/Show" - show: hasRelay4 - } - - MbEditBox { - id: relay5name - description: qsTr("Relay 5 Name") - item.bind: "com.victronenergy.settings/Settings/Relay/4/CustomName" - show: hasRelay5 && item.valid - maximumLength: 32 - enableSpaceBar: true - } - MbSwitch { - id: showRelay5 - name: qsTr("Show Relay 5 in overview") - bind: "com.victronenergy.settings/Settings/Relay/4/Show" - show: hasRelay5 - } - - MbEditBox { - id: relay6name - description: qsTr("Relay 6 Name") - item.bind: "com.victronenergy.settings/Settings/Relay/5/CustomName" - show: hasRelay6 && item.valid - maximumLength: 32 - enableSpaceBar: true - } - MbSwitch { - id: showRelay6 - name: qsTr("Show Relay 6 in overview") - bind: "com.victronenergy.settings/Settings/Relay/5/Show" - show: hasRelay6 - } - - MbEditBox { - id: relay7name - description: qsTr("Relay 7 Name") - item.bind: "com.victronenergy.settings/Settings/Relay/6/CustomName" - show: hasRelay7 && item.valid - maximumLength: 32 - enableSpaceBar: true - } - MbSwitch { - id: showRelay7 - name: qsTr("Show Relay 7 in overview") - bind: "com.victronenergy.settings/Settings/Relay/6/Show" - show: hasRelay7 - } - - MbEditBox { - id: relay8name - description: qsTr("Relay 8 Name") - item.bind: "com.victronenergy.settings/Settings/Relay/7/CustomName" - show: hasRelay8 && item.valid - maximumLength: 32 - enableSpaceBar: true - } - MbSwitch { - id: showRelay8 - name: qsTr("Show Relay 8 in overview") - bind: "com.victronenergy.settings/Settings/Relay/7/Show" - show: hasRelay8 - } - - MbEditBox { - id: relay9name - description: qsTr("Relay 9 Name") - item.bind: "com.victronenergy.settings/Settings/Relay/8/CustomName" - show: hasRelay9 && item.valid - maximumLength: 32 - enableSpaceBar: true - } - MbSwitch { - id: showRelay9 - name: qsTr("Show Relay 9 in overview") - bind: "com.victronenergy.settings/Settings/Relay/8/Show" - show: hasRelay9 - } - - MbEditBox { - id: relay10name - description: qsTr("Relay 10 Name") - item.bind: "com.victronenergy.settings/Settings/Relay/9/CustomName" - show: hasRelay10 && item.valid - maximumLength: 32 - enableSpaceBar: true - } - MbSwitch { - id: showRelay10 - name: qsTr("Show Relay 10 in overview") - bind: "com.victronenergy.settings/Settings/Relay/9/Show" - show: hasRelay10 - } - - MbEditBox { - id: relay11name - description: qsTr("Relay 11 Name") - item.bind: "com.victronenergy.settings/Settings/Relay/10/CustomName" - show: hasRelay11 && item.valid - maximumLength: 32 - enableSpaceBar: true - } - MbSwitch { - id: showRelay11 - name: qsTr("Show Relay 11 in overview") - bind: "com.victronenergy.settings/Settings/Relay/10/Show" - show: hasRelay11 - } - - MbEditBox { - id: relay12name - description: qsTr("Relay 12 Name") - item.bind: "com.victronenergy.settings/Settings/Relay/11/CustomName" - show: hasRelay12 && item.valid - maximumLength: 32 - enableSpaceBar: true - } - MbSwitch { - id: showRelay12 - name: qsTr("Show Relay 12 in overview") - bind: "com.victronenergy.settings/Settings/Relay/11/Show" - show: hasRelay12 - } - - MbEditBox { - id: relay13name - description: qsTr("Relay 13 Name") - item.bind: "com.victronenergy.settings/Settings/Relay/12/CustomName" - show: hasRelay13 && item.valid - maximumLength: 32 - enableSpaceBar: true - } - MbSwitch { - id: showRelay13 - name: qsTr("Show Relay 13 in overview") - bind: "com.victronenergy.settings/Settings/Relay/12/Show" - show: hasRelay13 - } - - MbEditBox { - id: relay14name - description: qsTr("Relay 14 Name") - item.bind: "com.victronenergy.settings/Settings/Relay/13/CustomName" - show: hasRelay14 && item.valid - maximumLength: 32 - enableSpaceBar: true - } - MbSwitch { - id: showRelay14 - name: qsTr("Show Relay 14 in overview") - bind: "com.victronenergy.settings/Settings/Relay/13/Show" - show: hasRelay14 - } - - MbEditBox { - id: relay15name - description: qsTr("Relay 15 Name") - item.bind: "com.victronenergy.settings/Settings/Relay/14/CustomName" - show: hasRelay15 && item.valid - maximumLength: 32 - enableSpaceBar: true - } - MbSwitch { - id: showRelay15 - name: qsTr("Show Relay 15 in overview") - bind: "com.victronenergy.settings/Settings/Relay/14/Show" - show: hasRelay15 - } - - MbEditBox { - id: relay16name - description: qsTr("Relay 16 Name") - item.bind: "com.victronenergy.settings/Settings/Relay/15/CustomName" - show: hasRelay16 && item.valid - maximumLength: 32 - enableSpaceBar: true - } - MbSwitch { - id: showRelay16 - name: qsTr("Show Relay 16 in overview") - bind: "com.victronenergy.settings/Settings/Relay/15/Show" - show: hasRelay16 - } - - MbEditBox { - id: relay17name - description: qsTr("Relay 17 Name") - item.bind: "com.victronenergy.settings/Settings/Relay/16/CustomName" - show: hasRelay17 && item.valid - maximumLength: 32 - enableSpaceBar: true - } - MbSwitch { - id: showRelay17 - name: qsTr("Show Relay 17 in overview") - bind: "com.victronenergy.settings/Settings/Relay/16/Show" - show: hasRelay17 - } - MbEditBox { - id: relay18name - description: qsTr("Relay 18 Name") - item.bind: "com.victronenergy.settings/Settings/Relay/17/CustomName" - show: hasRelay18 && item.valid - maximumLength: 32 - enableSpaceBar: true - } - MbSwitch { - id: showRelay18 - name: qsTr("Show Relay 18 in overview") - bind: "com.victronenergy.settings/Settings/Relay/17/Show" - show: hasRelay18 - } - } -} diff --git a/FileSets/v3.51/PageSettingsRelay.qml b/FileSets/v3.51/PageSettingsRelay.qml new file mode 120000 index 00000000..1d6cbd1e --- /dev/null +++ b/FileSets/v3.51/PageSettingsRelay.qml @@ -0,0 +1 @@ +../v3.52~1/PageSettingsRelay.qml \ No newline at end of file diff --git a/FileSets/v3.51/PowerGauge.qml b/FileSets/v3.51/PowerGauge.qml deleted file mode 100644 index 80c182c3..00000000 --- a/FileSets/v3.51/PowerGauge.qml +++ /dev/null @@ -1,320 +0,0 @@ -// displays value as a bar surrounded by three range regions -// use for I/O, PV inverter & charger - -import QtQuick 1.1 -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.51/PowerGauge.qml b/FileSets/v3.51/PowerGauge.qml new file mode 120000 index 00000000..5ee65909 --- /dev/null +++ b/FileSets/v3.51/PowerGauge.qml @@ -0,0 +1 @@ +../v3.52~1/PowerGauge.qml \ No newline at end of file diff --git a/FileSets/v3.51/TileDigIn.qml b/FileSets/v3.51/TileDigIn.qml deleted file mode 100644 index 492b8b2d..00000000 --- a/FileSets/v3.51/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.51/TileDigIn.qml b/FileSets/v3.51/TileDigIn.qml new file mode 120000 index 00000000..cadf4d9c --- /dev/null +++ b/FileSets/v3.51/TileDigIn.qml @@ -0,0 +1 @@ +../v3.52~1/TileDigIn.qml \ No newline at end of file diff --git a/FileSets/v3.51/TileRelay.qml b/FileSets/v3.51/TileRelay.qml deleted file mode 100644 index ab9dd82d..00000000 --- a/FileSets/v3.51/TileRelay.qml +++ /dev/null @@ -1,498 +0,0 @@ -// New for GuiMods to display and control relays on separate overview page - -import QtQuick 1.1 -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.51/TileRelay.qml b/FileSets/v3.51/TileRelay.qml new file mode 120000 index 00000000..e02257ed --- /dev/null +++ b/FileSets/v3.51/TileRelay.qml @@ -0,0 +1 @@ +../v3.52~1/TileRelay.qml \ No newline at end of file diff --git a/FileSets/v3.51/dbus_digitalinputs.py b/FileSets/v3.51/dbus_digitalinputs.py deleted file mode 100644 index b8864152..00000000 --- a/FileSets/v3.51/dbus_digitalinputs.py +++ /dev/null @@ -1,744 +0,0 @@ -#!/usr/bin/python3 -u - -#### modified for ExtTransferSwitch package - -import sys, os -import signal -from threading import Thread -from select import select, epoll, EPOLLPRI -from functools import partial -from collections import namedtuple -from argparse import ArgumentParser -import traceback -sys.path.insert(1, os.path.join(os.path.dirname(__file__), 'ext', 'velib_python')) - -from dbus.mainloop.glib import DBusGMainLoop -import dbus -from gi.repository import GLib -from vedbus import VeDbusService, VeDbusItemImport -from settingsdevice import SettingsDevice - -VERSION = '0.27' -MAXCOUNT = 2**31-1 -SAVEINTERVAL = 60000 - -INPUT_FUNCTION_COUNTER = 1 -INPUT_FUNCTION_INPUT = 2 - -Translation = namedtuple('Translation', ['no', 'yes']) - -# Only append at the end -INPUTTYPES = [ - 'Disabled', - 'Pulse meter', - 'Door', - 'Bilge pump', - 'Bilge alarm', - 'Burglar alarm', - 'Smoke alarm', - 'Fire alarm', - 'CO2 alarm', - 'Generator', - 'Generic I/O', - 'Touch enable', -#### added for ExtTransferSwitch package -- must be LAST in the list - 'Transfer switch' -] - -# Translations. The text will be used only for GetText, it will be translated -# in the gui. -TRANSLATIONS = [ - Translation('low', 'high'), - Translation('off', 'on'), - Translation('no', 'yes'), - Translation('open', 'closed'), - Translation('ok', 'alarm'), - Translation('running', 'stopped'), -#### added for ExtTransferSwitch package - Translation('on generator', 'on grid') -] - -class SystemBus(dbus.bus.BusConnection): - def __new__(cls): - return dbus.bus.BusConnection.__new__(cls, dbus.bus.BusConnection.TYPE_SYSTEM) - -class SessionBus(dbus.bus.BusConnection): - def __new__(cls): - return dbus.bus.BusConnection.__new__(cls, dbus.bus.BusConnection.TYPE_SESSION) - -class InputPin(): - devid = None - devinstance = None - - def __init__(self, name=None, path=None, label=None): - self.name = name - self.path = path - self.label = label - -class BasePulseCounter(object): - pass - -class DebugPulseCounter(BasePulseCounter): - def __init__(self): - self.gpiomap = {} - - def register(self, path, gpio): - self.gpiomap[gpio] = None - return 0 - - def unregister(self, gpio): - del self.gpiomap[gpio] - - def registered(self, gpio): - return gpio in self.gpiomap - - def __call__(self): - from itertools import cycle - from time import sleep - for level in cycle([0, 1]): - for gpio in list(self.gpiomap.keys()): - yield gpio, level - sleep(0.25/len(self.gpiomap)) - -class EpollPulseCounter(BasePulseCounter): - def __init__(self): - self.gpiomap = {} - self.states = {} - self.ob = epoll() - - def register(self, path, gpio): - path = os.path.realpath(path) - - # Set up gpio for rising edge interrupts - try: - with open(os.path.join(path, 'edge'), 'ab') as fp: - fp.write(b'both') - except: - pass - - fp = open(os.path.join(path, 'value'), 'rb') - level = int(fp.read()) # flush it in case it's high at startup - self.gpiomap[gpio] = fp - self.states[gpio] = level - self.ob.register(fp, EPOLLPRI) - return level - - def unregister(self, gpio): - fp = self.gpiomap[gpio] - self.ob.unregister(fp) - del self.gpiomap[gpio] - del self.states[gpio] - fp.close() - - def registered(self, gpio): - return gpio in self.gpiomap - - def __call__(self): - while True: - # We have a timeout of 1 second on the poll, because poll() only - # looks at files in the epoll object at the time poll() was called. - # The timeout means we let other files (added via calls to - # register/unregister) into the loop at least that often. - self.ob.poll(1) - - # When coming out of the epoll call, we read all the gpios to make - # sure we didn't miss any edges. This is a safety fallback that - # ensures everything is up to date once a second, but - # edge-triggered results are handled immediately. - # NOTE: There has not been a report of a missed interrupt yet. - # Belts and suspenders. - for gpio, fp in list(self.gpiomap.items()): - os.lseek(fp.fileno(), 0, os.SEEK_SET) - v = int(os.read(fp.fileno(), 1)) - if v != self.states[gpio]: - self.states[gpio] = v - yield gpio, v - -class PollingPulseCounter(BasePulseCounter): - def __init__(self): - self.gpiomap = {} - - def register(self, path, gpio): - path = os.path.realpath(path) - - fp = open(os.path.join(path, 'value'), 'rb') - level = int(fp.read()) - self.gpiomap[gpio] = [fp, level] - return level - - def unregister(self, gpio): - del self.gpiomap[gpio] - - def registered(self, gpio): - return gpio in self.gpiomap - - def __call__(self): - from itertools import cycle - from time import sleep - while True: - for gpio, (fp, level) in list(self.gpiomap.items()): - fp.seek(0, os.SEEK_SET) - v = int(fp.read()) - if v != level: - self.gpiomap[gpio][1] = v - yield gpio, v - sleep(1) - -class HandlerMaker(type): - """ Meta-class for keeping track of all extended classes. """ - def __init__(cls, name, bases, attrs): - if not hasattr(cls, 'handlers'): - cls.handlers = {} - else: - cls.handlers[cls.type_id] = cls - -class PinHandler(object, metaclass=HandlerMaker): - product_id = 0xFFFF - _product_name = 'Generic GPIO' - dbus_name = "digital" - def __init__(self, bus, base, path, gpio, settings): - self.bus = bus - self.settings = settings - self._level = 0 # Remember last state - - instance = int(settings['instance'].split(':')[1]) - - name = str(gpio) - if name[0].isdecimal(): - name = 'input_' + name - - self.service = VeDbusService( - "{}.{}.{}".format(base, self.dbus_name, name), bus=bus, - register=False) - - # Add objects required by ve-api - self.service.add_path('/Mgmt/ProcessName', __file__) - self.service.add_path('/Mgmt/ProcessVersion', VERSION) - self.service.add_path('/Mgmt/Connection', path) - self.service.add_path('/DeviceInstance', instance) - self.service.add_path('/ProductId', self.product_id) - self.service.add_path('/ProductName', self.product_name) - self.service.add_path('/Connected', 1) - - # Custom name setting - def _change_name(p, v): - # This should fire a change event that will update product_name - # below. - settings['name'] = v - return True - - self.service.add_path('/CustomName', settings['name'], writeable=True, - onchangecallback=_change_name) - - # We'll count the pulses for all types of services - self.service.add_path('/Count', value=settings['count']) - - # Register our name on dbus - self.service.register() - - @property - def product_name(self): - return self.settings['name'] or self._product_name - - @product_name.setter - def product_name(self, v): - # Some pin types don't have an associated service (Disabled pins for - # example) - if self.service is not None: - self.service['/ProductName'] = v or self._product_name - - def deactivate(self): - self.save_count() - self.service.__del__() - del self.service - self.service = None - - @property - def level(self): - return self._level - - @level.setter - def level(self, l): - self._level = int(bool(l)) - - def toggle(self, level): - raise NotImplementedError - - def _toggle(self, level, service): - # Only increment Count on rising edge. - if level and level != self._level: - service['/Count'] = (service['/Count']+1) % MAXCOUNT - self._level = level - - def refresh(self): - """ Toggle state to last remembered state. This is called if settings - are changed so the Service can recalculate paths. """ - self.toggle(self._level) - - def save_count(self): - if self.service is not None: - self.settings['count'] = self.count - - @property - def active(self): - return self.service is not None - - @property - def count(self): - return self.service['/Count'] - - @count.setter - def count(self, v): - self.service['/Count'] = v - - @classmethod - def createHandler(cls, _type, *args, **kwargs): - if _type in cls.handlers: - return cls.handlers[_type](*args, **kwargs) - return None - - -class NopPin(object): - """ Mixin for a pin with empty behaviour. Mix in BEFORE PinHandler so that - __init__ overrides the base behaviour. """ - def __init__(self, bus, base, path, gpio, settings): - self.service = None - self.bus = bus - self.settings = settings - self._level = 0 # Remember last state - - def deactivate(self): - pass - - def toggle(self, level): - self._level = level - - def save_count(self): - # Do nothing - pass - - @property - def count(self): - return self.settings['count'] - - @count.setter - def count(self, v): - pass - - def refresh(self): - pass - - -class DisabledPin(NopPin, PinHandler): - """ Place holder for a disabled pin. """ - _product_name = 'Disabled' - type_id = 0 - - -class VolumeCounter(PinHandler): - product_id = 0xA165 - _product_name = "Generic pulse meter" - dbus_name = "pulsemeter" - type_id = 1 - - def __init__(self, bus, base, path, gpio, settings): - super(VolumeCounter, self).__init__(bus, base, path, gpio, settings) - self.service.add_path('/Aggregate', value=self.count*self.rate, - gettextcallback=lambda p, v: (str(v) + ' cubic meter')) - - @property - def rate(self): - return self.settings['rate'] - - def toggle(self, level): - with self.service as s: - super(VolumeCounter, self)._toggle(level, s) - s['/Aggregate'] = self.count * self.rate - -class TouchEnable(NopPin, PinHandler): - """ The pin is used to enable/disable the Touch screen when toggled. - No dbus-service is created. """ - _product_name = 'TouchEnable' - type_id = 11 - - def __init__(self, *args, **kwargs): - super(TouchEnable, self).__init__(*args, **kwargs) - self.item = VeDbusItemImport(self.bus, - "com.victronenergy.settings", "/Settings/Gui/TouchEnabled") - - def toggle(self, level): - super(TouchEnable, self).toggle(level) - - # Toggle the touch-enable setting on the downward edge. - # Level is expected to be high with the switch open, and - # pulled low when pushed. - if level == 0: - enabled = bool(self.item.get_value()) - self.item.set_value(int(not enabled)) - - def deactivate(self): - # Always re-enable touch when the pin is deactivated. - # This adds another layer of protection against accidental - # lockout. - self.item.set_value(1) - del self.item - -class PinAlarm(PinHandler): - product_id = 0xA166 - _product_name = "Generic digital input" - dbus_name = "digitalinput" - type_id = 0xFF - translation = 0 # low, high - - def __init__(self, bus, base, path, gpio, settings): - super(PinAlarm, self).__init__(bus, base, path, gpio, settings) - self.service.add_path('/InputState', value=0) - self.service.add_path('/State', value=self.get_state(0), - gettextcallback=lambda p, v: TRANSLATIONS[v//2][v%2]) - self.service.add_path('/Alarm', value=self.get_alarm_state(0)) - - # Also expose the type - self.service.add_path('/Type', value=self.type_id, - gettextcallback=lambda p, v: INPUTTYPES[v]) - - def toggle(self, level): - with self.service as s: - super(PinAlarm, self)._toggle(level, s) - s['/InputState'] = bool(level)*1 - s['/State'] = self.get_state(level) - # Ensure that the alarm flag resets if the /AlarmSetting config option - # disappears. - s['/Alarm'] = self.get_alarm_state(level) - - def get_state(self, level): - state = level ^ self.settings['invert'] - return 2 * self.translation + state - - def get_alarm_state(self, level): - return 2 * bool( - (level ^ self.settings['invertalarm']) and self.settings['alarm']) - - -class Generator(PinAlarm): - _product_name = "Generator" - type_id = 9 - translation = 5 # running, stopped - startStopService = 'com.victronenergy.generator.startstop0' - - def __init__(self, bus, base, path, gpio, settings): - super(Generator, self).__init__(bus, base, path, gpio, settings) - self._gpio = gpio - # Periodically rewrite the generator selection. The Multi may reset - # causing this to be lost, or a race condition on startup may cause - # it to not be set properly. - self._timer = GLib.timeout_add(30000, - lambda: self.select_generator(self.level ^ self.settings['invert'] ^ 1) or True) - -#### added for ExtTransferSwitch package - self.mainVeBusServiceItem = None - - - def select_generator(self, v): - # Find all vebus services, and let them know - try: - services = [n for n in self.bus.list_names() if n.startswith( - 'com.victronenergy.vebus.')] - for n in services: -#### added for ExtTransferSwitch package - # skip this service if it is the main VE.Bus device - # processing for that is handled in ExtTransferSwitch - try: - if self.mainVeBusServiceItem == None: - self.mainVeBusServiceItem = VeDbusItemImport(self.bus, - "com.victronenergy.service", "/VebusService") - if n == self.mainVeBusService.get_value (): - continue - except: - pass -#### end added for ExtTransferSwitch package - - self.bus.call_async(n, '/Ac/Control/RemoteGeneratorSelected', 'com.victronenergy.BusItem', - 'SetValue', 'v', [v], None, None) - except dbus.exceptions.DBusException: - print ("DBus exception setting RemoteGeneratorSelected") - traceback.print_exc() - try: - self.bus.call_async(self.startStopService, '/DigitalInput/Input', 'com.victronenergy.BusItem', - 'SetValue', 'v', [self._gpio], None, None) - self.bus.call_async(self.startStopService, '/DigitalInput/Running', 'com.victronenergy.BusItem', - 'SetValue', 'v', [v], None, None) - except dbus.exceptions.DBusException: - print ("DBus exception setting RemoteGeneratorSelected") - traceback.print_exc() - - def toggle(self, level): - super(Generator, self).toggle(level) - - # Follow the same inversion sense as for display - self.select_generator(level ^ self.settings['invert'] ^ 1) - - def deactivate(self): - super(Generator, self).deactivate() - # When deactivating, reset the generator selection state - self.select_generator(0) - try: - self.bus.call_async(self.startStopService, '/DigitalInput/Input', 'com.victronenergy.BusItem', - 'SetValue', 'v', [0], None, None) - except dbus.exceptions.DBusException: - pass - # And kill the periodic job - GLib.source_remove(self._timer) - self._timer = None - -# Various types of things we might want to monitor -class DoorSensor(PinAlarm): - _product_name = "Door alarm" - type_id = 2 - translation = 3 # open, closed - -class BilgePump(PinAlarm): - _product_name = "Bilge pump" - type_id = 3 - translation = 1 # off, on - -class BilgeAlarm(PinAlarm): - _product_name = "Bilge alarm" - type_id = 4 - translation = 4 # ok, alarm - -class BurglarAlarm(PinAlarm): - _product_name = "Burglar alarm" - type_id = 5 - translation = 4 # ok, alarm - -class SmokeAlarm(PinAlarm): - _product_name = "Smoke alarm" - type_id = 6 - translation = 4 # ok, alarm - -class FireAlarm(PinAlarm): - _product_name = "Fire alarm" - type_id = 7 - translation = 4 # ok, alarm - -class CO2Alarm(PinAlarm): - _product_name = "CO2 alarm" - type_id = 8 - translation = 4 # ok, alarm - -class GenericIO(PinAlarm): - _product_name = "Generic I/O" - type_id = 10 - translation = 0 # low, high - -#### added for ExtTransferSwitch package -class TransferSwitch(PinAlarm): - _product_name = "External AC Input transfer switch" - type_id = 12 - translation = 6 # Grid In / Generator In - - -def dbusconnection(): - return SessionBus() if 'DBUS_SESSION_BUS_ADDRESS' in os.environ else SystemBus() - -def parse_config(conf): - f = open(conf) - - tag = None - pins = [] - - for line in f: - cmd, arg = line.strip().split(maxsplit=1) - - if cmd == 'tag': - tag = arg - continue - - if cmd == 'input': - pth, label = arg.split(maxsplit=1) - label = label.strip('"') - pin = InputPin(tag + '_' + os.path.basename(pth), pth, label) - pins.append(pin) - continue - - f.close() - - return pins - -def main(): - parser = ArgumentParser(description=sys.argv[0]) - parser.add_argument('--servicebase', - help='Base service name on dbus, default is com.victronenergy', - default='com.victronenergy') - parser.add_argument('--poll', - help='Use a different kind of polling. Options are epoll, dumb and debug', - default='epoll') - parser.add_argument('--conf', action='append', default=[], help='Config file') - parser.add_argument('inputs', nargs='*', help='Path to digital input') - args = parser.parse_args() - - PulseCounter = { - 'debug': DebugPulseCounter, - 'poll': PollingPulseCounter, - }.get(args.poll, EpollPulseCounter) - - DBusGMainLoop(set_as_default=True) - - ctlbus = dbusconnection() - ctlsvc = VeDbusService(args.servicebase + '.digitalinputs', bus=ctlbus, register=True) - - # Keep track of enabled services - services = {} - inputs = dict(enumerate(args.inputs, 1)) - pulses = PulseCounter() # callable that iterates over pulses - - def register_gpio(path, gpio, bus, settings): - _type = settings['inputtype'] - print ("Registering GPIO {} for type {}".format(gpio, _type)) - - handler = PinHandler.createHandler(_type, - bus, args.servicebase, path, gpio, settings) - services[gpio] = handler - - # Only monitor if enabled - if _type > 0: - handler.level = pulses.register(path, gpio) - handler.refresh() - - def unregister_gpio(gpio): - print ("unRegistering GPIO {}".format(gpio)) - if pulses.registered(gpio): - pulses.unregister(gpio) - services[gpio].deactivate() - - def handle_setting_change(pin, setting, old, new): - # This handler may also be called if some attribute of a setting - # is changed, but not the value. Bail if the value is unchanged. - if old == new: - return - - inp = pin.name - - if setting == 'inputtype': - if new: - # Get current bus and settings objects, to be reused - service = services[inp] - bus, settings = service.bus, service.settings - - # Input enabled. If already enabled, unregister the old one first. - if pulses.registered(inp): - unregister_gpio(inp) - - # We only want 1 generator input at a time, so disable other inputs configured as generator. - for i in inputs: - if i != inp and services[i].settings['inputtype'] == 9 == new: - services[i].settings['inputtype'] = 0 - unregister_gpio(i) - - # Before registering the new input, reset its settings to defaults - settings['count'] = 0 - settings['invert'] = 0 - settings['invertalarm'] = 0 - settings['alarm'] = 0 - - # Register it - register_gpio(pin.path, inp, bus, settings) - elif old: - # Input disabled - unregister_gpio(inp) - - ctlsvc['/Devices/{}/Type'.format(inp)] = new - elif setting in ('rate', 'invert', 'alarm', 'invertalarm'): - services[inp].refresh() - elif setting == 'name': - services[inp].product_name = new - elif setting == 'count': - # Don't want this triggered on a period save, so only execute - # if it has changed. - v = int(new) - s = services[inp] - if s.active and s.count != v: - s.count = v - s.refresh() - - def change_type(sd, path, val): - if not 0 <= val < len(INPUTTYPES): - return False - sd['inputtype'] = val - return True - - pins = [] - - for inp, pth in inputs.items(): - pin = InputPin(inp, pth, 'Digital input {}'.format(inp)) - pin.devid = os.path.basename(pth) - pin.devinstance = inp - pins.append(pin) - - for conf in args.conf: - pins += parse_config(conf) - - for pin in pins: - inp = pin.name - devid = pin.devid or pin.name - inst = 'digitalinput:{}'.format(pin.devinstance or 10) - supported_settings = { - 'inputtype': ['/Settings/DigitalInput/{}/Type'.format(inp), 0, 0, len(INPUTTYPES)-1], - 'rate': ['/Settings/DigitalInput/{}/Multiplier'.format(inp), 0.001, 0, 1.0], - 'count': ['/Settings/DigitalInput/{}/Count'.format(inp), 0, 0, MAXCOUNT, 1], - 'invert': ['/Settings/DigitalInput/{}/InvertTranslation'.format(inp), 0, 0, 1], - 'invertalarm': ['/Settings/DigitalInput/{}/InvertAlarm'.format(inp), 0, 0, 1], - 'alarm': ['/Settings/DigitalInput/{}/AlarmSetting'.format(inp), 0, 0, 1], - 'name': ['/Settings/DigitalInput/{}/CustomName'.format(inp), '', '', ''], - 'instance': ['/Settings/Devices/{}/ClassAndVrmInstance'.format(devid), inst, '', ''], - } - bus = dbusconnection() - sd = SettingsDevice(bus, supported_settings, partial(handle_setting_change, pin), timeout=10) - register_gpio(pin.path, inp, bus, sd) - ctlsvc.add_path('/Devices/{}/Label'.format(inp), pin.label) - ctlsvc.add_path('/Devices/{}/Type'.format(inp), sd['inputtype'], - writeable=True, onchangecallback=partial(change_type, sd)) - - def poll(mainloop): - from time import time - idx = 0 - - try: - for inp, level in pulses(): - # epoll object only resyncs once a second. We may receive - # a pulse for something that's been deregistered. - try: - services[inp].toggle(level) - except KeyError: - continue - except: - traceback.print_exc() - mainloop.quit() - - # Need to run the gpio polling in separate thread. Pass in the mainloop so - # the thread can kill us if there is an exception. - mainloop = GLib.MainLoop() - - poller = Thread(target=lambda: poll(mainloop)) - poller.daemon = True - poller.start() - - # Periodically save the counter - def save_counters(): - for svc in services.values(): - svc.save_count() - return True - GLib.timeout_add(SAVEINTERVAL, save_counters) - - # Save counter on shutdown - signal.signal(signal.SIGTERM, lambda *args: sys.exit(0)) - - try: - mainloop.run() - except KeyboardInterrupt: - pass - finally: - save_counters() - -if __name__ == "__main__": - main() diff --git a/FileSets/v3.51/dbus_digitalinputs.py b/FileSets/v3.51/dbus_digitalinputs.py new file mode 120000 index 00000000..8dbac81a --- /dev/null +++ b/FileSets/v3.51/dbus_digitalinputs.py @@ -0,0 +1 @@ +../v3.52~1/dbus_digitalinputs.py \ No newline at end of file diff --git a/FileSets/v3.51/startstop.py b/FileSets/v3.51/startstop.py deleted file mode 100644 index 244de097..00000000 --- a/FileSets/v3.51/startstop.py +++ /dev/null @@ -1,1482 +0,0 @@ -#!/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 vebus_service(self): - return self.parent._vebusservice if self.parent._vebusservice else '' - - @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.vebus_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.vebus_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.vebus_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.vebus_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.vebus_service, ('/Alarms/%s/Overload' % phase))) - return safe_max(inverterOverload) - return v - -class StopOnAc1Condition(Condition): - name = 'stoponac1' - monitoring = 'vebus' - boolean = True - timed = False - - def get_value(self): - # AC input 1 - available = self.monitor.get_value(self.vebus_service, - '/Ac/State/AcIn1Available') - if available is None: - # Not supported in firmware, fall back to old behaviour - activein = self.monitor.get_value(self.vebus_service, - '/Ac/ActiveIn/ActiveInput') - - # Active input is connected - connected = self.monitor.get_value(self.vebus_service, - '/Ac/ActiveIn/Connected') - if None not in (activein, connected): - return activein == 0 and connected == 1 - return None - - return bool(available) - -class StopOnAc2Condition(Condition): - name = 'stoponac2' - monitoring = 'vebus' - boolean = True - timed = False - - def get_value(self): - # AC input 2 available (used when grid is on AC-in-2) - available = self.monitor.get_value(self.vebus_service, - '/Ac/State/AcIn2Available') - - return None if available is None else bool(available) - -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._errorstate = 0 - self._battery_service = None - self._battery_prefix = None - - self._acpower_inverter_input = { - '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) - }) - - 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/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._vebusservice is not None and \ - dbusServiceName == self._vebusservice and \ - dbusPath == '/Ac/State/AcIn1Available': - 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_acinput() - 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_acinput(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_acpower_inverter_input() - return - - vebus_service = self._vebusservice if self._vebusservice else '' - activein_state = self._dbusmonitor.get_value( - vebus_service, '/Ac/ActiveIn/Connected') - - # 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._acpower_inverter_input['unabletostart']: - self.log_info('Generator detected at inverter AC input, alarm removed') - self._reset_acpower_inverter_input() -#### GuiMods warm-up / cool-down - elif not processAlarm: - self._reset_acpower_inverter_input() - return - elif self._acpower_inverter_input['timeout'] < self.RETRIES_ON_ERROR: - self._acpower_inverter_input['timeout'] += 1 - elif not self._acpower_inverter_input['unabletostart']: - self._acpower_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_acpower_inverter_input(self, clear_error=True): - 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. - readout_supported = self._dbusmonitor.get_value(self._vebusservice, - '/Ac/State/AcIn1Available') is not None - self._dbusservice['/Capabilities'] |= ( - Capabilities.WarmupCooldown if readout_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': - 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) - elif batterymeasurement == 'default': - newbatteryservice = 'default' - - 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._vebusservice != vebusservice: - self._vebusservice = vebusservice - self._set_capabilities() - self.log_info('Vebus service (%s) found! Using it for generator start/stop' % vebusservice) - else: - if self._vebusservice is not None: - self.log_info('Vebus service (%s) dissapeared! Stop evaluating related conditions' % self._vebusservice) - else: - self.log_info('Error getting Vebus service!') - self._vebusservice = 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._vebusservice 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._vebusservice, '/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._vebusservice, '/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/v3.51/startstop.py b/FileSets/v3.51/startstop.py new file mode 120000 index 00000000..263438af --- /dev/null +++ b/FileSets/v3.51/startstop.py @@ -0,0 +1 @@ +../v3.52~1/startstop.py \ No newline at end of file diff --git a/FileSets/v3.52~1/COMPLETE b/FileSets/v3.52~1/COMPLETE new file mode 100644 index 00000000..e69de29b diff --git a/FileSets/v3.52~1/DetailAcInput.qml b/FileSets/v3.52~1/DetailAcInput.qml new file mode 100644 index 00000000..d2865e4a --- /dev/null +++ b/FileSets/v3.52~1/DetailAcInput.qml @@ -0,0 +1,606 @@ +////// detail page for setting input current limit +////// and displaying AC input details +////// pushed from Flow overview + +import QtQuick 1.1 +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.51/DetailAcInput.qml.orig b/FileSets/v3.52~1/DetailAcInput.qml.orig similarity index 100% rename from FileSets/v3.51/DetailAcInput.qml.orig rename to FileSets/v3.52~1/DetailAcInput.qml.orig diff --git a/FileSets/v3.52~1/DetailInverter.qml b/FileSets/v3.52~1/DetailInverter.qml new file mode 100644 index 00000000..42fab4e7 --- /dev/null +++ b/FileSets/v3.52~1/DetailInverter.qml @@ -0,0 +1,650 @@ +////// detail page for setting inverter mode +////// and displaying inverter details +////// pushed from Flow overview + +import QtQuick 1.1 +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.51/DetailInverter.qml.orig b/FileSets/v3.52~1/DetailInverter.qml.orig similarity index 100% rename from FileSets/v3.51/DetailInverter.qml.orig rename to FileSets/v3.52~1/DetailInverter.qml.orig diff --git a/FileSets/v3.52~1/DetailLoadsCombined.qml b/FileSets/v3.52~1/DetailLoadsCombined.qml new file mode 100644 index 00000000..5909cb10 --- /dev/null +++ b/FileSets/v3.52~1/DetailLoadsCombined.qml @@ -0,0 +1,146 @@ +////// detail page for displaying critical AC output details +////// pushed from Flow overview + +import QtQuick 1.1 +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.51/DetailLoadsCombined.qml.orig b/FileSets/v3.52~1/DetailLoadsCombined.qml.orig similarity index 100% rename from FileSets/v3.51/DetailLoadsCombined.qml.orig rename to FileSets/v3.52~1/DetailLoadsCombined.qml.orig diff --git a/FileSets/v3.52~1/DetailLoadsOnInput.qml b/FileSets/v3.52~1/DetailLoadsOnInput.qml new file mode 100644 index 00000000..999a62ac --- /dev/null +++ b/FileSets/v3.52~1/DetailLoadsOnInput.qml @@ -0,0 +1,146 @@ +////// detail page for displaying non-critical AC output details +////// pushed from Flow overview + +import QtQuick 1.1 +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.51/DetailLoadsOnInput.qml.orig b/FileSets/v3.52~1/DetailLoadsOnInput.qml.orig similarity index 100% rename from FileSets/v3.51/DetailLoadsOnInput.qml.orig rename to FileSets/v3.52~1/DetailLoadsOnInput.qml.orig diff --git a/FileSets/v3.52~1/DetailLoadsOnOutput.qml b/FileSets/v3.52~1/DetailLoadsOnOutput.qml new file mode 100644 index 00000000..2092989c --- /dev/null +++ b/FileSets/v3.52~1/DetailLoadsOnOutput.qml @@ -0,0 +1,151 @@ +////// detail page for displaying critical AC output details +////// pushed from Flow overview + +import QtQuick 1.1 +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.51/DetailLoadsOnOutput.qml.orig b/FileSets/v3.52~1/DetailLoadsOnOutput.qml.orig similarity index 100% rename from FileSets/v3.51/DetailLoadsOnOutput.qml.orig rename to FileSets/v3.52~1/DetailLoadsOnOutput.qml.orig diff --git a/FileSets/v3.52~1/HubData.qml b/FileSets/v3.52~1/HubData.qml new file mode 100644 index 00000000..6c0413dc --- /dev/null +++ b/FileSets/v3.52~1/HubData.qml @@ -0,0 +1,268 @@ +//////// 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 + +Item { + id: root + + property variant sys: theSystem + + property string systemPrefix: "com.victronenergy.system" + property string settingsPrefix: "com.victronenergy.settings" + property string vebusPrefix: _vebusService.valid ? _vebusService.value : "" + +//////// add to support VE.Direct inverters + property string inverterService: "" +//////// add for grid/genset meters + property string gridMeterService: "" + property string gensetService: "" + + property variant battery: _battery + property alias dcSystem: _dcSystem + property alias alternator: _alternator + property alias windGenerator: _windGenerator + property alias fuelCell: _fuelCell + property alias acCharger: _acCharger + 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 acInput: _activein + property alias genset: _genset + 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"} + } + +//////// added alternator + QtObject { + id: _alternator + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Alternator/Power"); unit: "W"} + } + +//////// added AC charger + QtObject { + id: _acCharger + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Charger/Power"); unit: "W"} + } + +//////// added wind generator + QtObject { + id: _windGenerator + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/WindGenerator/Power"); unit: "W"} + } + +//////// added fuel cell + QtObject { + id: _fuelCell + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/FuelCell/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") +//////// modified for VE.Direct inverter support + inverterSource: "/Ac/ActiveIn" + inverterService: sys.vebusPrefix != "" ? sys.vebusPrefix : root.inverterService + } + + 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") +//////// modified for VE.Direct inverter support + inverterSource: "/Ac/ActiveIn" + inverterService: sys.vebusPrefix != "" ? sys.vebusPrefix : root.inverterService + } + + ObjectAcConnection { + id: _activein + splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0 + bindPrefix: Utils.path(systemPrefix, "/Ac/ActiveIn") +//////// modified for VE.Direct inverter support + inverterSource: "/Ac/ActiveIn" + inverterService: sys.vebusPrefix != "" ? sys.vebusPrefix : root.inverterService + } + + ObjectAcConnection { + id: _acLoad + l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0) + isAcOutput: true + bindPrefix: Utils.path(systemPrefix, "/Ac/Consumption") +//////// modified for VE.Direct inverter support + inverterSource: "/Ac/Out" + inverterService: sys.vebusPrefix != "" ? sys.vebusPrefix : root.inverterService + } + + 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"} + } + +//////// add to support for adjustable watt / killowatt display switching + VBusItem { id: kwThresholdItem; bind: Utils.path(settingsPrefix, "/Settings/GuiMods/KilowattThreshold") } + property int kilowattThreshold: kwThresholdItem.valid ? kwThresholdItem.value : 1000 + +//////// add to support VE.Direct inverters +//////// and grid/genset meters + Component.onCompleted: discoverServices() + + // 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_INVERTER: + if (inverterService === "") + inverterService = service.name; + break;; + case DBusService.DBUS_SERVICE_GRIDMETER: + if (gridMeterService === "") + gridMeterService = service.name; + break;; + case DBusService.DBUS_SERVICE_GENSET: + if (gensetService === "") + gensetService = service.name; + break;; + } + } + + // Check available services inverter services + function discoverServices() + { + inverterService = "" + gridMeterService = "" + gensetService = "" + for (var i = 0; i < DBusServices.count; i++) + addService(DBusServices.at(i)) + } +} diff --git a/FileSets/v3.51/HubData.qml.orig b/FileSets/v3.52~1/HubData.qml.orig similarity index 100% rename from FileSets/v3.51/HubData.qml.orig rename to FileSets/v3.52~1/HubData.qml.orig diff --git a/FileSets/v3.52~1/ObjectAcConnection.qml b/FileSets/v3.52~1/ObjectAcConnection.qml new file mode 100644 index 00000000..9e87e2db --- /dev/null +++ b/FileSets/v3.52~1/ObjectAcConnection.qml @@ -0,0 +1,52 @@ +////// 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 + +QtObject { + property string bindPrefix + property string inverterService: "" + property string inverterSource: "" + + property VBusItem powerL1: VBusItem { bind: Utils.path(bindPrefix, "/L1/Power"); unit: "W"} + property VBusItem powerL2: VBusItem { bind: Utils.path(bindPrefix, "/L2/Power"); unit: "W"} + property VBusItem powerL3: VBusItem { bind: Utils.path(bindPrefix, "/L3/Power"); unit: "W"} + property VBusItem power: VBusItem { unit: "W" } + property VBusItem phaseCount: VBusItem { bind: Utils.path(bindPrefix, "/NumberOfPhases") } + property bool splitPhaseL2PassthruDisabled: false + property bool isAcOutput: false + property bool l2AndL1OutSummed: false +////// added to show bar graphs + property VBusItem inverterState: VBusItem { bind: Utils.path(systemPrefix, "/SystemState/State" ) } + + ////// add to show voltage, current, frequency and bar graphs and use grid/genset meter + property VBusItem voltageL1: VBusItem { bind: Utils.path (bindPrefix, "/L1/Voltage"); unit: "V"} + property VBusItem voltageL2: VBusItem { bind: Utils.path (bindPrefix, "/L2/Voltage"); unit: "V"} + property VBusItem voltageL3: VBusItem { bind: Utils.path (bindPrefix, "/L3/Voltage"); unit: "V"} + + property VBusItem currentL1: VBusItem { bind: Utils.path (bindPrefix, "/L1/Current"); unit: "A"} + property VBusItem currentL2: VBusItem { bind: Utils.path (bindPrefix, "/L2/Current"); unit: "A"} + property VBusItem currentL3: VBusItem { bind: Utils.path (bindPrefix, "/L3/Current"); unit: "A"} + + property VBusItem frequency: VBusItem { bind: Utils.path (bindPrefix, "/Frequency"); unit: "Hz"} + + property VBusItem inCurrentLimit: VBusItem { bind: Utils.path(inverterService, inverterSource, "/CurrentLimit"); unit: "A"} + ////// end add to show voltage, current and frequency + + // As systemcalc doesn't provide the totals anymore we calculate it here. + // Timer is needed because the values are not received in once and then the total + // changes too often on system with more than one phase + property Timer timer: Timer { + interval: 1000 + running: true + repeat: true + onTriggered: { + power.value = powerL1.valid || powerL2.valid || powerL3.valid ? (powerL1.valid ? powerL1.value : 0) + + (powerL2.valid ? powerL2.value : 0) + + (powerL3.valid ? powerL3.value : 0) : undefined + } + } +} diff --git a/FileSets/v3.51/ObjectAcConnection.qml.orig b/FileSets/v3.52~1/ObjectAcConnection.qml.orig similarity index 100% rename from FileSets/v3.51/ObjectAcConnection.qml.orig rename to FileSets/v3.52~1/ObjectAcConnection.qml.orig diff --git a/FileSets/v3.52~1/OverviewAcValuesEnhanced.qml b/FileSets/v3.52~1/OverviewAcValuesEnhanced.qml new file mode 100644 index 00000000..dc36d2ea --- /dev/null +++ b/FileSets/v3.52~1/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 1.1 +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.51/OverviewAcValuesEnhanced.qml.orig b/FileSets/v3.52~1/OverviewAcValuesEnhanced.qml.orig similarity index 100% rename from FileSets/v3.51/OverviewAcValuesEnhanced.qml.orig rename to FileSets/v3.52~1/OverviewAcValuesEnhanced.qml.orig diff --git a/FileSets/v3.52~1/OverviewFlowComplex.qml b/FileSets/v3.52~1/OverviewFlowComplex.qml new file mode 100644 index 00000000..9a6d20ab --- /dev/null +++ b/FileSets/v3.52~1/OverviewFlowComplex.qml @@ -0,0 +1,1549 @@ +///// Enhanced DC Coupled / AC Coupled Overview for GuiMods + +import QtQuick 1.1 +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.51/OverviewFlowComplex.qml.orig b/FileSets/v3.52~1/OverviewFlowComplex.qml.orig similarity index 100% rename from FileSets/v3.51/OverviewFlowComplex.qml.orig rename to FileSets/v3.52~1/OverviewFlowComplex.qml.orig diff --git a/FileSets/v3.52~1/OverviewGeneratorEnhanced.qml b/FileSets/v3.52~1/OverviewGeneratorEnhanced.qml new file mode 100644 index 00000000..8c8c679a --- /dev/null +++ b/FileSets/v3.52~1/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 1.1 +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.51/OverviewGeneratorEnhanced.qml.orig b/FileSets/v3.52~1/OverviewGeneratorEnhanced.qml.orig similarity index 100% rename from FileSets/v3.51/OverviewGeneratorEnhanced.qml.orig rename to FileSets/v3.52~1/OverviewGeneratorEnhanced.qml.orig diff --git a/FileSets/v3.52~1/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.52~1/OverviewGeneratorRelayEnhanced.qml new file mode 100644 index 00000000..83326226 --- /dev/null +++ b/FileSets/v3.52~1/OverviewGeneratorRelayEnhanced.qml @@ -0,0 +1,8 @@ +import QtQuick 1.1 +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.51/OverviewGeneratorRelayEnhanced.qml.orig b/FileSets/v3.52~1/OverviewGeneratorRelayEnhanced.qml.orig similarity index 100% rename from FileSets/v3.51/OverviewGeneratorRelayEnhanced.qml.orig rename to FileSets/v3.52~1/OverviewGeneratorRelayEnhanced.qml.orig diff --git a/FileSets/v3.52~1/OverviewGridParallel.qml b/FileSets/v3.52~1/OverviewGridParallel.qml new file mode 100644 index 00000000..8e11752d --- /dev/null +++ b/FileSets/v3.52~1/OverviewGridParallel.qml @@ -0,0 +1,490 @@ +import QtQuick 1.1 +import "utils.js" as Utils + +OverviewPage { + 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 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) +////// GuiMods — DarkMode + titleColor: !darkMode ? "#E74c3c" : "#73261E" + color: !darkMode ? "#C0392B" : "#601C15" + 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") +////// GuiMods — DarkMode + color: !darkMode ? "#27AE60" : "#135730" + titleColor: !darkMode ? "#2ECC71" : "#176638" + 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") +////// GuiMods — DarkMode + color: !darkMode ? "#157894" : "#0a3c4a" + titleColor: !darkMode ? "#419FB9" : "#204f5c" + 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 + 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.51/OverviewGridParallel.qml.orig b/FileSets/v3.52~1/OverviewGridParallel.qml.orig similarity index 100% rename from FileSets/v3.51/OverviewGridParallel.qml.orig rename to FileSets/v3.52~1/OverviewGridParallel.qml.orig diff --git a/FileSets/v3.52~1/OverviewHub.qml b/FileSets/v3.52~1/OverviewHub.qml new file mode 100644 index 00000000..7c04b763 --- /dev/null +++ b/FileSets/v3.52~1/OverviewHub.qml @@ -0,0 +1,315 @@ +import QtQuick 1.1 +import "utils.js" as Utils + +OverviewPage { + 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 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) +////// GuiMods — DarkMode + titleColor: !darkMode ? "#E74c3c" : "#73261E" + color: !darkMode ? "#C0392B" : "#601C15" + + 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") +////// GuiMods — DarkMode + color: !darkMode ? "#27AE60" : "#135730" + titleColor: !darkMode ? "#2ECC71" : "#176638" + 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 + + 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.51/OverviewHub.qml.orig b/FileSets/v3.52~1/OverviewHub.qml.orig similarity index 100% rename from FileSets/v3.51/OverviewHub.qml.orig rename to FileSets/v3.52~1/OverviewHub.qml.orig diff --git a/FileSets/v3.52~1/OverviewHubEnhanced.qml b/FileSets/v3.52~1/OverviewHubEnhanced.qml new file mode 100644 index 00000000..9e2b2c65 --- /dev/null +++ b/FileSets/v3.52~1/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 1.1 +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.51/OverviewHubEnhanced.qml.orig b/FileSets/v3.52~1/OverviewHubEnhanced.qml.orig similarity index 100% rename from FileSets/v3.51/OverviewHubEnhanced.qml.orig rename to FileSets/v3.52~1/OverviewHubEnhanced.qml.orig diff --git a/FileSets/v3.52~1/OverviewMobileEnhanced.qml b/FileSets/v3.52~1/OverviewMobileEnhanced.qml new file mode 100644 index 00000000..664344ff --- /dev/null +++ b/FileSets/v3.52~1/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 1.1 +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.51/OverviewMobileEnhanced.qml.orig b/FileSets/v3.52~1/OverviewMobileEnhanced.qml.orig similarity index 100% rename from FileSets/v3.51/OverviewMobileEnhanced.qml.orig rename to FileSets/v3.52~1/OverviewMobileEnhanced.qml.orig diff --git a/FileSets/v3.52~1/OverviewTanksTempsDigInputs.qml b/FileSets/v3.52~1/OverviewTanksTempsDigInputs.qml new file mode 100644 index 00000000..781e163c --- /dev/null +++ b/FileSets/v3.52~1/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 1.1 +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.51/OverviewTanksTempsDigInputs.qml.orig b/FileSets/v3.52~1/OverviewTanksTempsDigInputs.qml.orig similarity index 100% rename from FileSets/v3.51/OverviewTanksTempsDigInputs.qml.orig rename to FileSets/v3.52~1/OverviewTanksTempsDigInputs.qml.orig diff --git a/FileSets/v3.52~1/PageGenerator.qml.USE_ORIGINAL b/FileSets/v3.52~1/PageGenerator.qml.USE_ORIGINAL new file mode 100644 index 00000000..e69de29b diff --git a/FileSets/v3.52~1/PageSettingsGenerator.qml b/FileSets/v3.52~1/PageSettingsGenerator.qml new file mode 100644 index 00000000..9b2625a6 --- /dev/null +++ b/FileSets/v3.52~1/PageSettingsGenerator.qml @@ -0,0 +1,115 @@ +//// GuiMods +//// added link to external state enable + +import QtQuick 1.1 +import com.victron.velib 1.0 +import "utils.js" as Utils + +MbPage { + id: root + title: qsTr("Generator start/stop settings") + property string settingsBindPrefix + property string startStopBindPrefix + property VBusItem acIn1Source: VBusItem { bind: "com.victronenergy.settings/Settings/SystemSetup/AcInput1" } + property VBusItem acIn2Source: VBusItem { bind: "com.victronenergy.settings/Settings/SystemSetup/AcInput2" } + property VBusItem capabilities: VBusItem { bind: Utils.path(startStopBindPrefix, "/Capabilities") } + property int warmupCapability: 1 + + model: VisibleItemModel { + + MbSubMenu { + id: conditions + description: qsTr("Conditions") + subpage: + Component { + PageGeneratorConditions { + title: qsTr("Conditions") + bindPrefix: root.settingsBindPrefix + startStopBindPrefix: root.startStopBindPrefix + } + } + } + + MbSpinBox { + description: qsTr("Minimum run time") + item { + bind: Utils.path(settingsBindPrefix, "/MinimumRuntime") + unit: "m" + decimals: 0 + step: 1 + } + } + + MbSubMenu { + show: capabilities.value & warmupCapability + description: qsTr("Warm-up & cool-down") + subpage: + Component { + PageSettingsGeneratorWarmup { + title: qsTr("Warm-up & cool-down") + } + } + } + + MbSwitch { + property bool generatorIsSet: acIn1Source.value === 2 || acIn2Source.value === 2 + name: qsTr("Detect generator at AC input") + bind: Utils.path(settingsBindPrefix, "/Alarms/NoGeneratorAtAcIn") + enabled: valid && (generatorIsSet || checked) + onClicked: { + if (!checked) { + if (!generatorIsSet) { + toast.createToast(qsTr("None of the AC inputs is set to generator. Go to the system setup page and set the correct " + + "AC input to generator in order to enable this functionality."), 10000, "icon-info-active") + } else { + toast.createToast(qsTr("An alarm will be triggered when no power from the generator is detected at the inverter AC input. " + + "Make sure that the correct AC input is set to generator on the system setup page."), 12000, "icon-info-active") + } + } + } + } + +//// GuiMods + MbSwitch { + name: qsTr("Link to external running state") + bind: Utils.path(settingsBindPrefix, "/LinkToExternalStatus") + onClicked: + { + if (!checked) + toast.createToast(qsTr("Manual run will be synchronized with the generaror 'is running digital input' or AC input"), 10000, "icon-info-active") + } + } + + MbSwitch { + name: qsTr("Alarm when generator is not in auto start mode") + bind: Utils.path(settingsBindPrefix, "/Alarms/AutoStartDisabled") + onClicked: { + if (!checked) { + toast.createToast(qsTr("An alarm will be triggered when auto start function is left disabled for more than 10 minutes."), 12000, "icon-info-active") + } + } + } + + MbSwitch { + id: timeZones + name: qsTr("Quiet hours") + bind: Utils.path(settingsBindPrefix, "/QuietHours/Enabled") + enabled: valid + writeAccessLevel: User.AccessUser + } + + MbEditBoxTime { + description: qsTr("Quiet hours start time") + item.bind: Utils.path(settingsBindPrefix, "/QuietHours/StartTime") + show: timeZones.checked + writeAccessLevel: User.AccessUser + } + + MbEditBoxTime { + description: qsTr("Quiet hours end time") + item.bind: Utils.path(settingsBindPrefix, "/QuietHours/EndTime") + show: timeZones.checked + writeAccessLevel: User.AccessUser + } + } +} diff --git a/FileSets/v3.51/PageSettingsGenerator.qml.orig b/FileSets/v3.52~1/PageSettingsGenerator.qml.orig similarity index 100% rename from FileSets/v3.51/PageSettingsGenerator.qml.orig rename to FileSets/v3.52~1/PageSettingsGenerator.qml.orig diff --git a/FileSets/v3.52~1/PageSettingsGuiMods.qml b/FileSets/v3.52~1/PageSettingsGuiMods.qml new file mode 100644 index 00000000..420a42ac --- /dev/null +++ b/FileSets/v3.52~1/PageSettingsGuiMods.qml @@ -0,0 +1,290 @@ +/////// new menu for all Gui Mods + +import QtQuick 1.1 +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.51/PageSettingsGuiMods.qml.orig b/FileSets/v3.52~1/PageSettingsGuiMods.qml.orig similarity index 100% rename from FileSets/v3.51/PageSettingsGuiMods.qml.orig rename to FileSets/v3.52~1/PageSettingsGuiMods.qml.orig diff --git a/FileSets/v3.52~1/PageSettingsRelay.qml b/FileSets/v3.52~1/PageSettingsRelay.qml new file mode 100644 index 00000000..44c08871 --- /dev/null +++ b/FileSets/v3.52~1/PageSettingsRelay.qml @@ -0,0 +1,516 @@ +//////// 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 + +MbPage { + id: pageRelaySettings + title: qsTr("Relay") + property string bindPrefix: "com.victronenergy.settings" + property VBusItem relay1Item: VBusItem { bind: "com.victronenergy.system/Relay/0/State" } + property bool hasRelay1: relay1Item.valid + property VBusItem relay2Item: VBusItem { bind: "com.victronenergy.system/Relay/1/State" } + property bool hasRelay2: relay2Item.valid + property VBusItem relay3Item: VBusItem { bind: "com.victronenergy.system/Relay/2/State" } + property bool hasRelay3: relay3Item.valid + property VBusItem relay4Item: VBusItem { bind: "com.victronenergy.system/Relay/3/State" } + property bool hasRelay4: relay4Item.valid + property VBusItem relay5Item: VBusItem { bind: "com.victronenergy.system/Relay/4/State" } + property bool hasRelay5: relay5Item.valid + property VBusItem relay6Item: VBusItem { bind: "com.victronenergy.system/Relay/5/State" } + property bool hasRelay6: relay6Item.valid + property VBusItem relay7Item: VBusItem { bind: "com.victronenergy.system/Relay/6/State" } + property bool hasRelay7: relay7Item.valid + property VBusItem relay8Item: VBusItem { bind: "com.victronenergy.system/Relay/7/State" } + property bool hasRelay8: relay8Item.valid + property VBusItem relay9Item: VBusItem { bind: "com.victronenergy.system/Relay/8/State" } + property bool hasRelay9: relay9Item.valid + property VBusItem relay10Item: VBusItem { bind: "com.victronenergy.system/Relay/9/State" } + property bool hasRelay10: relay10Item.valid + property VBusItem relay11Item: VBusItem { bind: "com.victronenergy.system/Relay/10/State" } + property bool hasRelay11: relay11Item.valid + property VBusItem relay12Item: VBusItem { bind: "com.victronenergy.system/Relay/11/State" } + property bool hasRelay12: relay12Item.valid + property VBusItem relay13Item: VBusItem { bind: "com.victronenergy.system/Relay/12/State" } + property bool hasRelay13: relay13Item.valid + property VBusItem relay14Item: VBusItem { bind: "com.victronenergy.system/Relay/13/State" } + property bool hasRelay14: relay14Item.valid + property VBusItem relay15Item: VBusItem { bind: "com.victronenergy.system/Relay/14/State" } + property bool hasRelay15: relay15Item.valid + property VBusItem relay16Item: VBusItem { bind: "com.victronenergy.system/Relay/15/State" } + property bool hasRelay16: relay16Item.valid + property VBusItem relay17Item: VBusItem { bind: "com.victronenergy.system/Relay/16/State" } + property bool hasRelay17: relay17Item.valid + property VBusItem relay18Item: VBusItem { bind: "com.victronenergy.system/Relay/17/State" } + property bool hasRelay18: relay18Item.valid + + property VBusItem relay1NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/0/CustomName") } + property VBusItem relay2NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/1/CustomName") } + property VBusItem relay3NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/2/CustomName") } + property VBusItem relay4NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/3/CustomName") } + property VBusItem relay5NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/4/CustomName") } + property VBusItem relay6NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/5/CustomName") } + property VBusItem relay7NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/6/CustomName") } + property VBusItem relay8NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/7/CustomName") } + property VBusItem relay9NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/8/CustomName") } + property VBusItem relay10NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/9/CustomName") } + property VBusItem relay11NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/10/CustomName") } + property VBusItem relay12NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/11/CustomName") } + property VBusItem relay13NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/12/CustomName") } + property VBusItem relay14NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/13/CustomName") } + property VBusItem relay15NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/14/CustomName") } + property VBusItem relay16NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/15/CustomName") } + property VBusItem relay17NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/16/CustomName") } + property VBusItem relay18NameItem: VBusItem { bind: Utils.path(bindPrefix, "/Settings/Relay/17/CustomName") } + + function relayName (nameItem, relayNumber) + { + var prefix, suffix + if (nameItem.valid && nameItem.value != "") + { + prefix = nameItem.value + " (" + suffix = ")" + } + else + { + prefix = "" + suffix = "" + } + if (relayNumber == 1) + return prefix + (hasRelay2 ? qsTr("Relay 1") : qsTr("Relay")) + suffix + " " + qsTr("On") + else + return prefix + qsTr("Relay") + " " + relayNumber + suffix + " " + qsTr("On") + } + + model: VisibleItemModel { + MbItemOptions { + id: relay1Function + description: hasRelay2 ? qsTr("Function (Relay 1)") : qsTr("Function") + bind: Utils.path(bindPrefix, "/Settings/Relay/Function") + possibleValues:[ + MbOption { description: qsTr("Alarm relay"); value: 0 }, + MbOption { description: qsTr("Genset start/stop"); value: 1 }, + MbOption { description: qsTr("Connected genset helper relay"); value: 5 }, + MbOption { description: qsTr("Tank pump"); value: 3 }, + MbOption { description: qsTr("Manual"); value: 2 }, + MbOption { description: qsTr("Temperature"); value: 4 } + ] + show: hasRelay1 + } + + MbItemOptions { + description: qsTr("Alarm relay polarity") + bind: Utils.path(bindPrefix, "/Settings/Relay/Polarity") + show: hasRelay1 && relay1Function.value === 0 + possibleValues: [ + MbOption { description: qsTr("Normally open"); value: 0 }, + MbOption { description: qsTr("Normally closed"); value: 1 } + ] + } + + MbSwitch { + id: manualSwitch1 + name: relayName (relay1NameItem, 1) + bind: "com.victronenergy.system/Relay/0/State" + show: hasRelay1 && relay1Function.value === 2 // manual mode + } + + MbItemOptions { + id: relay2Function + description: hasRelay2 ? qsTr("Function (Relay 2)") : qsTr("Function") + bind: Utils.path(bindPrefix, "/Settings/Relay/1/Function") + show: hasRelay2 + possibleValues:[ + MbOption { description: qsTr("Manual"); value: 2 }, + MbOption { description: qsTr("Temperature"); value: 4 } + ] + } + MbSwitch { + id: manualSwitch2 + name: relayName (relay2NameItem, 2) + bind: "com.victronenergy.system/Relay/1/State" + show: hasRelay2 && relay2Function.value === 2 + } + MbSwitch { + id: manualSwitch3 + name: relayName (relay3NameItem, 3) + bind: "com.victronenergy.system/Relay/2/State" + show: hasRelay3 + } + MbSwitch { + id: manualSwitch4 + name: relayName (relay4NameItem, 4) + bind: "com.victronenergy.system/Relay/3/State" + show: hasRelay4 + } + MbSwitch { + id: manualSwitch5 + name: relayName (relay5NameItem, 5) + bind: "com.victronenergy.system/Relay/4/State" + show: hasRelay5 + } + MbSwitch { + id: manualSwitch6 + name: relayName (relay6NameItem, 6) + bind: "com.victronenergy.system/Relay/5/State" + show: hasRelay6 + } + MbSwitch { + id: manualSwitch7 + name: relayName (relay7NameItem, 7) + bind: "com.victronenergy.system/Relay/6/State" + show: hasRelay7 + } + MbSwitch { + id: manualSwitch8 + name: relayName (relay8NameItem, 8) + bind: "com.victronenergy.system/Relay/7/State" + show: hasRelay8 + } + MbSwitch { + id: manualSwitch9 + name: relayName (relay9NameItem, 9) + bind: "com.victronenergy.system/Relay/8/State" + show: hasRelay9 + } + MbSwitch { + id: manualSwitch10 + name: relayName (relay10NameItem, 10) + bind: "com.victronenergy.system/Relay/9/State" + show: hasRelay10 + } + MbSwitch { + id: manualSwitch11 + name: relayName (relay11NameItem, 11) + bind: "com.victronenergy.system/Relay/10/State" + show: hasRelay11 + } + MbSwitch { + id: manualSwitch12 + name: relayName (relay12NameItem, 12) + bind: "com.victronenergy.system/Relay/11/State" + show: hasRelay12 + } + MbSwitch { + id: manualSwitch13 + name: relayName (relay13NameItem, 13) + bind: "com.victronenergy.system/Relay/12/State" + show: hasRelay13 + } + MbSwitch { + id: manualSwitch14 + name: relayName (relay14NameItem, 14) + bind: "com.victronenergy.system/Relay/13/State" + show: hasRelay14 + } + MbSwitch { + id: manualSwitch15 + name: relayName (relay15NameItem, 15) + bind: "com.victronenergy.system/Relay/14/State" + show: hasRelay15 + } + MbSwitch { + id: manualSwitch16 + name: relayName (relay16NameItem, 16) + bind: "com.victronenergy.system/Relay/15/State" + show: hasRelay16 + } + MbSwitch { + id: manualSwitch17 + name: relayName (relay17NameItem, 17) + bind: "com.victronenergy.system/Relay/16/State" + show: hasRelay17 + } + MbSwitch { + id: manualSwitch18 + name: relayName (relay18NameItem, 18) + bind: "com.victronenergy.system/Relay/17/State" + show: hasRelay18 + } + + MbSubMenu { + id: conditions + description: qsTr("Temperature control rules") + show: relay1Function.value === 4 || relay2Function.value === 4 + subpage: Component { + PageSettingsRelayTempSensors { + id: relayPage + title: qsTr("Temperature control rules") + } + } + } + + MbEditBox { + id: relay1name + description: qsTr("Relay 1 Name") + item.bind: "com.victronenergy.settings/Settings/Relay/0/CustomName" + show: hasRelay1 && item.valid && relay1Function.value === 2 // manual mode + maximumLength: 32 + enableSpaceBar: true + } + MbSwitch { + id: showRelay1 + name: qsTr("Show Relay 1 in overview") + bind: "com.victronenergy.settings/Settings/Relay/0/Show" + show: hasRelay1 + } + + MbEditBox { + id: relay2name + description: qsTr("Relay 2 Name") + item.bind: "com.victronenergy.settings/Settings/Relay/1/CustomName" + show: hasRelay2 && item.valid + maximumLength: 32 + enableSpaceBar: true + } + MbSwitch { + id: showRelay2 + name: qsTr("Show Relay 2 in overview") + bind: "com.victronenergy.settings/Settings/Relay/1/Show" + show: hasRelay2 + } + + MbEditBox { + id: relay3name + description: qsTr("Relay 3 Name") + item.bind: "com.victronenergy.settings/Settings/Relay/2/CustomName" + show: hasRelay3 && item.valid + maximumLength: 32 + enableSpaceBar: true + } + MbSwitch { + id: showRelay3 + name: qsTr("Show Relay 3 in overview") + bind: "com.victronenergy.settings/Settings/Relay/2/Show" + show: hasRelay3 + } + + MbEditBox { + id: relay4name + description: qsTr("Relay 4 Name") + item.bind: "com.victronenergy.settings/Settings/Relay/3/CustomName" + show: hasRelay4 && item.valid + maximumLength: 32 + enableSpaceBar: true + } + MbSwitch { + id: showRelay4 + name: qsTr("Show Relay 4 in overview") + bind: "com.victronenergy.settings/Settings/Relay/3/Show" + show: hasRelay4 + } + + MbEditBox { + id: relay5name + description: qsTr("Relay 5 Name") + item.bind: "com.victronenergy.settings/Settings/Relay/4/CustomName" + show: hasRelay5 && item.valid + maximumLength: 32 + enableSpaceBar: true + } + MbSwitch { + id: showRelay5 + name: qsTr("Show Relay 5 in overview") + bind: "com.victronenergy.settings/Settings/Relay/4/Show" + show: hasRelay5 + } + + MbEditBox { + id: relay6name + description: qsTr("Relay 6 Name") + item.bind: "com.victronenergy.settings/Settings/Relay/5/CustomName" + show: hasRelay6 && item.valid + maximumLength: 32 + enableSpaceBar: true + } + MbSwitch { + id: showRelay6 + name: qsTr("Show Relay 6 in overview") + bind: "com.victronenergy.settings/Settings/Relay/5/Show" + show: hasRelay6 + } + + MbEditBox { + id: relay7name + description: qsTr("Relay 7 Name") + item.bind: "com.victronenergy.settings/Settings/Relay/6/CustomName" + show: hasRelay7 && item.valid + maximumLength: 32 + enableSpaceBar: true + } + MbSwitch { + id: showRelay7 + name: qsTr("Show Relay 7 in overview") + bind: "com.victronenergy.settings/Settings/Relay/6/Show" + show: hasRelay7 + } + + MbEditBox { + id: relay8name + description: qsTr("Relay 8 Name") + item.bind: "com.victronenergy.settings/Settings/Relay/7/CustomName" + show: hasRelay8 && item.valid + maximumLength: 32 + enableSpaceBar: true + } + MbSwitch { + id: showRelay8 + name: qsTr("Show Relay 8 in overview") + bind: "com.victronenergy.settings/Settings/Relay/7/Show" + show: hasRelay8 + } + + MbEditBox { + id: relay9name + description: qsTr("Relay 9 Name") + item.bind: "com.victronenergy.settings/Settings/Relay/8/CustomName" + show: hasRelay9 && item.valid + maximumLength: 32 + enableSpaceBar: true + } + MbSwitch { + id: showRelay9 + name: qsTr("Show Relay 9 in overview") + bind: "com.victronenergy.settings/Settings/Relay/8/Show" + show: hasRelay9 + } + + MbEditBox { + id: relay10name + description: qsTr("Relay 10 Name") + item.bind: "com.victronenergy.settings/Settings/Relay/9/CustomName" + show: hasRelay10 && item.valid + maximumLength: 32 + enableSpaceBar: true + } + MbSwitch { + id: showRelay10 + name: qsTr("Show Relay 10 in overview") + bind: "com.victronenergy.settings/Settings/Relay/9/Show" + show: hasRelay10 + } + + MbEditBox { + id: relay11name + description: qsTr("Relay 11 Name") + item.bind: "com.victronenergy.settings/Settings/Relay/10/CustomName" + show: hasRelay11 && item.valid + maximumLength: 32 + enableSpaceBar: true + } + MbSwitch { + id: showRelay11 + name: qsTr("Show Relay 11 in overview") + bind: "com.victronenergy.settings/Settings/Relay/10/Show" + show: hasRelay11 + } + + MbEditBox { + id: relay12name + description: qsTr("Relay 12 Name") + item.bind: "com.victronenergy.settings/Settings/Relay/11/CustomName" + show: hasRelay12 && item.valid + maximumLength: 32 + enableSpaceBar: true + } + MbSwitch { + id: showRelay12 + name: qsTr("Show Relay 12 in overview") + bind: "com.victronenergy.settings/Settings/Relay/11/Show" + show: hasRelay12 + } + + MbEditBox { + id: relay13name + description: qsTr("Relay 13 Name") + item.bind: "com.victronenergy.settings/Settings/Relay/12/CustomName" + show: hasRelay13 && item.valid + maximumLength: 32 + enableSpaceBar: true + } + MbSwitch { + id: showRelay13 + name: qsTr("Show Relay 13 in overview") + bind: "com.victronenergy.settings/Settings/Relay/12/Show" + show: hasRelay13 + } + + MbEditBox { + id: relay14name + description: qsTr("Relay 14 Name") + item.bind: "com.victronenergy.settings/Settings/Relay/13/CustomName" + show: hasRelay14 && item.valid + maximumLength: 32 + enableSpaceBar: true + } + MbSwitch { + id: showRelay14 + name: qsTr("Show Relay 14 in overview") + bind: "com.victronenergy.settings/Settings/Relay/13/Show" + show: hasRelay14 + } + + MbEditBox { + id: relay15name + description: qsTr("Relay 15 Name") + item.bind: "com.victronenergy.settings/Settings/Relay/14/CustomName" + show: hasRelay15 && item.valid + maximumLength: 32 + enableSpaceBar: true + } + MbSwitch { + id: showRelay15 + name: qsTr("Show Relay 15 in overview") + bind: "com.victronenergy.settings/Settings/Relay/14/Show" + show: hasRelay15 + } + + MbEditBox { + id: relay16name + description: qsTr("Relay 16 Name") + item.bind: "com.victronenergy.settings/Settings/Relay/15/CustomName" + show: hasRelay16 && item.valid + maximumLength: 32 + enableSpaceBar: true + } + MbSwitch { + id: showRelay16 + name: qsTr("Show Relay 16 in overview") + bind: "com.victronenergy.settings/Settings/Relay/15/Show" + show: hasRelay16 + } + + MbEditBox { + id: relay17name + description: qsTr("Relay 17 Name") + item.bind: "com.victronenergy.settings/Settings/Relay/16/CustomName" + show: hasRelay17 && item.valid + maximumLength: 32 + enableSpaceBar: true + } + MbSwitch { + id: showRelay17 + name: qsTr("Show Relay 17 in overview") + bind: "com.victronenergy.settings/Settings/Relay/16/Show" + show: hasRelay17 + } + MbEditBox { + id: relay18name + description: qsTr("Relay 18 Name") + item.bind: "com.victronenergy.settings/Settings/Relay/17/CustomName" + show: hasRelay18 && item.valid + maximumLength: 32 + enableSpaceBar: true + } + MbSwitch { + id: showRelay18 + name: qsTr("Show Relay 18 in overview") + bind: "com.victronenergy.settings/Settings/Relay/17/Show" + show: hasRelay18 + } + } +} diff --git a/FileSets/v3.51/PageSettingsRelay.qml.orig b/FileSets/v3.52~1/PageSettingsRelay.qml.orig similarity index 100% rename from FileSets/v3.51/PageSettingsRelay.qml.orig rename to FileSets/v3.52~1/PageSettingsRelay.qml.orig diff --git a/FileSets/v3.52~1/PowerGauge.qml b/FileSets/v3.52~1/PowerGauge.qml new file mode 100644 index 00000000..80c182c3 --- /dev/null +++ b/FileSets/v3.52~1/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 1.1 +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.51/PowerGauge.qml.orig b/FileSets/v3.52~1/PowerGauge.qml.orig similarity index 100% rename from FileSets/v3.51/PowerGauge.qml.orig rename to FileSets/v3.52~1/PowerGauge.qml.orig diff --git a/FileSets/v3.52~1/TileDigIn.qml b/FileSets/v3.52~1/TileDigIn.qml new file mode 100644 index 00000000..492b8b2d --- /dev/null +++ b/FileSets/v3.52~1/TileDigIn.qml @@ -0,0 +1,133 @@ +// 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.51/TileDigIn.qml.orig b/FileSets/v3.52~1/TileDigIn.qml.orig similarity index 100% rename from FileSets/v3.51/TileDigIn.qml.orig rename to FileSets/v3.52~1/TileDigIn.qml.orig diff --git a/FileSets/v3.52~1/TileRelay.qml b/FileSets/v3.52~1/TileRelay.qml new file mode 100644 index 00000000..ab9dd82d --- /dev/null +++ b/FileSets/v3.52~1/TileRelay.qml @@ -0,0 +1,498 @@ +// New for GuiMods to display and control relays on separate overview page + +import QtQuick 1.1 +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.51/TileRelay.qml.orig b/FileSets/v3.52~1/TileRelay.qml.orig similarity index 100% rename from FileSets/v3.51/TileRelay.qml.orig rename to FileSets/v3.52~1/TileRelay.qml.orig diff --git a/FileSets/v3.52~1/dbus_digitalinputs.py b/FileSets/v3.52~1/dbus_digitalinputs.py new file mode 100644 index 00000000..b8864152 --- /dev/null +++ b/FileSets/v3.52~1/dbus_digitalinputs.py @@ -0,0 +1,744 @@ +#!/usr/bin/python3 -u + +#### modified for ExtTransferSwitch package + +import sys, os +import signal +from threading import Thread +from select import select, epoll, EPOLLPRI +from functools import partial +from collections import namedtuple +from argparse import ArgumentParser +import traceback +sys.path.insert(1, os.path.join(os.path.dirname(__file__), 'ext', 'velib_python')) + +from dbus.mainloop.glib import DBusGMainLoop +import dbus +from gi.repository import GLib +from vedbus import VeDbusService, VeDbusItemImport +from settingsdevice import SettingsDevice + +VERSION = '0.27' +MAXCOUNT = 2**31-1 +SAVEINTERVAL = 60000 + +INPUT_FUNCTION_COUNTER = 1 +INPUT_FUNCTION_INPUT = 2 + +Translation = namedtuple('Translation', ['no', 'yes']) + +# Only append at the end +INPUTTYPES = [ + 'Disabled', + 'Pulse meter', + 'Door', + 'Bilge pump', + 'Bilge alarm', + 'Burglar alarm', + 'Smoke alarm', + 'Fire alarm', + 'CO2 alarm', + 'Generator', + 'Generic I/O', + 'Touch enable', +#### added for ExtTransferSwitch package -- must be LAST in the list + 'Transfer switch' +] + +# Translations. The text will be used only for GetText, it will be translated +# in the gui. +TRANSLATIONS = [ + Translation('low', 'high'), + Translation('off', 'on'), + Translation('no', 'yes'), + Translation('open', 'closed'), + Translation('ok', 'alarm'), + Translation('running', 'stopped'), +#### added for ExtTransferSwitch package + Translation('on generator', 'on grid') +] + +class SystemBus(dbus.bus.BusConnection): + def __new__(cls): + return dbus.bus.BusConnection.__new__(cls, dbus.bus.BusConnection.TYPE_SYSTEM) + +class SessionBus(dbus.bus.BusConnection): + def __new__(cls): + return dbus.bus.BusConnection.__new__(cls, dbus.bus.BusConnection.TYPE_SESSION) + +class InputPin(): + devid = None + devinstance = None + + def __init__(self, name=None, path=None, label=None): + self.name = name + self.path = path + self.label = label + +class BasePulseCounter(object): + pass + +class DebugPulseCounter(BasePulseCounter): + def __init__(self): + self.gpiomap = {} + + def register(self, path, gpio): + self.gpiomap[gpio] = None + return 0 + + def unregister(self, gpio): + del self.gpiomap[gpio] + + def registered(self, gpio): + return gpio in self.gpiomap + + def __call__(self): + from itertools import cycle + from time import sleep + for level in cycle([0, 1]): + for gpio in list(self.gpiomap.keys()): + yield gpio, level + sleep(0.25/len(self.gpiomap)) + +class EpollPulseCounter(BasePulseCounter): + def __init__(self): + self.gpiomap = {} + self.states = {} + self.ob = epoll() + + def register(self, path, gpio): + path = os.path.realpath(path) + + # Set up gpio for rising edge interrupts + try: + with open(os.path.join(path, 'edge'), 'ab') as fp: + fp.write(b'both') + except: + pass + + fp = open(os.path.join(path, 'value'), 'rb') + level = int(fp.read()) # flush it in case it's high at startup + self.gpiomap[gpio] = fp + self.states[gpio] = level + self.ob.register(fp, EPOLLPRI) + return level + + def unregister(self, gpio): + fp = self.gpiomap[gpio] + self.ob.unregister(fp) + del self.gpiomap[gpio] + del self.states[gpio] + fp.close() + + def registered(self, gpio): + return gpio in self.gpiomap + + def __call__(self): + while True: + # We have a timeout of 1 second on the poll, because poll() only + # looks at files in the epoll object at the time poll() was called. + # The timeout means we let other files (added via calls to + # register/unregister) into the loop at least that often. + self.ob.poll(1) + + # When coming out of the epoll call, we read all the gpios to make + # sure we didn't miss any edges. This is a safety fallback that + # ensures everything is up to date once a second, but + # edge-triggered results are handled immediately. + # NOTE: There has not been a report of a missed interrupt yet. + # Belts and suspenders. + for gpio, fp in list(self.gpiomap.items()): + os.lseek(fp.fileno(), 0, os.SEEK_SET) + v = int(os.read(fp.fileno(), 1)) + if v != self.states[gpio]: + self.states[gpio] = v + yield gpio, v + +class PollingPulseCounter(BasePulseCounter): + def __init__(self): + self.gpiomap = {} + + def register(self, path, gpio): + path = os.path.realpath(path) + + fp = open(os.path.join(path, 'value'), 'rb') + level = int(fp.read()) + self.gpiomap[gpio] = [fp, level] + return level + + def unregister(self, gpio): + del self.gpiomap[gpio] + + def registered(self, gpio): + return gpio in self.gpiomap + + def __call__(self): + from itertools import cycle + from time import sleep + while True: + for gpio, (fp, level) in list(self.gpiomap.items()): + fp.seek(0, os.SEEK_SET) + v = int(fp.read()) + if v != level: + self.gpiomap[gpio][1] = v + yield gpio, v + sleep(1) + +class HandlerMaker(type): + """ Meta-class for keeping track of all extended classes. """ + def __init__(cls, name, bases, attrs): + if not hasattr(cls, 'handlers'): + cls.handlers = {} + else: + cls.handlers[cls.type_id] = cls + +class PinHandler(object, metaclass=HandlerMaker): + product_id = 0xFFFF + _product_name = 'Generic GPIO' + dbus_name = "digital" + def __init__(self, bus, base, path, gpio, settings): + self.bus = bus + self.settings = settings + self._level = 0 # Remember last state + + instance = int(settings['instance'].split(':')[1]) + + name = str(gpio) + if name[0].isdecimal(): + name = 'input_' + name + + self.service = VeDbusService( + "{}.{}.{}".format(base, self.dbus_name, name), bus=bus, + register=False) + + # Add objects required by ve-api + self.service.add_path('/Mgmt/ProcessName', __file__) + self.service.add_path('/Mgmt/ProcessVersion', VERSION) + self.service.add_path('/Mgmt/Connection', path) + self.service.add_path('/DeviceInstance', instance) + self.service.add_path('/ProductId', self.product_id) + self.service.add_path('/ProductName', self.product_name) + self.service.add_path('/Connected', 1) + + # Custom name setting + def _change_name(p, v): + # This should fire a change event that will update product_name + # below. + settings['name'] = v + return True + + self.service.add_path('/CustomName', settings['name'], writeable=True, + onchangecallback=_change_name) + + # We'll count the pulses for all types of services + self.service.add_path('/Count', value=settings['count']) + + # Register our name on dbus + self.service.register() + + @property + def product_name(self): + return self.settings['name'] or self._product_name + + @product_name.setter + def product_name(self, v): + # Some pin types don't have an associated service (Disabled pins for + # example) + if self.service is not None: + self.service['/ProductName'] = v or self._product_name + + def deactivate(self): + self.save_count() + self.service.__del__() + del self.service + self.service = None + + @property + def level(self): + return self._level + + @level.setter + def level(self, l): + self._level = int(bool(l)) + + def toggle(self, level): + raise NotImplementedError + + def _toggle(self, level, service): + # Only increment Count on rising edge. + if level and level != self._level: + service['/Count'] = (service['/Count']+1) % MAXCOUNT + self._level = level + + def refresh(self): + """ Toggle state to last remembered state. This is called if settings + are changed so the Service can recalculate paths. """ + self.toggle(self._level) + + def save_count(self): + if self.service is not None: + self.settings['count'] = self.count + + @property + def active(self): + return self.service is not None + + @property + def count(self): + return self.service['/Count'] + + @count.setter + def count(self, v): + self.service['/Count'] = v + + @classmethod + def createHandler(cls, _type, *args, **kwargs): + if _type in cls.handlers: + return cls.handlers[_type](*args, **kwargs) + return None + + +class NopPin(object): + """ Mixin for a pin with empty behaviour. Mix in BEFORE PinHandler so that + __init__ overrides the base behaviour. """ + def __init__(self, bus, base, path, gpio, settings): + self.service = None + self.bus = bus + self.settings = settings + self._level = 0 # Remember last state + + def deactivate(self): + pass + + def toggle(self, level): + self._level = level + + def save_count(self): + # Do nothing + pass + + @property + def count(self): + return self.settings['count'] + + @count.setter + def count(self, v): + pass + + def refresh(self): + pass + + +class DisabledPin(NopPin, PinHandler): + """ Place holder for a disabled pin. """ + _product_name = 'Disabled' + type_id = 0 + + +class VolumeCounter(PinHandler): + product_id = 0xA165 + _product_name = "Generic pulse meter" + dbus_name = "pulsemeter" + type_id = 1 + + def __init__(self, bus, base, path, gpio, settings): + super(VolumeCounter, self).__init__(bus, base, path, gpio, settings) + self.service.add_path('/Aggregate', value=self.count*self.rate, + gettextcallback=lambda p, v: (str(v) + ' cubic meter')) + + @property + def rate(self): + return self.settings['rate'] + + def toggle(self, level): + with self.service as s: + super(VolumeCounter, self)._toggle(level, s) + s['/Aggregate'] = self.count * self.rate + +class TouchEnable(NopPin, PinHandler): + """ The pin is used to enable/disable the Touch screen when toggled. + No dbus-service is created. """ + _product_name = 'TouchEnable' + type_id = 11 + + def __init__(self, *args, **kwargs): + super(TouchEnable, self).__init__(*args, **kwargs) + self.item = VeDbusItemImport(self.bus, + "com.victronenergy.settings", "/Settings/Gui/TouchEnabled") + + def toggle(self, level): + super(TouchEnable, self).toggle(level) + + # Toggle the touch-enable setting on the downward edge. + # Level is expected to be high with the switch open, and + # pulled low when pushed. + if level == 0: + enabled = bool(self.item.get_value()) + self.item.set_value(int(not enabled)) + + def deactivate(self): + # Always re-enable touch when the pin is deactivated. + # This adds another layer of protection against accidental + # lockout. + self.item.set_value(1) + del self.item + +class PinAlarm(PinHandler): + product_id = 0xA166 + _product_name = "Generic digital input" + dbus_name = "digitalinput" + type_id = 0xFF + translation = 0 # low, high + + def __init__(self, bus, base, path, gpio, settings): + super(PinAlarm, self).__init__(bus, base, path, gpio, settings) + self.service.add_path('/InputState', value=0) + self.service.add_path('/State', value=self.get_state(0), + gettextcallback=lambda p, v: TRANSLATIONS[v//2][v%2]) + self.service.add_path('/Alarm', value=self.get_alarm_state(0)) + + # Also expose the type + self.service.add_path('/Type', value=self.type_id, + gettextcallback=lambda p, v: INPUTTYPES[v]) + + def toggle(self, level): + with self.service as s: + super(PinAlarm, self)._toggle(level, s) + s['/InputState'] = bool(level)*1 + s['/State'] = self.get_state(level) + # Ensure that the alarm flag resets if the /AlarmSetting config option + # disappears. + s['/Alarm'] = self.get_alarm_state(level) + + def get_state(self, level): + state = level ^ self.settings['invert'] + return 2 * self.translation + state + + def get_alarm_state(self, level): + return 2 * bool( + (level ^ self.settings['invertalarm']) and self.settings['alarm']) + + +class Generator(PinAlarm): + _product_name = "Generator" + type_id = 9 + translation = 5 # running, stopped + startStopService = 'com.victronenergy.generator.startstop0' + + def __init__(self, bus, base, path, gpio, settings): + super(Generator, self).__init__(bus, base, path, gpio, settings) + self._gpio = gpio + # Periodically rewrite the generator selection. The Multi may reset + # causing this to be lost, or a race condition on startup may cause + # it to not be set properly. + self._timer = GLib.timeout_add(30000, + lambda: self.select_generator(self.level ^ self.settings['invert'] ^ 1) or True) + +#### added for ExtTransferSwitch package + self.mainVeBusServiceItem = None + + + def select_generator(self, v): + # Find all vebus services, and let them know + try: + services = [n for n in self.bus.list_names() if n.startswith( + 'com.victronenergy.vebus.')] + for n in services: +#### added for ExtTransferSwitch package + # skip this service if it is the main VE.Bus device + # processing for that is handled in ExtTransferSwitch + try: + if self.mainVeBusServiceItem == None: + self.mainVeBusServiceItem = VeDbusItemImport(self.bus, + "com.victronenergy.service", "/VebusService") + if n == self.mainVeBusService.get_value (): + continue + except: + pass +#### end added for ExtTransferSwitch package + + self.bus.call_async(n, '/Ac/Control/RemoteGeneratorSelected', 'com.victronenergy.BusItem', + 'SetValue', 'v', [v], None, None) + except dbus.exceptions.DBusException: + print ("DBus exception setting RemoteGeneratorSelected") + traceback.print_exc() + try: + self.bus.call_async(self.startStopService, '/DigitalInput/Input', 'com.victronenergy.BusItem', + 'SetValue', 'v', [self._gpio], None, None) + self.bus.call_async(self.startStopService, '/DigitalInput/Running', 'com.victronenergy.BusItem', + 'SetValue', 'v', [v], None, None) + except dbus.exceptions.DBusException: + print ("DBus exception setting RemoteGeneratorSelected") + traceback.print_exc() + + def toggle(self, level): + super(Generator, self).toggle(level) + + # Follow the same inversion sense as for display + self.select_generator(level ^ self.settings['invert'] ^ 1) + + def deactivate(self): + super(Generator, self).deactivate() + # When deactivating, reset the generator selection state + self.select_generator(0) + try: + self.bus.call_async(self.startStopService, '/DigitalInput/Input', 'com.victronenergy.BusItem', + 'SetValue', 'v', [0], None, None) + except dbus.exceptions.DBusException: + pass + # And kill the periodic job + GLib.source_remove(self._timer) + self._timer = None + +# Various types of things we might want to monitor +class DoorSensor(PinAlarm): + _product_name = "Door alarm" + type_id = 2 + translation = 3 # open, closed + +class BilgePump(PinAlarm): + _product_name = "Bilge pump" + type_id = 3 + translation = 1 # off, on + +class BilgeAlarm(PinAlarm): + _product_name = "Bilge alarm" + type_id = 4 + translation = 4 # ok, alarm + +class BurglarAlarm(PinAlarm): + _product_name = "Burglar alarm" + type_id = 5 + translation = 4 # ok, alarm + +class SmokeAlarm(PinAlarm): + _product_name = "Smoke alarm" + type_id = 6 + translation = 4 # ok, alarm + +class FireAlarm(PinAlarm): + _product_name = "Fire alarm" + type_id = 7 + translation = 4 # ok, alarm + +class CO2Alarm(PinAlarm): + _product_name = "CO2 alarm" + type_id = 8 + translation = 4 # ok, alarm + +class GenericIO(PinAlarm): + _product_name = "Generic I/O" + type_id = 10 + translation = 0 # low, high + +#### added for ExtTransferSwitch package +class TransferSwitch(PinAlarm): + _product_name = "External AC Input transfer switch" + type_id = 12 + translation = 6 # Grid In / Generator In + + +def dbusconnection(): + return SessionBus() if 'DBUS_SESSION_BUS_ADDRESS' in os.environ else SystemBus() + +def parse_config(conf): + f = open(conf) + + tag = None + pins = [] + + for line in f: + cmd, arg = line.strip().split(maxsplit=1) + + if cmd == 'tag': + tag = arg + continue + + if cmd == 'input': + pth, label = arg.split(maxsplit=1) + label = label.strip('"') + pin = InputPin(tag + '_' + os.path.basename(pth), pth, label) + pins.append(pin) + continue + + f.close() + + return pins + +def main(): + parser = ArgumentParser(description=sys.argv[0]) + parser.add_argument('--servicebase', + help='Base service name on dbus, default is com.victronenergy', + default='com.victronenergy') + parser.add_argument('--poll', + help='Use a different kind of polling. Options are epoll, dumb and debug', + default='epoll') + parser.add_argument('--conf', action='append', default=[], help='Config file') + parser.add_argument('inputs', nargs='*', help='Path to digital input') + args = parser.parse_args() + + PulseCounter = { + 'debug': DebugPulseCounter, + 'poll': PollingPulseCounter, + }.get(args.poll, EpollPulseCounter) + + DBusGMainLoop(set_as_default=True) + + ctlbus = dbusconnection() + ctlsvc = VeDbusService(args.servicebase + '.digitalinputs', bus=ctlbus, register=True) + + # Keep track of enabled services + services = {} + inputs = dict(enumerate(args.inputs, 1)) + pulses = PulseCounter() # callable that iterates over pulses + + def register_gpio(path, gpio, bus, settings): + _type = settings['inputtype'] + print ("Registering GPIO {} for type {}".format(gpio, _type)) + + handler = PinHandler.createHandler(_type, + bus, args.servicebase, path, gpio, settings) + services[gpio] = handler + + # Only monitor if enabled + if _type > 0: + handler.level = pulses.register(path, gpio) + handler.refresh() + + def unregister_gpio(gpio): + print ("unRegistering GPIO {}".format(gpio)) + if pulses.registered(gpio): + pulses.unregister(gpio) + services[gpio].deactivate() + + def handle_setting_change(pin, setting, old, new): + # This handler may also be called if some attribute of a setting + # is changed, but not the value. Bail if the value is unchanged. + if old == new: + return + + inp = pin.name + + if setting == 'inputtype': + if new: + # Get current bus and settings objects, to be reused + service = services[inp] + bus, settings = service.bus, service.settings + + # Input enabled. If already enabled, unregister the old one first. + if pulses.registered(inp): + unregister_gpio(inp) + + # We only want 1 generator input at a time, so disable other inputs configured as generator. + for i in inputs: + if i != inp and services[i].settings['inputtype'] == 9 == new: + services[i].settings['inputtype'] = 0 + unregister_gpio(i) + + # Before registering the new input, reset its settings to defaults + settings['count'] = 0 + settings['invert'] = 0 + settings['invertalarm'] = 0 + settings['alarm'] = 0 + + # Register it + register_gpio(pin.path, inp, bus, settings) + elif old: + # Input disabled + unregister_gpio(inp) + + ctlsvc['/Devices/{}/Type'.format(inp)] = new + elif setting in ('rate', 'invert', 'alarm', 'invertalarm'): + services[inp].refresh() + elif setting == 'name': + services[inp].product_name = new + elif setting == 'count': + # Don't want this triggered on a period save, so only execute + # if it has changed. + v = int(new) + s = services[inp] + if s.active and s.count != v: + s.count = v + s.refresh() + + def change_type(sd, path, val): + if not 0 <= val < len(INPUTTYPES): + return False + sd['inputtype'] = val + return True + + pins = [] + + for inp, pth in inputs.items(): + pin = InputPin(inp, pth, 'Digital input {}'.format(inp)) + pin.devid = os.path.basename(pth) + pin.devinstance = inp + pins.append(pin) + + for conf in args.conf: + pins += parse_config(conf) + + for pin in pins: + inp = pin.name + devid = pin.devid or pin.name + inst = 'digitalinput:{}'.format(pin.devinstance or 10) + supported_settings = { + 'inputtype': ['/Settings/DigitalInput/{}/Type'.format(inp), 0, 0, len(INPUTTYPES)-1], + 'rate': ['/Settings/DigitalInput/{}/Multiplier'.format(inp), 0.001, 0, 1.0], + 'count': ['/Settings/DigitalInput/{}/Count'.format(inp), 0, 0, MAXCOUNT, 1], + 'invert': ['/Settings/DigitalInput/{}/InvertTranslation'.format(inp), 0, 0, 1], + 'invertalarm': ['/Settings/DigitalInput/{}/InvertAlarm'.format(inp), 0, 0, 1], + 'alarm': ['/Settings/DigitalInput/{}/AlarmSetting'.format(inp), 0, 0, 1], + 'name': ['/Settings/DigitalInput/{}/CustomName'.format(inp), '', '', ''], + 'instance': ['/Settings/Devices/{}/ClassAndVrmInstance'.format(devid), inst, '', ''], + } + bus = dbusconnection() + sd = SettingsDevice(bus, supported_settings, partial(handle_setting_change, pin), timeout=10) + register_gpio(pin.path, inp, bus, sd) + ctlsvc.add_path('/Devices/{}/Label'.format(inp), pin.label) + ctlsvc.add_path('/Devices/{}/Type'.format(inp), sd['inputtype'], + writeable=True, onchangecallback=partial(change_type, sd)) + + def poll(mainloop): + from time import time + idx = 0 + + try: + for inp, level in pulses(): + # epoll object only resyncs once a second. We may receive + # a pulse for something that's been deregistered. + try: + services[inp].toggle(level) + except KeyError: + continue + except: + traceback.print_exc() + mainloop.quit() + + # Need to run the gpio polling in separate thread. Pass in the mainloop so + # the thread can kill us if there is an exception. + mainloop = GLib.MainLoop() + + poller = Thread(target=lambda: poll(mainloop)) + poller.daemon = True + poller.start() + + # Periodically save the counter + def save_counters(): + for svc in services.values(): + svc.save_count() + return True + GLib.timeout_add(SAVEINTERVAL, save_counters) + + # Save counter on shutdown + signal.signal(signal.SIGTERM, lambda *args: sys.exit(0)) + + try: + mainloop.run() + except KeyboardInterrupt: + pass + finally: + save_counters() + +if __name__ == "__main__": + main() diff --git a/FileSets/v3.51/dbus_digitalinputs.py.orig b/FileSets/v3.52~1/dbus_digitalinputs.py.orig similarity index 100% rename from FileSets/v3.51/dbus_digitalinputs.py.orig rename to FileSets/v3.52~1/dbus_digitalinputs.py.orig diff --git a/FileSets/v3.52~1/startstop.py b/FileSets/v3.52~1/startstop.py new file mode 100644 index 00000000..244de097 --- /dev/null +++ b/FileSets/v3.52~1/startstop.py @@ -0,0 +1,1482 @@ +#!/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 vebus_service(self): + return self.parent._vebusservice if self.parent._vebusservice else '' + + @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.vebus_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.vebus_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.vebus_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.vebus_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.vebus_service, ('/Alarms/%s/Overload' % phase))) + return safe_max(inverterOverload) + return v + +class StopOnAc1Condition(Condition): + name = 'stoponac1' + monitoring = 'vebus' + boolean = True + timed = False + + def get_value(self): + # AC input 1 + available = self.monitor.get_value(self.vebus_service, + '/Ac/State/AcIn1Available') + if available is None: + # Not supported in firmware, fall back to old behaviour + activein = self.monitor.get_value(self.vebus_service, + '/Ac/ActiveIn/ActiveInput') + + # Active input is connected + connected = self.monitor.get_value(self.vebus_service, + '/Ac/ActiveIn/Connected') + if None not in (activein, connected): + return activein == 0 and connected == 1 + return None + + return bool(available) + +class StopOnAc2Condition(Condition): + name = 'stoponac2' + monitoring = 'vebus' + boolean = True + timed = False + + def get_value(self): + # AC input 2 available (used when grid is on AC-in-2) + available = self.monitor.get_value(self.vebus_service, + '/Ac/State/AcIn2Available') + + return None if available is None else bool(available) + +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._errorstate = 0 + self._battery_service = None + self._battery_prefix = None + + self._acpower_inverter_input = { + '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) + }) + + 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/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._vebusservice is not None and \ + dbusServiceName == self._vebusservice and \ + dbusPath == '/Ac/State/AcIn1Available': + 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_acinput() + 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_acinput(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_acpower_inverter_input() + return + + vebus_service = self._vebusservice if self._vebusservice else '' + activein_state = self._dbusmonitor.get_value( + vebus_service, '/Ac/ActiveIn/Connected') + + # 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._acpower_inverter_input['unabletostart']: + self.log_info('Generator detected at inverter AC input, alarm removed') + self._reset_acpower_inverter_input() +#### GuiMods warm-up / cool-down + elif not processAlarm: + self._reset_acpower_inverter_input() + return + elif self._acpower_inverter_input['timeout'] < self.RETRIES_ON_ERROR: + self._acpower_inverter_input['timeout'] += 1 + elif not self._acpower_inverter_input['unabletostart']: + self._acpower_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_acpower_inverter_input(self, clear_error=True): + 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. + readout_supported = self._dbusmonitor.get_value(self._vebusservice, + '/Ac/State/AcIn1Available') is not None + self._dbusservice['/Capabilities'] |= ( + Capabilities.WarmupCooldown if readout_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': + 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) + elif batterymeasurement == 'default': + newbatteryservice = 'default' + + 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._vebusservice != vebusservice: + self._vebusservice = vebusservice + self._set_capabilities() + self.log_info('Vebus service (%s) found! Using it for generator start/stop' % vebusservice) + else: + if self._vebusservice is not None: + self.log_info('Vebus service (%s) dissapeared! Stop evaluating related conditions' % self._vebusservice) + else: + self.log_info('Error getting Vebus service!') + self._vebusservice = 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._vebusservice 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._vebusservice, '/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._vebusservice, '/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/v3.51/startstop.py.orig b/FileSets/v3.52~1/startstop.py.orig similarity index 100% rename from FileSets/v3.51/startstop.py.orig rename to FileSets/v3.52~1/startstop.py.orig diff --git a/changes b/changes index b7173a0d..e2350bea 100644 --- a/changes +++ b/changes @@ -1,3 +1,6 @@ +v10.76: + add support for v3.52~1 + v10.75: added support for v3.51 diff --git a/version b/version index 646dce79..fd305bae 100644 --- a/version +++ b/version @@ -1 +1 @@ -v10.75 +v10.76