diff --git a/FileSets/PatchSource/HubData.qml b/FileSets/PatchSource/HubData.qml index 6c0413dc..5d61f363 100644 --- a/FileSets/PatchSource/HubData.qml +++ b/FileSets/PatchSource/HubData.qml @@ -1,11 +1,7 @@ -//////// modified for VE.Direct inverter support -//////// modified for grid/genset meter -//////// added alternator, AC charger, wind generator - import QtQuick 1.1 -import com.victron.velib 1.0 import "utils.js" as Utils +import com.victron.velib 1.0 Item { id: root diff --git a/FileSets/PatchSource/HubData.qml.patch b/FileSets/PatchSource/HubData.qml.patch index 86c5d113..6c3fb54f 100644 --- a/FileSets/PatchSource/HubData.qml.patch +++ b/FileSets/PatchSource/HubData.qml.patch @@ -1,14 +1,10 @@ --- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/HubData.qml.orig 2025-01-12 08:25:03 -+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/HubData.qml 2024-05-17 06:58:33 -@@ -1,14 +1,32 @@ -+//////// modified for VE.Direct inverter support -+//////// modified for grid/genset meter -+//////// added alternator, AC charger, wind generator -+ ++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/HubData.qml 2025-02-02 21:49:14 +@@ -1,14 +1,28 @@ import QtQuick 1.1 -+import com.victron.velib 1.0 import "utils.js" as Utils ++import com.victron.velib 1.0 Item { id: root @@ -33,7 +29,7 @@ property alias pvCharger: _pvCharger property alias pvOnAcIn1: _pvOnAcIn1 property alias pvOnAcIn2: _pvOnAcIn2 -@@ -18,8 +36,8 @@ +@@ -18,8 +32,8 @@ property alias acInLoad: _acInLoad property alias acOutLoad: _acOutLoad property alias grid: _grid @@ -43,7 +39,7 @@ property VBusItem systemType: VBusItem { bind: Utils.path(systemPrefix, "/SystemType") } property bool hasGridMeter: _hasGridMeter.valid property variant acSource: _acSource.value -@@ -60,6 +78,30 @@ +@@ -60,6 +74,30 @@ property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Pv/Power"); unit: "W"} } @@ -74,7 +70,7 @@ ObjectAcConnection { id: _pvOnAcOut bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnOutput") -@@ -77,8 +119,11 @@ +@@ -77,8 +115,11 @@ ObjectAcConnection { id: _genset @@ -87,7 +83,7 @@ } VBusItem { -@@ -91,20 +136,20 @@ +@@ -91,20 +132,20 @@ bind: Utils.path(systemPrefix, "/Ac/Grid/DeviceType") } @@ -121,7 +117,7 @@ VBusItem { id: _l2L1OutSummed -@@ -114,32 +159,42 @@ +@@ -114,32 +155,42 @@ ObjectAcConnection { id: _grid @@ -172,7 +168,7 @@ bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnInput") } -@@ -167,4 +222,47 @@ +@@ -167,4 +218,47 @@ id: _dcSystem property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/System/Power"); unit: "W"} } diff --git a/FileSets/PatchSource/MbItemDigitalInput.qml-v3.40 b/FileSets/PatchSource/MbItemDigitalInput.qml-v3.40 index 599ed90a..412ef280 100644 --- a/FileSets/PatchSource/MbItemDigitalInput.qml-v3.40 +++ b/FileSets/PatchSource/MbItemDigitalInput.qml-v3.40 @@ -1,5 +1,3 @@ -//// modified for ExtTransferSwitch package - import QtQuick 1.1 MbItemOptions { diff --git a/FileSets/PatchSource/MbItemDigitalInput.qml-v3.40.patch b/FileSets/PatchSource/MbItemDigitalInput.qml-v3.40.patch index cbfd2dc7..6c557615 100644 --- a/FileSets/PatchSource/MbItemDigitalInput.qml-v3.40.patch +++ b/FileSets/PatchSource/MbItemDigitalInput.qml-v3.40.patch @@ -1,12 +1,6 @@ --- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/MbItemDigitalInput.qml-v3.40.orig 2024-07-09 10:19:45 -+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/MbItemDigitalInput.qml-v3.40 2024-05-14 07:00:12 -@@ -1,3 +1,5 @@ -+//// modified for ExtTransferSwitch package -+ - import QtQuick 1.1 - - MbItemOptions { -@@ -14,8 +16,11 @@ ++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/MbItemDigitalInput.qml-v3.40 2025-02-02 17:42:41 +@@ -14,8 +14,11 @@ MbOption { description: qsTr("Smoke alarm"); value: 6 }, MbOption { description: qsTr("Fire alarm"); value: 7 }, MbOption { description: qsTr("CO2 alarm"); value: 8 }, diff --git a/FileSets/PatchSource/ObjectAcConnection.qml b/FileSets/PatchSource/ObjectAcConnection.qml index 9e87e2db..351e5b28 100644 --- a/FileSets/PatchSource/ObjectAcConnection.qml +++ b/FileSets/PatchSource/ObjectAcConnection.qml @@ -1,7 +1,3 @@ -////// modified to show voltage, current and frequency in flow overview -////// modified to show bar graphs -////// modified to use grid or genset meter if present - import QtQuick 1.1 import com.victron.velib 1.0 import "utils.js" as Utils diff --git a/FileSets/PatchSource/ObjectAcConnection.qml.patch b/FileSets/PatchSource/ObjectAcConnection.qml.patch index b78f55c1..60be74d9 100644 --- a/FileSets/PatchSource/ObjectAcConnection.qml.patch +++ b/FileSets/PatchSource/ObjectAcConnection.qml.patch @@ -1,13 +1,6 @@ --- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/ObjectAcConnection.qml.orig 2025-01-12 08:28:05 -+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/ObjectAcConnection.qml 2024-05-14 07:00:12 -@@ -1,19 +1,41 @@ -+////// modified to show voltage, current and frequency in flow overview -+////// modified to show bar graphs -+////// modified to use grid or genset meter if present -+ - import QtQuick 1.1 - import com.victron.velib 1.0 - import "utils.js" as Utils ++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/ObjectAcConnection.qml 2025-02-02 17:43:09 +@@ -4,16 +4,34 @@ QtObject { property string bindPrefix diff --git a/FileSets/PatchSource/PageDigitalInput.qml b/FileSets/PatchSource/PageDigitalInput.qml index 3a9483ca..14bf985e 100644 --- a/FileSets/PatchSource/PageDigitalInput.qml +++ b/FileSets/PatchSource/PageDigitalInput.qml @@ -1,5 +1,3 @@ -//// modified for ExtTransferSwitch package - import QtQuick 1.1 import com.victron.velib 1.0 import "utils.js" as Utils diff --git a/FileSets/PatchSource/PageDigitalInput.qml.patch b/FileSets/PatchSource/PageDigitalInput.qml.patch index d0a89525..150b7bc3 100644 --- a/FileSets/PatchSource/PageDigitalInput.qml.patch +++ b/FileSets/PatchSource/PageDigitalInput.qml.patch @@ -1,12 +1,6 @@ --- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageDigitalInput.qml.orig 2024-07-09 10:19:57 -+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageDigitalInput.qml 2024-05-14 07:00:12 -@@ -1,3 +1,5 @@ -+//// modified for ExtTransferSwitch package -+ - import QtQuick 1.1 - import com.victron.velib 1.0 - import "utils.js" as Utils -@@ -18,6 +20,20 @@ ++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageDigitalInput.qml 2025-02-02 17:43:22 +@@ -18,6 +18,20 @@ bind: service.path("/DeviceInstance") } @@ -27,7 +21,7 @@ // Handle translations function getType(type){ switch (type) { -@@ -41,6 +57,9 @@ +@@ -41,6 +55,9 @@ return qsTr("CO2 alarm") case "Generator": return qsTr("Generator") @@ -37,7 +31,7 @@ } return type; } -@@ -72,6 +91,11 @@ +@@ -72,6 +89,11 @@ return qsTr("Running") case 11: return qsTr("Stopped") @@ -49,7 +43,7 @@ } return qsTr("Unknown") -@@ -105,5 +129,19 @@ +@@ -105,5 +127,19 @@ } } } diff --git a/FileSets/PatchSource/PageMain.qml-v3.22 b/FileSets/PatchSource/PageMain.qml-v3.22 index 454c2498..78fc4236 100644 --- a/FileSets/PatchSource/PageMain.qml-v3.22 +++ b/FileSets/PatchSource/PageMain.qml-v3.22 @@ -1,5 +1,3 @@ -//////// GuiMods modified order to put Settings, then Notifications at top of list - import QtQuick 1.1 import "utils.js" as Utils import com.victron.velib 1.0 diff --git a/FileSets/PatchSource/PageMain.qml-v3.22.patch b/FileSets/PatchSource/PageMain.qml-v3.22.patch index cbdceadf..a800e562 100644 --- a/FileSets/PatchSource/PageMain.qml-v3.22.patch +++ b/FileSets/PatchSource/PageMain.qml-v3.22.patch @@ -1,8 +1,6 @@ --- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageMain.qml-v3.22.orig 2024-07-08 09:45:00 -+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageMain.qml-v3.22 2024-07-09 09:53:20 -@@ -1,11 +1,52 @@ -+//////// GuiMods modified order to put Settings, then Notifications at top of list -+ ++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageMain.qml-v3.22 2025-02-02 17:47:12 +@@ -1,11 +1,50 @@ import QtQuick 1.1 +import "utils.js" as Utils import com.victron.velib 1.0 @@ -53,7 +51,7 @@ VisualDataModel { model: VeSortFilterProxyModel { model: DeviceList { -@@ -27,7 +68,7 @@ +@@ -27,7 +66,7 @@ subpage: model.page } } @@ -62,7 +60,7 @@ MbSubMenu { id: menuNotifications description: qsTr("Notifications") -@@ -37,17 +78,22 @@ +@@ -37,17 +76,22 @@ value: active.length > 0 ? active.length : "" } subpage: Component { PageNotifications {} } diff --git a/FileSets/PatchSource/PageMain.qml-v3.34 b/FileSets/PatchSource/PageMain.qml-v3.34 index 34850138..dc24808f 100644 --- a/FileSets/PatchSource/PageMain.qml-v3.34 +++ b/FileSets/PatchSource/PageMain.qml-v3.34 @@ -1,5 +1,3 @@ -//////// GuiMods modified order to put Settings, then Notifications at top of list - import QtQuick 1.1 import "utils.js" as Utils import com.victron.velib 1.0 diff --git a/FileSets/PatchSource/PageMain.qml-v3.34.patch b/FileSets/PatchSource/PageMain.qml-v3.34.patch index 10ff3252..95e1ea04 100644 --- a/FileSets/PatchSource/PageMain.qml-v3.34.patch +++ b/FileSets/PatchSource/PageMain.qml-v3.34.patch @@ -1,8 +1,6 @@ --- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageMain.qml-v3.34.orig 2024-07-08 09:45:00 -+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageMain.qml-v3.34 2024-07-09 09:53:47 -@@ -1,11 +1,48 @@ -+//////// GuiMods modified order to put Settings, then Notifications at top of list -+ ++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageMain.qml-v3.34 2025-02-02 17:47:35 +@@ -1,11 +1,46 @@ import QtQuick 1.1 +import "utils.js" as Utils import com.victron.velib 1.0 @@ -49,7 +47,7 @@ VisualDataModel { model: VeSortFilterProxyModel { model: DeviceList { -@@ -27,23 +64,28 @@ +@@ -27,23 +62,28 @@ subpage: model.page } } diff --git a/FileSets/PatchSource/PageMain.qml-v3.40 b/FileSets/PatchSource/PageMain.qml-v3.40 index 9600c264..44d11166 100644 --- a/FileSets/PatchSource/PageMain.qml-v3.40 +++ b/FileSets/PatchSource/PageMain.qml-v3.40 @@ -1,5 +1,3 @@ -//////// GuiMods modified order to put Settings, then Notifications at top of list - import QtQuick 1.1 import "utils.js" as Utils import com.victron.velib 1.0 diff --git a/FileSets/PatchSource/PageMain.qml-v3.40.patch b/FileSets/PatchSource/PageMain.qml-v3.40.patch index 8357d94c..8dee0347 100644 --- a/FileSets/PatchSource/PageMain.qml-v3.40.patch +++ b/FileSets/PatchSource/PageMain.qml-v3.40.patch @@ -1,8 +1,6 @@ --- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageMain.qml-v3.40.orig 2024-07-08 09:45:00 -+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageMain.qml-v3.40 2024-07-09 09:55:28 -@@ -1,11 +1,48 @@ -+//////// GuiMods modified order to put Settings, then Notifications at top of list -+ ++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageMain.qml-v3.40 2025-02-02 17:47:45 +@@ -1,11 +1,46 @@ import QtQuick 1.1 +import "utils.js" as Utils import com.victron.velib 1.0 @@ -49,7 +47,7 @@ VisualDataModel { model: VeSortFilterProxyModel { model: DeviceList { -@@ -27,23 +64,28 @@ +@@ -27,23 +62,28 @@ subpage: model.page } } @@ -80,7 +78,7 @@ editable: true function clicked() { -@@ -155,7 +197,8 @@ +@@ -155,7 +195,8 @@ page = vebusPage break; case DBusService.DBUS_SERVICE_MULTI_RS: diff --git a/FileSets/PatchSource/PageMain.qml-v3.60~21 b/FileSets/PatchSource/PageMain.qml-v3.60~21 new file mode 100644 index 00000000..1f990841 --- /dev/null +++ b/FileSets/PatchSource/PageMain.qml-v3.60~21 @@ -0,0 +1,294 @@ +import QtQuick 2 +import "utils.js" as Utils +import com.victron.velib 1.0 + +MbPage { + id: root + title: qsTr("Device List") + +//////// GuiMods put Settings, Notifications, Remove disconnected... at top of list + property VBusItem moveSettings: VBusItem { id: moveSettings; bind: Utils.path("com.victronenergy.settings", "/Settings/GuiMods/MoveSettings")} + property bool settingsAtTop: moveSettings.valid && moveSettings.value === 1 + + model: VisualModels { +//////// GuiMods put Settings, Notifications, Remove disconnected... at top of list + VisibleItemModel { //////// use VisualItemModel below for v2.93 and earlier + MbSubMenu { + description: qsTr("Settings") + subpage: Component { PageSettings {} } + show: settingsAtTop + } + + MbSubMenu { + id: menuNotificationsTop + description: qsTr("Notifications") + item: VBusItem { value: menuNotifications.subpage.summary } + subpage: PageNotifications { } + show: settingsAtTop + } + + MbOK { + description: qsTr("Remove disconnected devices") + value: qsTr("Press to remove") + show: settingsAtTop && deviceList.disconnectedDevices != 0 + editable: true + + onClicked: { + listview.decrementCurrentIndex() + deviceList.removeDisconnected() + } + } + } +//////// end GuiMods put Settings, Notifications, Remove disconnected... at top of list + + DelegateModel { + model: VeSortFilterProxyModel { + model: DeviceList { + id: deviceList + onRowsAboutToBeRemoved: { + for (var i = first; i <= last; i++) + deviceList.page(i).destroy() + } + } + sortRole: DeviceList.DescriptionRole + dynamicSortFilter: true + naturalSort: true + sortCaseSensitivity: Qt.CaseInsensitive + } + + delegate: MbDevice { + iconId: "icon-toolbar-enter" + service: model.page.service + subpage: model.page + } + } + VisibleItemModel { + MbSubMenu { + id: menuNotifications + description: qsTr("Notifications") + item: VBusItem { value: menuNotifications.subpage.summary } + subpage: PageNotifications { } +//////// GuiMods hide this if added at top + show: !settingsAtTop + } + + MbSubMenu { + description: qsTr("Settings") + subpage: Component { PageSettings {} } +//////// GuiMods hide this if added at top + show: !settingsAtTop + } + + MbOK { + description: qsTr("Remove disconnected devices") + value: qsTr("Press to remove") +//////// GuiMods hide this if added at top + show: !settingsAtTop && deviceList.disconnectedDevices != 0 + editable: true + + onClicked: { + listview.decrementCurrentIndex() + deviceList.removeDisconnected() + } + } + } + } + + Component { + id: vebusPage + PageVebus {} + } + + Component { + id: batteryPage + PageBattery {} + } + + Component { + id: solarChargerPage + PageSolarCharger {} + } + + Component { + id: acInPage + PageAcIn {} + } + + Component { + id: acChargerPage + PageAcCharger {} + } + + Component { + id: tankPage + PageTankSensor {} + } + + Component { + id: motorDrivePage + PageMotorDrive {} + } + + Component { + id: inverterPage + PageInverter {} + } + + Component { + id: pulseCounterPage + PagePulseCounter {} + } + + Component { + id: digitalInputPage + PageDigitalInput {} + } + + Component { + id: temperatureSensorPage + PageTemperatureSensor {} + } + + Component { + id: unsupportedDevicePage + PageUnsupportedDevice {} + } + + Component { + id: meteoDevicePage + PageMeteo {} + } + + Component { + id: evChargerPage + PageEvCharger {} + } + + Component { + id: dcMeterPage + PageDcMeter {} + } + + Component { + id: alternatorPage + PageAlternator {} + } + + Component { + id: dcDcConverterPage + PageDcDcConverter {} + } + + Component { + id: acSystemPage + PageRsSystem {} + } + + Component { + id: gensetPage + PageGenset {} + } + + function addService(service) + { + var name = service.name + + var page + switch(service.type) + { + case DBusService.DBUS_SERVICE_MULTI: + page = vebusPage + break; + case DBusService.DBUS_SERVICE_MULTI_RS: + return; + case DBusService.DBUS_SERVICE_BATTERY: + page = batteryPage + break; + case DBusService.DBUS_SERVICE_SOLAR_CHARGER: + page = solarChargerPage + break; + case DBusService.DBUS_SERVICE_PV_INVERTER: + page = acInPage + break; + case DBusService.DBUS_SERVICE_AC_CHARGER: + page = acChargerPage + break; + case DBusService.DBUS_SERVICE_TANK: + page = tankPage + break; + case DBusService.DBUS_SERVICE_GRIDMETER: + page = acInPage + break + case DBusService.DBUS_SERVICE_GENSET: + case DBusService.DBUS_SERVICE_DCGENSET: + page = gensetPage + break + case DBusService.DBUS_SERVICE_MOTOR_DRIVE: + page = motorDrivePage + break + case DBusService.DBUS_SERVICE_INVERTER: + page = inverterPage + break; + case DBusService.DBUS_SERVICE_TEMPERATURE_SENSOR: + page = temperatureSensorPage + break; + case DBusService.DBUS_SERVICE_SYSTEM_CALC: + return; + case DBusService.DBUS_SERVICE_DIGITAL_INPUT: + page = digitalInputPage + break; + case DBusService.DBUS_SERVICE_PULSE_COUNTER: + page = pulseCounterPage + break; + case DBusService.DBUS_SERVICE_UNSUPPORTED: + page = unsupportedDevicePage + break; + case DBusService.DBUS_SERVICE_METEO: + page = meteoDevicePage + break; + case DBusService.DBUS_SERVICE_VECAN: + return; + case DBusService.DBUS_SERVICE_EVCHARGER: + page = evChargerPage + break + case DBusService.DBUS_SERVICE_ACLOAD: + page = acInPage + break + case DBusService.DBUS_SERVICE_HUB4: + return; + case DBusService.DBUS_SERVICE_FUELCELL: + case DBusService.DBUS_SERVICE_DCSOURCE: + case DBusService.DBUS_SERVICE_DCLOAD: + case DBusService.DBUS_SERVICE_DCSYSTEM: + page = dcMeterPage + break + case DBusService.DBUS_SERVICE_ALTERNATOR: + page = alternatorPage + break + case DBusService.DBUS_SERVICE_DCDC: + page = dcDcConverterPage + break + case DBusService.DBUS_SERVICE_ACSYSTEM: + page = acSystemPage + break + case DBusService.DBUS_SERVICE_HEATPUMP: + page = acInPage + break + default: + console.log("unknown service " + name) + return; + } + + deviceList.append(service, page.createObject(root, {service: service, bindPrefix: service.name})) + } + + Component.onCompleted: { + for (var i = 0; i < DBusServices.count; i++) + addService(DBusServices.at(i)) + } + + Connections { + target: DBusServices + onDbusServiceFound: addService(service) + } +} diff --git a/FileSets/PatchSource/PageMain.qml-v3.60~21.orig b/FileSets/PatchSource/PageMain.qml-v3.60~21.orig new file mode 100644 index 00000000..9cf0c4cb --- /dev/null +++ b/FileSets/PatchSource/PageMain.qml-v3.60~21.orig @@ -0,0 +1,254 @@ +import QtQuick 2 +import com.victron.velib 1.0 + +MbPage { + id: root + title: qsTr("Device List") + + model: VisualModels { + DelegateModel { + model: VeSortFilterProxyModel { + model: DeviceList { + id: deviceList + onRowsAboutToBeRemoved: { + for (var i = first; i <= last; i++) + deviceList.page(i).destroy() + } + } + sortRole: DeviceList.DescriptionRole + dynamicSortFilter: true + naturalSort: true + sortCaseSensitivity: Qt.CaseInsensitive + } + + delegate: MbDevice { + iconId: "icon-toolbar-enter" + service: model.page.service + subpage: model.page + } + } + VisibleItemModel { + MbSubMenu { + id: menuNotifications + description: qsTr("Notifications") + item: VBusItem { value: menuNotifications.subpage.summary } + subpage: PageNotifications { } + } + + MbSubMenu { + description: qsTr("Settings") + subpage: Component { PageSettings {} } + } + + MbOK { + description: qsTr("Remove disconnected devices") + value: qsTr("Press to remove") + show: deviceList.disconnectedDevices != 0 + editable: true + + onClicked: { + listview.decrementCurrentIndex() + deviceList.removeDisconnected() + } + } + } + } + + Component { + id: vebusPage + PageVebus {} + } + + Component { + id: batteryPage + PageBattery {} + } + + Component { + id: solarChargerPage + PageSolarCharger {} + } + + Component { + id: acInPage + PageAcIn {} + } + + Component { + id: acChargerPage + PageAcCharger {} + } + + Component { + id: tankPage + PageTankSensor {} + } + + Component { + id: motorDrivePage + PageMotorDrive {} + } + + Component { + id: inverterPage + PageInverter {} + } + + Component { + id: pulseCounterPage + PagePulseCounter {} + } + + Component { + id: digitalInputPage + PageDigitalInput {} + } + + Component { + id: temperatureSensorPage + PageTemperatureSensor {} + } + + Component { + id: unsupportedDevicePage + PageUnsupportedDevice {} + } + + Component { + id: meteoDevicePage + PageMeteo {} + } + + Component { + id: evChargerPage + PageEvCharger {} + } + + Component { + id: dcMeterPage + PageDcMeter {} + } + + Component { + id: alternatorPage + PageAlternator {} + } + + Component { + id: dcDcConverterPage + PageDcDcConverter {} + } + + Component { + id: acSystemPage + PageRsSystem {} + } + + Component { + id: gensetPage + PageGenset {} + } + + function addService(service) + { + var name = service.name + + var page + switch(service.type) + { + case DBusService.DBUS_SERVICE_MULTI: + page = vebusPage + break; + case DBusService.DBUS_SERVICE_MULTI_RS: + return; + case DBusService.DBUS_SERVICE_BATTERY: + page = batteryPage + break; + case DBusService.DBUS_SERVICE_SOLAR_CHARGER: + page = solarChargerPage + break; + case DBusService.DBUS_SERVICE_PV_INVERTER: + page = acInPage + break; + case DBusService.DBUS_SERVICE_AC_CHARGER: + page = acChargerPage + break; + case DBusService.DBUS_SERVICE_TANK: + page = tankPage + break; + case DBusService.DBUS_SERVICE_GRIDMETER: + page = acInPage + break + case DBusService.DBUS_SERVICE_GENSET: + case DBusService.DBUS_SERVICE_DCGENSET: + page = gensetPage + break + case DBusService.DBUS_SERVICE_MOTOR_DRIVE: + page = motorDrivePage + break + case DBusService.DBUS_SERVICE_INVERTER: + page = inverterPage + break; + case DBusService.DBUS_SERVICE_TEMPERATURE_SENSOR: + page = temperatureSensorPage + break; + case DBusService.DBUS_SERVICE_SYSTEM_CALC: + return; + case DBusService.DBUS_SERVICE_DIGITAL_INPUT: + page = digitalInputPage + break; + case DBusService.DBUS_SERVICE_PULSE_COUNTER: + page = pulseCounterPage + break; + case DBusService.DBUS_SERVICE_UNSUPPORTED: + page = unsupportedDevicePage + break; + case DBusService.DBUS_SERVICE_METEO: + page = meteoDevicePage + break; + case DBusService.DBUS_SERVICE_VECAN: + return; + case DBusService.DBUS_SERVICE_EVCHARGER: + page = evChargerPage + break + case DBusService.DBUS_SERVICE_ACLOAD: + page = acInPage + break + case DBusService.DBUS_SERVICE_HUB4: + return; + case DBusService.DBUS_SERVICE_FUELCELL: + case DBusService.DBUS_SERVICE_DCSOURCE: + case DBusService.DBUS_SERVICE_DCLOAD: + case DBusService.DBUS_SERVICE_DCSYSTEM: + page = dcMeterPage + break + case DBusService.DBUS_SERVICE_ALTERNATOR: + page = alternatorPage + break + case DBusService.DBUS_SERVICE_DCDC: + page = dcDcConverterPage + break + case DBusService.DBUS_SERVICE_ACSYSTEM: + page = acSystemPage + break + case DBusService.DBUS_SERVICE_HEATPUMP: + page = acInPage + break + default: + console.log("unknown service " + name) + return; + } + + deviceList.append(service, page.createObject(root, {service: service, bindPrefix: service.name})) + } + + Component.onCompleted: { + for (var i = 0; i < DBusServices.count; i++) + addService(DBusServices.at(i)) + } + + Connections { + target: DBusServices + onDbusServiceFound: addService(service) + } +} diff --git a/FileSets/PatchSource/PageMain.qml-v3.60~21.patch b/FileSets/PatchSource/PageMain.qml-v3.60~21.patch new file mode 100644 index 00000000..3762c926 --- /dev/null +++ b/FileSets/PatchSource/PageMain.qml-v3.60~21.patch @@ -0,0 +1,73 @@ +--- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageMain.qml-v3.60~21.orig 2025-01-29 07:31:17 ++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageMain.qml-v3.60~21 2025-02-02 22:03:18 +@@ -1,11 +1,46 @@ + import QtQuick 2 ++import "utils.js" as Utils + import com.victron.velib 1.0 + + MbPage { + id: root + title: qsTr("Device List") + ++//////// GuiMods put Settings, Notifications, Remove disconnected... at top of list ++ property VBusItem moveSettings: VBusItem { id: moveSettings; bind: Utils.path("com.victronenergy.settings", "/Settings/GuiMods/MoveSettings")} ++ property bool settingsAtTop: moveSettings.valid && moveSettings.value === 1 ++ + model: VisualModels { ++//////// GuiMods put Settings, Notifications, Remove disconnected... at top of list ++ VisibleItemModel { //////// use VisualItemModel below for v2.93 and earlier ++ MbSubMenu { ++ description: qsTr("Settings") ++ subpage: Component { PageSettings {} } ++ show: settingsAtTop ++ } ++ ++ MbSubMenu { ++ id: menuNotificationsTop ++ description: qsTr("Notifications") ++ item: VBusItem { value: menuNotifications.subpage.summary } ++ subpage: PageNotifications { } ++ show: settingsAtTop ++ } ++ ++ MbOK { ++ description: qsTr("Remove disconnected devices") ++ value: qsTr("Press to remove") ++ show: settingsAtTop && deviceList.disconnectedDevices != 0 ++ editable: true ++ ++ onClicked: { ++ listview.decrementCurrentIndex() ++ deviceList.removeDisconnected() ++ } ++ } ++ } ++//////// end GuiMods put Settings, Notifications, Remove disconnected... at top of list ++ + DelegateModel { + model: VeSortFilterProxyModel { + model: DeviceList { +@@ -33,17 +68,22 @@ + description: qsTr("Notifications") + item: VBusItem { value: menuNotifications.subpage.summary } + subpage: PageNotifications { } ++//////// GuiMods hide this if added at top ++ show: !settingsAtTop + } + + MbSubMenu { + description: qsTr("Settings") + subpage: Component { PageSettings {} } ++//////// GuiMods hide this if added at top ++ show: !settingsAtTop + } + + MbOK { + description: qsTr("Remove disconnected devices") + value: qsTr("Press to remove") +- show: deviceList.disconnectedDevices != 0 ++//////// GuiMods hide this if added at top ++ show: !settingsAtTop && deviceList.disconnectedDevices != 0 + editable: true + + onClicked: { diff --git a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.14 b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.14 index de3b95d4..e4b5276b 100644 --- a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.14 +++ b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.14 @@ -1,5 +1,3 @@ -//////// modified to add GuiMods controls - import QtQuick 1.1 import com.victron.velib 1.0 import "utils.js" as Utils diff --git a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.14.patch b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.14.patch index 74fcc5ee..d42fa297 100644 --- a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.14.patch +++ b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.14.patch @@ -1,12 +1,6 @@ --- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsDisplay.qml-v3.14.orig 2024-07-09 10:19:58 -+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsDisplay.qml-v3.14 2024-01-24 10:19:41 -@@ -1,3 +1,5 @@ -+//////// modified to add GuiMods controls -+ - import QtQuick 1.1 - import com.victron.velib 1.0 - import "utils.js" as Utils -@@ -52,6 +54,15 @@ ++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsDisplay.qml-v3.14 2025-02-02 17:43:45 +@@ -52,6 +52,15 @@ ] } @@ -22,7 +16,7 @@ MbSwitch { bind: Utils.path(bindPrefix, "/MobileOverview") name: qsTr("Show boat & motorhome overview") -@@ -65,12 +76,20 @@ +@@ -65,12 +74,20 @@ name: qsTr("Show tanks overview") } @@ -44,7 +38,7 @@ // NOTE: do make sure application.cpp returns the correct fontForLanguage. // The current font might not be able to display these values / the default // font might not be contain the characters required for the selected language. -@@ -83,11 +102,9 @@ +@@ -83,11 +100,9 @@ MbOptionLang { description: "Français"; value: "fr" }, MbOptionLang { description: "Italiano"; value: "it" }, MbOptionLang { description: "Nederlands"; value: "nl" }, diff --git a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.22 b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.22 index b1c8f278..ad3f7d85 100644 --- a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.22 +++ b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.22 @@ -1,5 +1,3 @@ -//////// modified to add GuiMods controls - import QtQuick 1.1 import com.victron.velib 1.0 import "utils.js" as Utils diff --git a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.22.patch b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.22.patch index 2b87c002..58907266 100644 --- a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.22.patch +++ b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.22.patch @@ -1,12 +1,6 @@ --- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsDisplay.qml-v3.22.orig 2024-07-09 10:19:58 -+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsDisplay.qml-v3.22 2024-03-24 08:13:08 -@@ -1,3 +1,5 @@ -+//////// modified to add GuiMods controls -+ - import QtQuick 1.1 - import com.victron.velib 1.0 - import "utils.js" as Utils -@@ -52,6 +54,15 @@ ++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsDisplay.qml-v3.22 2025-02-02 17:43:59 +@@ -52,6 +52,15 @@ ] } @@ -22,7 +16,7 @@ MbSwitch { bind: Utils.path(bindPrefix, "/MobileOverview") name: qsTr("Show boat & motorhome overview") -@@ -65,6 +76,15 @@ +@@ -65,6 +74,15 @@ name: qsTr("Show tanks overview") } @@ -38,7 +32,7 @@ MbItemOptions { id: languageSelect description: qsTr("Language") -@@ -75,23 +95,23 @@ +@@ -75,23 +93,23 @@ // The current font might not be able to display these values / the default // font might not be contain the characters required for the selected language. possibleValues: [ diff --git a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.34 b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.34 index bc182a38..e6f1cb52 100644 --- a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.34 +++ b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.34 @@ -1,5 +1,3 @@ -//////// modified to add GuiMods controls - import QtQuick 1.1 import com.victron.velib 1.0 import "utils.js" as Utils diff --git a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.34.patch b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.34.patch index c1ff3b58..5aac065c 100644 --- a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.34.patch +++ b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.34.patch @@ -1,12 +1,6 @@ --- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsDisplay.qml-v3.34.orig 2024-07-09 10:19:58 -+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsDisplay.qml-v3.34 2024-03-24 08:13:08 -@@ -1,3 +1,5 @@ -+//////// modified to add GuiMods controls -+ - import QtQuick 1.1 - import com.victron.velib 1.0 - import "utils.js" as Utils -@@ -65,6 +67,15 @@ ++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsDisplay.qml-v3.34 2025-02-02 17:44:10 +@@ -65,6 +65,15 @@ name: qsTr("Show tanks overview") } diff --git a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.40 b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.40 index ad1b1187..a0461853 100644 --- a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.40 +++ b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.40 @@ -1,5 +1,3 @@ -//////// modified to add GuiMods controls - import QtQuick 1.1 import com.victron.velib 1.0 import "utils.js" as Utils diff --git a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.40.patch b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.40.patch index fa17a507..d0349312 100644 --- a/FileSets/PatchSource/PageSettingsDisplay.qml-v3.40.patch +++ b/FileSets/PatchSource/PageSettingsDisplay.qml-v3.40.patch @@ -1,12 +1,6 @@ --- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsDisplay.qml-v3.40.orig 2024-07-09 10:19:58 -+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsDisplay.qml-v3.40 2024-05-14 07:00:12 -@@ -1,3 +1,5 @@ -+//////// modified to add GuiMods controls -+ - import QtQuick 1.1 - import com.victron.velib 1.0 - import "utils.js" as Utils -@@ -65,6 +67,15 @@ ++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsDisplay.qml-v3.40 2025-02-02 17:43:38 +@@ -65,6 +65,15 @@ name: qsTr("Show tanks overview") } diff --git a/FileSets/PatchSource/PageSettingsGenerator.qml b/FileSets/PatchSource/PageSettingsGenerator.qml index 9b2625a6..ae056da8 100644 --- a/FileSets/PatchSource/PageSettingsGenerator.qml +++ b/FileSets/PatchSource/PageSettingsGenerator.qml @@ -1,6 +1,3 @@ -//// GuiMods -//// added link to external state enable - import QtQuick 1.1 import com.victron.velib 1.0 import "utils.js" as Utils diff --git a/FileSets/PatchSource/PageSettingsGenerator.qml.patch b/FileSets/PatchSource/PageSettingsGenerator.qml.patch index a0a8eac6..45ef77de 100644 --- a/FileSets/PatchSource/PageSettingsGenerator.qml.patch +++ b/FileSets/PatchSource/PageSettingsGenerator.qml.patch @@ -1,13 +1,6 @@ --- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsGenerator.qml.orig 2025-01-12 08:33:22 -+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsGenerator.qml 2024-09-28 14:15:31 -@@ -1,3 +1,6 @@ -+//// GuiMods -+//// added link to external state enable -+ - import QtQuick 1.1 - import com.victron.velib 1.0 - import "utils.js" as Utils -@@ -66,7 +69,18 @@ ++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsGenerator.qml 2025-02-02 17:44:35 +@@ -66,7 +66,18 @@ } } diff --git a/FileSets/PatchSource/PageSettingsRelay.qml-v3.22 b/FileSets/PatchSource/PageSettingsRelay.qml-v3.22 index 0c401e3e..921f24cf 100644 --- a/FileSets/PatchSource/PageSettingsRelay.qml-v3.22 +++ b/FileSets/PatchSource/PageSettingsRelay.qml-v3.22 @@ -1,8 +1,3 @@ -//////// modified to -//////// add 6 relays for Raspberry PI -//////// custom relay name for Relay Overview -//////// show/hide relay in Relay Overview - import QtQuick 1.1 import com.victron.velib 1.0 import "utils.js" as Utils diff --git a/FileSets/PatchSource/PageSettingsRelay.qml-v3.22.patch b/FileSets/PatchSource/PageSettingsRelay.qml-v3.22.patch index 2c034d95..81302df9 100644 --- a/FileSets/PatchSource/PageSettingsRelay.qml-v3.22.patch +++ b/FileSets/PatchSource/PageSettingsRelay.qml-v3.22.patch @@ -1,15 +1,6 @@ --- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsRelay.qml-v3.22.orig 2025-01-09 14:05:43 -+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsRelay.qml-v3.22 2024-02-27 21:25:25 -@@ -1,3 +1,8 @@ -+//////// modified to -+//////// add 6 relays for Raspberry PI -+//////// custom relay name for Relay Overview -+//////// show/hide relay in Relay Overview -+ - import QtQuick 1.1 - import com.victron.velib 1.0 - import "utils.js" as Utils -@@ -6,13 +11,85 @@ ++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsRelay.qml-v3.22 2025-02-02 17:44:48 +@@ -6,13 +6,85 @@ id: pageRelaySettings title: qsTr("Relay") property string bindPrefix: "com.victronenergy.settings" @@ -98,7 +89,7 @@ bind: Utils.path(bindPrefix, "/Settings/Relay/Function") possibleValues:[ MbOption { description: qsTr("Alarm relay"); value: 0 }, -@@ -21,12 +98,13 @@ +@@ -21,12 +93,13 @@ MbOption { description: qsTr("Manual"); value: 2 }, MbOption { description: qsTr("Temperature"); value: 4 } ] @@ -113,7 +104,7 @@ possibleValues: [ MbOption { description: qsTr("Normally open"); value: 0 }, MbOption { description: qsTr("Normally closed"); value: 1 } -@@ -34,58 +112,422 @@ +@@ -34,52 +107,147 @@ } MbSwitch { @@ -276,10 +267,10 @@ subpage: Component { PageSettingsRelayTempSensors { id: relayPage - title: qsTr("Temperature control rules") +@@ -87,5 +255,274 @@ } } -+ } + } + + MbEditBox { + id: relay1name @@ -309,7 +300,7 @@ + name: qsTr("Show Relay 2 in overview") + bind: "com.victronenergy.settings/Settings/Relay/1/Show" + show: hasRelay2 - } ++ } + + MbEditBox { + id: relay3name diff --git a/FileSets/PatchSource/PageSettingsRelay.qml-v3.60~16 b/FileSets/PatchSource/PageSettingsRelay.qml-v3.60~16 index 44c08871..8d9781ad 100644 --- a/FileSets/PatchSource/PageSettingsRelay.qml-v3.60~16 +++ b/FileSets/PatchSource/PageSettingsRelay.qml-v3.60~16 @@ -1,8 +1,3 @@ -//////// modified to -//////// add up to 18 relays -//////// custom relay name for Relay Overview -//////// show/hide relay in Relay Overview - import QtQuick 1.1 import com.victron.velib 1.0 import "utils.js" as Utils diff --git a/FileSets/PatchSource/PageSettingsRelay.qml-v3.60~16.patch b/FileSets/PatchSource/PageSettingsRelay.qml-v3.60~16.patch index a0eb7c1e..ca912a0b 100644 --- a/FileSets/PatchSource/PageSettingsRelay.qml-v3.60~16.patch +++ b/FileSets/PatchSource/PageSettingsRelay.qml-v3.60~16.patch @@ -1,15 +1,6 @@ --- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsRelay.qml-v3.60~16.orig 2025-01-09 14:05:42 -+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsRelay.qml-v3.60~16 2024-10-02 11:55:16 -@@ -1,3 +1,8 @@ -+//////// modified to -+//////// add up to 18 relays -+//////// custom relay name for Relay Overview -+//////// show/hide relay in Relay Overview -+ - import QtQuick 1.1 - import com.victron.velib 1.0 - import "utils.js" as Utils -@@ -6,14 +11,85 @@ ++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/PageSettingsRelay.qml-v3.60~16 2025-02-02 17:45:04 +@@ -6,14 +6,85 @@ id: pageRelaySettings title: qsTr("Relay") property string bindPrefix: "com.victronenergy.settings" @@ -99,7 +90,7 @@ bind: Utils.path(bindPrefix, "/Settings/Relay/Function") possibleValues:[ MbOption { description: qsTr("Alarm relay"); value: 0 }, -@@ -23,12 +99,13 @@ +@@ -23,12 +94,13 @@ MbOption { description: qsTr("Manual"); value: 2 }, MbOption { description: qsTr("Temperature"); value: 4 } ] @@ -114,7 +105,7 @@ possibleValues: [ MbOption { description: qsTr("Normally open"); value: 0 }, MbOption { description: qsTr("Normally closed"); value: 1 } -@@ -36,34 +113,129 @@ +@@ -36,40 +108,404 @@ } MbSwitch { @@ -257,10 +248,10 @@ subpage: Component { PageSettingsRelayTempSensors { id: relayPage -@@ -71,5 +243,274 @@ + title: qsTr("Temperature control rules") } } - } ++ } + + MbEditBox { + id: relay1name @@ -365,7 +356,7 @@ + name: qsTr("Show Relay 7 in overview") + bind: "com.victronenergy.settings/Settings/Relay/6/Show" + show: hasRelay7 -+ } + } + + MbEditBox { + id: relay8name diff --git a/FileSets/PatchSource/main.qml-v3.22 b/FileSets/PatchSource/main.qml-v3.22 index 6df292cd..ccfffc9a 100644 --- a/FileSets/PatchSource/main.qml-v3.22 +++ b/FileSets/PatchSource/main.qml-v3.22 @@ -1,6 +1,3 @@ -//////// Modified to hide the OverviewTiles page -//////// Modified to substitute flow overview pages - import QtQuick 1.1 import Qt.labs.components.native 1.0 diff --git a/FileSets/PatchSource/main.qml-v3.22.patch b/FileSets/PatchSource/main.qml-v3.22.patch index fb43f14f..4f459b8b 100644 --- a/FileSets/PatchSource/main.qml-v3.22.patch +++ b/FileSets/PatchSource/main.qml-v3.22.patch @@ -1,13 +1,6 @@ --- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.22.orig 2024-07-08 09:16:57 -+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.22 2024-01-24 10:19:41 -@@ -1,3 +1,6 @@ -+//////// Modified to hide the OverviewTiles page -+//////// Modified to substitute flow overview pages -+ - import QtQuick 1.1 - - import Qt.labs.components.native 1.0 -@@ -16,48 +19,108 @@ ++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.22 2025-02-02 17:46:00 +@@ -16,48 +16,108 @@ property bool completed: false property bool showAlert: NotificationCenter.alert property bool alarm: NotificationCenter.alarm @@ -138,7 +131,7 @@ VBusItem { bind: "com.victronenergy.generator.startstop1/GensetProductId" onValueChanged: { -@@ -75,21 +138,103 @@ +@@ -75,21 +135,103 @@ } } @@ -180,7 +173,7 @@ - extraOverview("OverviewTanks.qml", value === 1) - } - } - ++ +//////// handle OverviewMobileEnhanced page + VBusItem + { @@ -236,7 +229,7 @@ + bind: "com.victronenergy.settings/Settings/GuiMods/ShowRelayOverview" + onValueChanged: extraOverview ("OverviewRelays.qml", value === 1) + } -+ + +//////// show/hide the Overview Tanks/Temps/Digital Inputs page + VBusItem { + id: showOverviewTanksTemps @@ -255,7 +248,7 @@ VBusItem { id: startWithMenu bind: "com.victronenergy.settings/Settings/Gui/StartWithMenuView" -@@ -169,76 +314,111 @@ +@@ -169,76 +311,111 @@ id: mbTools height: parent.height @@ -419,7 +412,7 @@ } } } -@@ -253,9 +433,10 @@ +@@ -253,9 +430,10 @@ ListElement { pageSource: "OverviewHub.qml" } @@ -433,7 +426,7 @@ } Component { -@@ -274,7 +455,12 @@ +@@ -274,7 +452,12 @@ Timer { interval: 2000 running: completed && overviewsLoaded && startWithMenu.valid @@ -447,7 +440,7 @@ } function getDefaultOverviewIndex() -@@ -297,6 +483,7 @@ +@@ -297,6 +480,7 @@ Component { id: offlineFwUpdates PageSettingsFirmwareOffline { checkOnCompleted: true} @@ -455,7 +448,7 @@ } // Add or remove extra overviews. for example, generator overview -@@ -327,11 +514,19 @@ +@@ -327,11 +511,19 @@ } } diff --git a/FileSets/PatchSource/main.qml-v3.34 b/FileSets/PatchSource/main.qml-v3.34 index d300de63..3398b6e7 100644 --- a/FileSets/PatchSource/main.qml-v3.34 +++ b/FileSets/PatchSource/main.qml-v3.34 @@ -1,6 +1,3 @@ -//////// Modified to hide the OverviewTiles page -//////// Modified to substitute flow overview pages - import QtQuick 1.1 import Qt.labs.components.native 1.0 diff --git a/FileSets/PatchSource/main.qml-v3.34.patch b/FileSets/PatchSource/main.qml-v3.34.patch index b49adf80..ec6bbcfd 100644 --- a/FileSets/PatchSource/main.qml-v3.34.patch +++ b/FileSets/PatchSource/main.qml-v3.34.patch @@ -1,13 +1,6 @@ --- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.34.orig 2024-07-08 09:16:57 -+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.34 2024-05-14 07:00:12 -@@ -1,3 +1,6 @@ -+//////// Modified to hide the OverviewTiles page -+//////// Modified to substitute flow overview pages -+ - import QtQuick 1.1 - - import Qt.labs.components.native 1.0 -@@ -16,48 +19,108 @@ ++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.34 2025-02-02 17:46:10 +@@ -16,48 +16,108 @@ property bool completed: false property bool alarm: alarmNotification.valid ? alarmNotification.value : 0 property bool showAlert: alertNotification.valid ? alertNotification.value : 0 @@ -138,7 +131,7 @@ VBusItem { bind: "com.victronenergy.generator.startstop1/GensetProductId" onValueChanged: { -@@ -75,21 +138,103 @@ +@@ -75,21 +135,103 @@ } } @@ -180,7 +173,7 @@ - extraOverview("OverviewTanks.qml", value === 1) - } - } - ++ +//////// handle OverviewMobileEnhanced page + VBusItem + { @@ -236,7 +229,7 @@ + bind: "com.victronenergy.settings/Settings/GuiMods/ShowRelayOverview" + onValueChanged: extraOverview ("OverviewRelays.qml", value === 1) + } -+ + +//////// show/hide the Overview Tanks/Temps/Digital Inputs page + VBusItem { + id: showOverviewTanksTemps @@ -255,7 +248,7 @@ VBusItem { id: startWithMenu bind: "com.victronenergy.settings/Settings/Gui/StartWithMenuView" -@@ -179,76 +324,111 @@ +@@ -179,76 +321,111 @@ id: mbTools height: parent.height @@ -419,7 +412,7 @@ } } } -@@ -263,9 +443,10 @@ +@@ -263,9 +440,10 @@ ListElement { pageSource: "OverviewHub.qml" } @@ -433,7 +426,7 @@ } Component { -@@ -284,7 +465,12 @@ +@@ -284,7 +462,12 @@ Timer { interval: 2000 running: completed && overviewsLoaded && startWithMenu.valid @@ -447,7 +440,7 @@ } function getDefaultOverviewIndex() -@@ -307,6 +493,7 @@ +@@ -307,6 +490,7 @@ Component { id: offlineFwUpdates PageSettingsFirmwareOffline { checkOnCompleted: true} @@ -455,7 +448,7 @@ } // Add or remove extra overviews. for example, generator overview -@@ -337,11 +524,19 @@ +@@ -337,11 +521,19 @@ } } diff --git a/FileSets/PatchSource/main.qml-v3.40 b/FileSets/PatchSource/main.qml-v3.40 index f90616b4..30e839c1 100644 --- a/FileSets/PatchSource/main.qml-v3.40 +++ b/FileSets/PatchSource/main.qml-v3.40 @@ -1,6 +1,3 @@ -//////// Modified to hide the OverviewTiles page -//////// Modified to substitute flow overview pages - import QtQuick 1.1 import Qt.labs.components.native 1.0 diff --git a/FileSets/PatchSource/main.qml-v3.40.patch b/FileSets/PatchSource/main.qml-v3.40.patch index ce4e1617..70735501 100644 --- a/FileSets/PatchSource/main.qml-v3.40.patch +++ b/FileSets/PatchSource/main.qml-v3.40.patch @@ -1,13 +1,6 @@ --- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.40.orig 2024-07-08 09:16:57 -+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.40 2024-05-17 07:34:02 -@@ -1,3 +1,6 @@ -+//////// Modified to hide the OverviewTiles page -+//////// Modified to substitute flow overview pages -+ - import QtQuick 1.1 - - import Qt.labs.components.native 1.0 -@@ -16,7 +19,8 @@ ++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.40 2025-02-02 17:46:23 +@@ -16,7 +16,8 @@ property bool completed: false property bool alarm: alarmNotification.valid ? alarmNotification.value : 0 property bool showAlert: alertNotification.valid ? alertNotification.value : 0 @@ -17,7 +10,7 @@ property string bindPrefix: "com.victronenergy.settings" property bool isNotificationPage: pageStack.currentPage && pageStack.currentPage.title === qsTr("Notifications") -@@ -25,25 +29,83 @@ +@@ -25,25 +26,83 @@ property bool hasGridMeter: theSystem.hasGridMeter @@ -107,7 +100,7 @@ VBusItem { bind: "com.victronenergy.generator.startstop1/GensetProductId" onValueChanged: { -@@ -52,7 +114,7 @@ +@@ -52,7 +111,7 @@ // Show generic overview for ComAp and DSE extraOverview("OverviewGeneratorOther.qml", @@ -116,7 +109,7 @@ // Switch to FP overview in case it is the default one if (isOverviewPage) { -@@ -61,20 +123,102 @@ +@@ -61,21 +120,103 @@ } } @@ -158,7 +151,7 @@ - extraOverview("OverviewTanks.qml", value === 1) - } - } -+ + +//////// handle OverviewMobileEnhanced page + VBusItem + { @@ -229,10 +222,11 @@ + extraOverview("OverviewTanks.qml", value === 1) + } + } - ++ VBusItem { id: startWithMenu -@@ -165,76 +309,111 @@ + bind: "com.victronenergy.settings/Settings/Gui/StartWithMenuView" +@@ -165,76 +306,111 @@ id: mbTools height: parent.height @@ -397,7 +391,7 @@ } } } -@@ -249,9 +428,10 @@ +@@ -249,9 +425,10 @@ ListElement { pageSource: "OverviewHub.qml" } @@ -411,7 +405,7 @@ } Component { -@@ -270,7 +450,12 @@ +@@ -270,7 +447,12 @@ Timer { interval: 2000 running: completed && overviewsLoaded && startWithMenu.valid @@ -425,7 +419,7 @@ } function getDefaultOverviewIndex() -@@ -293,6 +478,7 @@ +@@ -293,6 +475,7 @@ Component { id: offlineFwUpdates PageSettingsFirmwareOffline { checkOnCompleted: true} @@ -433,7 +427,7 @@ } // Add or remove extra overviews. for example, generator overview -@@ -323,11 +509,19 @@ +@@ -323,11 +506,19 @@ } } diff --git a/FileSets/PatchSource/main.qml-v3.50 b/FileSets/PatchSource/main.qml-v3.50 index 0042e812..b821c1e1 100644 --- a/FileSets/PatchSource/main.qml-v3.50 +++ b/FileSets/PatchSource/main.qml-v3.50 @@ -1,6 +1,3 @@ -//////// Modified to hide the OverviewTiles page -//////// Modified to substitute flow overview pages - import QtQuick 1.1 import Qt.labs.components.native 1.0 diff --git a/FileSets/PatchSource/main.qml-v3.50.patch b/FileSets/PatchSource/main.qml-v3.50.patch index 8b280cdd..3edab79a 100644 --- a/FileSets/PatchSource/main.qml-v3.50.patch +++ b/FileSets/PatchSource/main.qml-v3.50.patch @@ -1,13 +1,6 @@ --- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.50.orig 2024-10-29 13:29:52 -+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.50 2024-11-03 13:26:42 -@@ -1,3 +1,6 @@ -+//////// Modified to hide the OverviewTiles page -+//////// Modified to substitute flow overview pages -+ - import QtQuick 1.1 - - import Qt.labs.components.native 1.0 -@@ -16,7 +19,8 @@ ++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.50 2025-02-02 17:46:37 +@@ -16,7 +16,8 @@ property bool completed: false property bool alarm: alarmNotification.valid ? alarmNotification.value : 0 property bool showAlert: alertNotification.valid ? alertNotification.value : 0 @@ -17,7 +10,7 @@ property string bindPrefix: "com.victronenergy.settings" property bool isNotificationPage: pageStack.currentPage && pageStack.currentPage.title === qsTr("Notifications") -@@ -27,24 +31,83 @@ +@@ -27,24 +28,83 @@ property bool showInputLoads: theSystem.acInLoad.power.valid && (hasVebusEss ? (theSystem.hasGridMeter && withoutGridMeter.value === 0) : theSystem.hasGridMeter) property int newUiAnnouncementVersion: 2 // Increase to make the popup appear again @@ -107,7 +100,7 @@ VBusItem { bind: "com.victronenergy.generator.startstop1/GensetProductId" -@@ -63,13 +126,98 @@ +@@ -63,13 +123,98 @@ } } @@ -212,7 +205,7 @@ VBusItem { id: tanksOverview bind: "com.victronenergy.settings/Settings/Gui/TanksOverview" -@@ -172,76 +320,111 @@ +@@ -172,76 +317,111 @@ id: mbTools height: parent.height @@ -377,7 +370,7 @@ } } } -@@ -256,9 +439,10 @@ +@@ -256,9 +436,10 @@ ListElement { pageSource: "OverviewHub.qml" } @@ -391,7 +384,7 @@ } Component { -@@ -284,7 +468,12 @@ +@@ -284,7 +465,12 @@ id: newUiPopupTimer interval: 10000 running: false @@ -405,7 +398,7 @@ } function showNewUiPopup() -@@ -350,11 +539,19 @@ +@@ -350,11 +536,19 @@ } } diff --git a/FileSets/PatchSource/main.qml-v3.51 b/FileSets/PatchSource/main.qml-v3.51 index 5ad419f3..c8e951c5 100644 --- a/FileSets/PatchSource/main.qml-v3.51 +++ b/FileSets/PatchSource/main.qml-v3.51 @@ -1,6 +1,3 @@ -//////// Modified to hide the OverviewTiles page -//////// Modified to substitute flow overview pages - import QtQuick 1.1 import Qt.labs.components.native 1.0 diff --git a/FileSets/PatchSource/main.qml-v3.51.patch b/FileSets/PatchSource/main.qml-v3.51.patch index 4e4b7016..f9ef1490 100644 --- a/FileSets/PatchSource/main.qml-v3.51.patch +++ b/FileSets/PatchSource/main.qml-v3.51.patch @@ -1,13 +1,6 @@ --- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.51.orig 2024-11-04 09:10:11 -+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.51 2024-11-05 11:00:48 -@@ -1,3 +1,6 @@ -+//////// Modified to hide the OverviewTiles page -+//////// Modified to substitute flow overview pages -+ - import QtQuick 1.1 - - import Qt.labs.components.native 1.0 -@@ -8,7 +11,7 @@ ++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.51 2025-02-02 17:46:52 +@@ -8,7 +8,7 @@ id: rootWindow gpsConnected: gpsFix.value === 1 @@ -16,7 +9,7 @@ initialPage: PageMain {} property VeQuickItem gpsService: VeQuickItem { uid: "dbus/com.victronenergy.system/GpsService" } -@@ -16,7 +19,8 @@ +@@ -16,7 +16,8 @@ property bool completed: false property bool alarm: alarmNotification.valid ? alarmNotification.value : 0 property bool showAlert: alertNotification.valid ? alertNotification.value : 0 @@ -26,7 +19,7 @@ property string bindPrefix: "com.victronenergy.settings" property bool isNotificationPage: pageStack.currentPage && pageStack.currentPage.title === qsTr("Notifications") -@@ -25,25 +29,85 @@ +@@ -25,25 +26,85 @@ property bool hasVebusEss: ['ESS', 'Hub-4'].indexOf(theSystem.systemType.value) > -1 property bool showInputLoads: theSystem.acInLoad.power.valid && (hasVebusEss ? (theSystem.hasGridMeter && withoutGridMeter.value === 0) : theSystem.hasGridMeter) @@ -118,7 +111,7 @@ VBusItem { bind: "com.victronenergy.generator.startstop1/GensetProductId" -@@ -62,13 +126,98 @@ +@@ -62,13 +123,98 @@ } } @@ -223,7 +216,7 @@ VBusItem { id: tanksOverview bind: "com.victronenergy.settings/Settings/Gui/TanksOverview" -@@ -112,6 +261,11 @@ +@@ -112,6 +258,11 @@ bind: "com.victronenergy.platform/Notifications/Alarm" } @@ -235,7 +228,7 @@ // Note: finding a firmware image on the storage device is error 4 for vrm storage // since it should not be used for logging. That fact is used here to determine if // there is a firmware image. -@@ -166,76 +320,111 @@ +@@ -166,76 +317,111 @@ id: mbTools height: parent.height @@ -399,7 +392,7 @@ } } } -@@ -250,9 +439,10 @@ +@@ -250,9 +436,10 @@ ListElement { pageSource: "OverviewHub.qml" } @@ -413,7 +406,7 @@ } Component { -@@ -274,6 +464,26 @@ +@@ -274,6 +461,26 @@ onTriggered: if (startWithMenu.value === 0) showOverview() } @@ -440,7 +433,7 @@ function getDefaultOverviewIndex() { if(!defaultOverview.valid) -@@ -324,11 +534,19 @@ +@@ -324,11 +531,19 @@ } } diff --git a/FileSets/PatchSource/startstop.py-v3.60~25 b/FileSets/PatchSource/startstop.py-v3.60~25 new file mode 100644 index 00000000..1cf3daed --- /dev/null +++ b/FileSets/PatchSource/startstop.py-v3.60~25 @@ -0,0 +1,1552 @@ +#!/usr/bin/python -u +# -*- coding: utf-8 -*- + +#### GuiMods +#### This file has been modified to allow the generator running state derived from the generator digital input +#### Previous versions also used the genset AC input but this has been removed from this version with recent changes to stock code !!!!! +#### If the incoming generator state changes, the manual start state is updated +#### A switch in the generator settings menu controls whethter the incoming state affects manual start or time accumulaiton +#### It is now possible to start the generator manually and have it stop automatically based on the preset conditions +#### for automaitc start / stop +#### warm-up and cool-down periods have been modified in order to work with an external transfer switch +#### selecting grid or generator ahead of a MultiPlus input. +#### Search for #### GuiMods to find changes + +# Function +# dbus_generator monitors the dbus for batteries (com.victronenergy.battery.*) and +# vebus com.victronenergy.vebus.* +# Battery and vebus monitors can be configured through the gui. +# It then monitors SOC, AC loads, battery current and battery voltage,to auto start/stop the generator based +# on the configuration settings. Generator can be started manually or periodically setting a tes trun period. +# Time zones function allows to use different values for the conditions along the day depending on time + +import dbus +import datetime +import calendar +import time +import sys +import json +import os +import logging +from collections import OrderedDict +import monotonic_time +from gen_utils import SettingsPrefix, Errors, States, enum +from gen_utils import create_dbus_service +# Victron packages +sys.path.insert(1, os.path.join(os.path.dirname(__file__), 'ext', 'velib_python')) +from ve_utils import exit_on_error +from settingsdevice import SettingsDevice + +RunningConditions = enum( + Stopped = 0, + Manual = 1, + TestRun = 2, + LossOfCommunication = 3, + Soc = 4, + Acload = 5, + BatteryCurrent = 6, + BatteryVoltage = 7, + InverterHighTemp = 8, + InverterOverload = 9, + StopOnAc1 = 10, + StopOnAc2 = 11) + +Capabilities = enum( + WarmupCooldown = 1 +) + +SYSTEM_SERVICE = 'com.victronenergy.system' +BATTERY_PREFIX = '/Dc/Battery' +HISTORY_DAYS = 30 +AUTOSTART_DISABLED_ALARM_TIME = 600 + +def safe_max(args): + try: + return max(x for x in args if x is not None) + except ValueError: + return None + +class Condition(object): + def __init__(self, parent): + self.parent = parent + self.reached = False + self.start_timer = 0 + self.stop_timer = 0 + self.valid = True + self.enabled = False + self.retries = 0 + + def __getitem__(self, key): + try: + return getattr(self, key) + except AttributeError: + raise KeyError(key) + + def __setitem__(self, key, value): + setattr(self, key, value) + + def get_value(self): + raise NotImplementedError("get_value") + + @property + def multi_service(self): + return self.parent.multiservice + + @property + def multi_service_type(self): + return self.parent.multiservice_type + + @property + def monitor(self): + return self.parent._dbusmonitor + +class SocCondition(Condition): + name = 'soc' + monitoring = 'battery' + boolean = False + timed = True + + def get_value(self): + return self.parent._get_battery().soc + +class AcLoadCondition(Condition): + name = 'acload' + monitoring = 'vebus' + boolean = False + timed = True + + def get_value(self): + loadOnAcOut = [] + totalConsumption = [] + + for phase in ['L1', 'L2', 'L3']: + # Get the values directly from the inverter, systemcalc doesn't provide raw inverted power + loadOnAcOut.append(self.monitor.get_value(self.multi_service, ('/Ac/Out/%s/P' % phase))) + + # Calculate total consumption, '/Ac/Consumption/%s/Power' is deprecated + c_i = self.monitor.get_value(SYSTEM_SERVICE, ('/Ac/ConsumptionOnInput/%s/Power' % phase)) + c_o = self.monitor.get_value(SYSTEM_SERVICE, ('/Ac/ConsumptionOnOutput/%s/Power' % phase)) + totalConsumption.append(sum(filter(None, (c_i, c_o)))) + + # Invalidate if vebus is not available + if loadOnAcOut[0] == None: + return None + + # Total consumption + if self.parent._settings['acloadmeasurement'] == 0: + return sum(filter(None, totalConsumption)) + + # Load on inverter AC out + if self.parent._settings['acloadmeasurement'] == 1: + return sum(filter(None, loadOnAcOut)) + + # Highest phase load + if self.parent._settings['acloadmeasurement'] == 2: + return safe_max(loadOnAcOut) + +class BatteryCurrentCondition(Condition): + name = 'batterycurrent' + monitoring = 'battery' + boolean = False + timed = True + + def get_value(self): + c = self.parent._get_battery().current + if c is not None: + c *= -1 + return c + +class BatteryVoltageCondition(Condition): + name = 'batteryvoltage' + monitoring = 'battery' + boolean = False + timed = True + + def get_value(self): + return self.parent._get_battery().voltage + +class InverterTempCondition(Condition): + name = 'inverterhightemp' + monitoring = 'vebus' + boolean = True + timed = True + + def get_value(self): + v = self.monitor.get_value(self.multi_service, + '/Alarms/HighTemperature') + + # When multi is connected to CAN-bus, alarms are published to + # /Alarms/HighTemperature... but when connected to vebus alarms are + # splitted in three phases and published to /Alarms/LX/HighTemperature... + if v is None: + inverterHighTemp = [] + for phase in ['L1', 'L2', 'L3']: + # Inverter alarms must be fetched directly from the inverter service + inverterHighTemp.append(self.monitor.get_value(self.multi_service, ('/Alarms/%s/HighTemperature' % phase))) + return safe_max(inverterHighTemp) + return v + +class InverterOverloadCondition(Condition): + name = 'inverteroverload' + monitoring = 'vebus' + boolean = True + timed = True + + def get_value(self): + v = self.monitor.get_value(self.multi_service, + '/Alarms/Overload') + + # When multi is connected to CAN-bus, alarms are published to + # /Alarms/Overload... but when connected to vebus alarms are + # splitted in three phases and published to /Alarms/LX/Overload... + if v is None: + inverterOverload = [] + for phase in ['L1', 'L2', 'L3']: + # Inverter alarms must be fetched directly from the inverter service + inverterOverload.append(self.monitor.get_value(self.multi_service, ('/Alarms/%s/Overload' % phase))) + return safe_max(inverterOverload) + return v + +# The 'Stop on AC [1/2] conditions are disabled for the Multi RS (acsystem) +# The 'stop on AC' condition stops the generator, which is connected to one AC input when there is AC detected on the other. +# Since the Multi RS only has one AC input, these conditions cannot be used. +class StopOnAc1Condition(Condition): + name = 'stoponac1' + monitoring = 'vebus' + boolean = True + timed = False + + def get_value(self): + # AC input 1 + if self.multi_service_type == 'vebus': + available = self.monitor.get_value(self.multi_service, + '/Ac/State/AcIn1Available') + if available is None: + # Not supported in firmware, fall back to old behaviour + activein = self.monitor.get_value(self.multi_service, + '/Ac/ActiveIn/ActiveInput') + + # Active input is connected + connected = self.monitor.get_value(self.multi_service, + '/Ac/ActiveIn/Connected') + if None not in (activein, connected): + return activein == 0 and connected == 1 + return None + + return bool(available) + else: + # Disabled + return False + +# The StopOnAc2 condition cannot have the fallback to evaluating based on`/Ac/ActiveIn/ActiveInput` + `/Ac/ActiveIn/Connected` +# Because these paths were present in the vebus firmware before `/Ac/State/AcIn2Available` was, +# but switching over to AC input 2 was not supported in that firmware version. +# So if the fallback were implemented for this condition as well, it would stop the +# generator when AC in 2 becomes available but the quattro would not switch over. +class StopOnAc2Condition(Condition): + name = 'stoponac2' + monitoring = 'vebus' + boolean = True + timed = False + + def get_value(self): + if self.multi_service_type == 'vebus': + # AC input 2 available (used when grid is on AC-in-2) + available = self.monitor.get_value(self.multi_service, + '/Ac/State/AcIn2Available') + + return None if available is None else bool(available) + else: + # Disabled + return False + +class Battery(object): + def __init__(self, monitor, service, prefix): + self.monitor = monitor + self.service = service + self.prefix = prefix + + @property + def voltage(self): + return self.monitor.get_value(self.service, self.prefix + '/Voltage') + + @property + def current(self): + return self.monitor.get_value(self.service, self.prefix + '/Current') + + @property + def soc(self): + # Soc from the device doesn't have the '/Dc/0' prefix like the current and voltage do, but it does + # have the same prefix on systemcalc + return self.monitor.get_value(self.service, (BATTERY_PREFIX if self.prefix == BATTERY_PREFIX else '') + '/Soc') + +class StartStop(object): + _driver = None + def __init__(self, instance): +#### GuiMods #### TODO: check if any of these are needed + logging.info ("GuiMods version of startstop.py") + self._currentTime = self._get_monotonic_seconds() + self._last_update_mtime = 0 + self._accumulatedRunTime = 0 + self._lastIsRunning = False + self._externalOverrideDelay = 99 + self._linkToExternalState = False +#### GuiMods warm-up / cool-down + self._warmUpEndTime = 0 + self._coolDownEndTime = 0 + self._ac1isIgnored = False + self._ac2isIgnored = False + self._activeAcInIsIgnored = False + self._acInIsGenerator = False +#### end GuiMods + + self._dbusservice = None + self._settings = None + self._dbusmonitor = None + self._remoteservice = None + self._name = None + self._enabled = False + self._generator_running = False + self._useGensetHours = False # Sync with genset operatinghours. + self._instance = instance + + # One second per retry + self.RETRIES_ON_ERROR = 300 + self._testrun_soc_retries = 0 + self._last_counters_check = 0 + + # Two different starttime values. + # starttime_fb is set by the modules (relay.py, genset.py) and will be set to the current time when + # the feedback mechanism (digital input / `/StatusCode`) indicates that the generator is running. + # The other one is set by startstop when commanding the module to start the generator and is used by the + # warm-up mechanism to ensure warm-up finishes without needing feedback that the generator has actually started. + # If there is no feedback mechanism in place, the values will be equal as the module will call '_generator_started()` + # right after receiving the start command from startstop. + self._starttime_fb = 0 # Starttime of the generator as reported by the feedback mechanism (e.g., digital input), if present + self._starttime = 0 # Starttime of the generator, maintained by startstop. Not influenced by feedback mechanism. + self._stoptime = 0 # Used for cooldown + self._manualstarttimer = 0 + self._last_runtime_update = 0 + self._timer_runnning = 0 + + # The installer left autostart disabled + self._autostart_last_time = self._currentTime + self._remote_start_mode_last_time = self._currentTime + + + # Manual battery service selection is deprecated in favour + # of getting the values directly from systemcalc, we keep + # manual selected services handling for compatibility reasons. + self._vebusservice = None + self._acsystemservice = None + self._errorstate = 0 + self._battery_service = None + self._battery_prefix = None + + self._power_input_timer = { + 'timeout': 0, + 'unabletostart': False + } + + # Order is important. Conditions are evaluated in the order listed. + self._condition_stack = OrderedDict({ + SocCondition.name: SocCondition(self), + AcLoadCondition.name: AcLoadCondition(self), + BatteryCurrentCondition.name: BatteryCurrentCondition(self), + BatteryVoltageCondition.name: BatteryVoltageCondition(self), + InverterTempCondition.name: InverterTempCondition(self), + InverterOverloadCondition.name: InverterOverloadCondition(self), + StopOnAc1Condition.name: StopOnAc1Condition(self), + StopOnAc2Condition.name: StopOnAc2Condition(self) + }) + + # Return the active vebusservice or the acsystemservice. + @property + def multiservice(self): + return self._vebusservice if self._vebusservice else self._acsystemservice if self._acsystemservice else None + + @property + def multiservice_type(self): + return self.multiservice.split('.')[2] if self.multiservice is not None else None + + def set_sources(self, dbusmonitor, settings, name, remoteservice): + self._settings = SettingsPrefix(settings, name) + self._dbusmonitor = dbusmonitor + self._remoteservice = remoteservice + self._name = name + + self.log_info('Start/stop instance created for %s.' % self._remoteservice) + self._remote_setup() + + def _create_service(self): + self._dbusservice = self._create_dbus_service() + + # The driver used for this start/stop service + self._dbusservice.add_path('/Type', value=self._driver) + # State: None = invalid, 0 = stopped, 1 = running, 2=Warm-up, 3=Cool-down + self._dbusservice.add_path('/State', value=None, gettextcallback=lambda p, v: States.get_description(v)) + self._dbusservice.add_path('/Enabled', value=1) + # RunningByConditionCode: Numeric Companion to /RunningByCondition below, but + # also encompassing a Stopped state. + self._dbusservice.add_path('/RunningByConditionCode', value=None) + # Error + self._dbusservice.add_path('/Error', value=None, gettextcallback=lambda p, v: Errors.get_description(v)) + # Condition that made the generator start + self._dbusservice.add_path('/RunningByCondition', value=None) + # Runtime + self._dbusservice.add_path('/Runtime', value=None, gettextcallback=self._seconds_to_text) + # Today runtime + self._dbusservice.add_path('/TodayRuntime', value=None, gettextcallback=self._seconds_to_text) + # Test run runtime + self._dbusservice.add_path('/TestRunIntervalRuntime', value=None , gettextcallback=self._seconds_to_text) + # Next test run date, values is 0 for test run disabled + self._dbusservice.add_path('/NextTestRun', value=None, gettextcallback=lambda p, v: datetime.datetime.fromtimestamp(v).strftime('%c')) + # Next test run is needed 1, not needed 0 + self._dbusservice.add_path('/SkipTestRun', value=None) + # Manual start + self._dbusservice.add_path('/ManualStart', value=None, writeable=True) + # Manual start timer + self._dbusservice.add_path('/ManualStartTimer', value=None, writeable=True) + # Silent mode active + self._dbusservice.add_path('/QuietHours', value=None) + # Alarms + self._dbusservice.add_path('/Alarms/NoGeneratorAtAcIn', value=None) + self._dbusservice.add_path('/Alarms/NoGeneratorAtDcIn', value=None) + self._dbusservice.add_path('/Alarms/ServiceIntervalExceeded', value=None) + self._dbusservice.add_path('/Alarms/AutoStartDisabled', value=None) + self._dbusservice.add_path('/Alarms/RemoteStartModeDisabled', value=None) + # Autostart + self._dbusservice.add_path('/AutoStartEnabled', value=None, writeable=True, onchangecallback=self._set_autostart) + # Accumulated runtime + self._dbusservice.add_path('/AccumulatedRuntime', value=None) + # Service interval + self._dbusservice.add_path('/ServiceInterval', value=None) + # Capabilities, where we can add bits + self._dbusservice.add_path('/Capabilities', value=0) + # Service countdown, calculated by running time and service interval + self._dbusservice.add_path('/ServiceCounter', value=None) + self._dbusservice.add_path('/ServiceCounterReset', value=None, writeable=True, onchangecallback=self._reset_service_counter) + # Publish what service we're controlling, and the productid + self._dbusservice.add_path('/GensetService', value=self._remoteservice) + self._dbusservice.add_path('/GensetServiceType', + value=self._remoteservice.split('.')[2] if self._remoteservice is not None else None) + self._dbusservice.add_path('/GensetInstance', + value=self._dbusmonitor.get_value(self._remoteservice, '/DeviceInstance')) + self._dbusservice.add_path('/GensetProductId', + value=self._dbusmonitor.get_value(self._remoteservice, '/ProductId')) + self._dbusservice.add_path('/DigitalInput/Running', value=None, writeable=True, onchangecallback=self._running_by_digital_input) + self._dbusservice.add_path('/DigitalInput/Input', value=None, writeable=True, onchangecallback=self._running_by_digital_input) + + self._dbusservice.register() + # We need to set the values after creating the paths to trigger the 'onValueChanged' event for the gui + # otherwise the gui will report the paths as invalid if we remove and recreate the paths without + # restarting the dbusservice. + self._dbusservice['/State'] = 0 + self._dbusservice['/RunningByConditionCode'] = RunningConditions.Stopped + self._dbusservice['/Error'] = 0 + self._dbusservice['/RunningByCondition'] = '' + self._dbusservice['/Runtime'] = 0 + self._dbusservice['/TodayRuntime'] = 0 + self._dbusservice['/TestRunIntervalRuntime'] = self._interval_runtime(self._settings['testruninterval']) + self._dbusservice['/NextTestRun'] = None + self._dbusservice['/SkipTestRun'] = None + self._dbusservice['/ProductName'] = "Generator start/stop" + self._dbusservice['/ManualStart'] = 0 + self._dbusservice['/ManualStartTimer'] = 0 + self._dbusservice['/QuietHours'] = 0 + self._dbusservice['/Alarms/NoGeneratorAtAcIn'] = 0 + self._dbusservice['/Alarms/ServiceIntervalExceeded'] = 0 + self._dbusservice['/Alarms/AutoStartDisabled'] = 0 # GX auto start/stop + self._dbusservice['/Alarms/RemoteStartModeDisabled'] = 0 # Genset remote start mode + self._dbusservice['/AutoStartEnabled'] = self._settings['autostart'] + self._dbusservice['/AccumulatedRuntime'] = int(self._settings['accumulatedtotal']) + self._dbusservice['/ServiceInterval'] = int(self._settings['serviceinterval']) + self._dbusservice['/ServiceCounter'] = None + self._dbusservice['/ServiceCounterReset'] = 0 + self._dbusservice['/DigitalInput/Running'] = 0 + self._dbusservice['/DigitalInput/Input'] = 0 + + # When this startstop instance controls a genset which reports operatinghours, make sure to synchronize with that. + self._useGensetHours = self._dbusmonitor.get_value(self._remoteservice, '/Engine/OperatingHours', None) is not None + +#### GuiMods + # generator input running state + # external override active + self._dbusservice.add_path('/ExternalOverride', value=None) + self._dbusservice['/ExternalOverride'] = False + self._ignoreAutoStartCondition = False + + + @property + def _is_running(self): + return self._generator_running + + @property + def capabilities(self): + return self._dbusservice['/Capabilities'] + + def _set_autostart(self, path, value): + if 0 <= value <= 1: + self._settings['autostart'] = int(value) + return True + return False + + def enable(self): + if self._enabled: + return + self.log_info('Enabling auto start/stop and taking control of remote switch') + self._create_service() + self._determineservices() + self._update_remote_switch() + # If cooldown or warmup is enabled, the Quattro may be left in a bad + # state if there is an unfortunate crash or a reboot. Set the ignore_ac + # flag to a sane value on startup. + if self._settings['cooldowntime'] > 0 or \ + self._settings['warmuptime'] > 0: + self._set_ignore_ac(False) + self._enabled = True + + def disable(self): + if not self._enabled: + return + self.log_info('Disabling auto start/stop, releasing control of remote switch') + self._remove_service() + self._enabled = False + + def remove(self): + self.disable() + self.log_info('Removed from start/stop instances') + + def _remove_service(self): + self._dbusservice.__del__() + self._dbusservice = None + + def device_added(self, dbusservicename, instance): + self._determineservices() + + def device_removed(self, dbusservicename, instance): + self._determineservices() + + def get_error(self): + return self._dbusservice['/Error'] + + def set_error(self, errorn): + self._dbusservice['/Error'] = errorn + + def clear_error(self): + self._dbusservice['/Error'] = Errors.NONE + + def dbus_value_changed(self, dbusServiceName, dbusPath, options, changes, deviceInstance): + if self._dbusservice is None: + return + + # AcIn1Available is needed to determine capabilities, but may + # only show up later. So we have to wait for it here. + if self.multiservice is not None and \ + dbusServiceName == self.multiservice and \ + dbusPath == '/Ac/State/AcIn1Available' or dbusPath == '/Ac/Control/IgnoreAcIn1': + self._set_capabilities() + + # If gensethours is updated, update the accumulated time. + if dbusPath == '/Engine/OperatingHours' and self._useGensetHours: + self._update_accumulated_time(gensetHours=changes['Value']) + + if dbusServiceName != 'com.victronenergy.system': + return + if dbusPath == '/AutoSelectedBatteryMeasurement' and self._settings['batterymeasurement'] == 'default': + self._determineservices() + + if dbusPath == '/VebusService': + self._determineservices() + + def handlechangedsetting(self, setting, oldvalue, newvalue): + if self._dbusservice is None: + return + if self._name not in setting: + # Not our setting + return + + s = self._settings.removeprefix(setting) + + if s == 'batterymeasurement': + self._determineservices() + # Reset retries and valid if service changes + for condition in self._condition_stack.values(): + if condition['monitoring'] == 'battery': + condition['valid'] = True + condition['retries'] = 0 + + if s == 'autostart': + self.log_info('Autostart function %s.' % ('enabled' if newvalue == 1 else 'disabled')) + self._dbusservice['/AutoStartEnabled'] = self._settings['autostart'] + + if self._dbusservice is not None and s == 'testruninterval': + self._dbusservice['/TestRunIntervalRuntime'] = self._interval_runtime( + self._settings['testruninterval']) + + if s == 'serviceinterval': + try: + self._dbusservice['/ServiceInterval'] = int(newvalue) + except TypeError: + pass + if newvalue == 0: + self._dbusservice['/ServiceCounter'] = None + else: + self._update_accumulated_time() + if s == 'lastservicereset': + self._update_accumulated_time() + + def _reset_service_counter(self, path, value): + if (path == '/ServiceCounterReset' and value == int(1) and self._dbusservice['/AccumulatedRuntime']): + self._settings['lastservicereset'] = self._dbusservice['/AccumulatedRuntime'] + self._update_accumulated_time() + self.log_info('Service counter reset triggered.') + + return True + + def _seconds_to_text(self, path, value): + m, s = divmod(value, 60) + h, m = divmod(m, 60) + return '%dh, %dm, %ds' % (h, m, s) + + def log_info(self, msg): + logging.info(self._name + ': %s' % msg) + + def tick(self): + if not self._enabled: + return + +#### GuiMods warm-up / cool-down + self._currentTime = self._get_monotonic_seconds () + + self._check_remote_status() +#### GuiMods + self._linkToExternalState = self._settings['linkManualStartToExternal'] == 1 + self.syncManualRunToExternalState () + + self._evaluate_startstop_conditions() + self._evaluate_autostart_disabled_alarm() + self._detect_generator_at_input() + if self._dbusservice['/ServiceCounterReset'] == 1: + self._dbusservice['/ServiceCounterReset'] = 0 + +#### GuiMods warm-up / cool-down + + # shed load for active generator input in warm-up and cool-down + # note that external transfer switch might change the state of on generator + # so this needs to be checked and load adjusted every pass + # restore load for sources no longer in use or if state is not in warm-up/cool-down + # restoring load is delayed 1following end of cool-down + # to allow the generator to actually stop producing power + state = self._dbusservice['/State'] + if state in (States.WARMUP, States.COOLDOWN, States.STOPPING): + self._set_ignore_ac (True) + else: + self._set_ignore_ac (False) + + # update cool down end time while running and generator has the load + # this is done because acInIsGenerator may change by an external transfer switch + # and we want an accurate picture of the cool down end time + # based on the last time the generatot was loaded + if state == States.RUNNING and self._acInIsGenerator: + self._coolDownEndTime = self._currentTime + self._settings['cooldowntime'] +#### end GuiMods warm-up / cool-down + + + def _evaluate_startstop_conditions(self): + if self.get_error() != Errors.NONE: + # First evaluation after an error, log it + if self._errorstate == 0: + self._errorstate = 1 + self._dbusservice['/State'] = States.ERROR + self.log_info('Error: #%i - %s, stop controlling remote.' % + (self.get_error(), + Errors.get_description(self.get_error()))) + elif self._errorstate == 1: + # Error cleared + self._errorstate = 0 + self._dbusservice['/State'] = States.STOPPED + self.log_info('Error state cleared, taking control of remote switch.') + + start = False + startbycondition = None + activecondition = self._dbusservice['/RunningByCondition'] + today = calendar.timegm(datetime.date.today().timetuple()) + self._timer_runnning = False + connection_lost = False + running = self._dbusservice['/State'] in (States.RUNNING, States.WARMUP) + + self._check_quiet_hours() + + # New day, register it + if self._last_counters_check < today and self._dbusservice['/State'] == States.STOPPED: + self._last_counters_check = today + self._update_accumulated_time() + + self._update_runtime() + +#### GuiMods + # A negative /ManualStartTimer is used by the GUI to signal the generator should start now + # but stop when all auto stop conditions have been met + # so we skip manual start evaluation if this is the case + # and set a flag for use below to ignore auto start conditions + # the generator is actually started by the auto start/stop logic below + if self._dbusservice['/ManualStartTimer'] < 0 and self._dbusservice['/ManualStart'] == 1: + self._dbusservice['/ManualStartTimer'] = 0 + self._dbusservice['/ManualStart'] = 0 + self._ignoreAutoStartCondition = True + + else: + self._ignoreAutoStartCondition = False + if self._evaluate_manual_start(): + startbycondition = 'manual' + start = True +#### end GuiMods + + # Conditions will only be evaluated if the autostart functionality is enabled + if self._settings['autostart'] == 1: + + if self._evaluate_testrun_condition(): + startbycondition = 'testrun' + start = True + + # Evaluate stop on AC IN conditions first, when this conditions are enabled and reached the generator + # will stop as soon as AC IN in active. Manual and testrun conditions will make the generator start + # or keep it running. + stop_on_ac_reached = (self._evaluate_condition(self._condition_stack[StopOnAc1Condition.name]) or + self._evaluate_condition(self._condition_stack[StopOnAc2Condition.name])) + stop_by_ac1_ac2 = startbycondition not in ['manual', 'testrun'] and stop_on_ac_reached + + if stop_by_ac1_ac2 and running and activecondition not in ['manual', 'testrun']: + self.log_info('AC input available, stopping') + + # Evaluate value conditions + for condition, data in self._condition_stack.items(): + # Do not evaluate rest of conditions if generator is configured to stop + # when AC IN is available + if stop_by_ac1_ac2: + start = False + if running: + self._reset_condition(data) + continue + else: + break + + # Don't short-circuit this, _evaluate_condition sets .reached + start = self._evaluate_condition(data) or start + startbycondition = condition if start and startbycondition is None else startbycondition + # Connection lost is set to true if the number of retries of one or more enabled conditions + # >= RETRIES_ON_ERROR + if data.enabled: + connection_lost = data.retries >= self.RETRIES_ON_ERROR + + # If none condition is reached check if connection is lost and start/keep running the generator + # depending on '/OnLossCommunication' setting + if not start and connection_lost: + # Start always + if self._settings['onlosscommunication'] == 1: + start = True + startbycondition = 'lossofcommunication' + # Keep running if generator already started + if running and self._settings['onlosscommunication'] == 2: + start = True + startbycondition = 'lossofcommunication' + +#### GuiMods + ## auto start disabled and generator is stopped - clear the 'reached' flags + elif self._dbusservice['/State'] == States.STOPPED: + for condition, data in self._condition_stack.items(): + self._reset_condition(data) + + if not start and self._errorstate: + self._stop_generator() + + if self._errorstate: + return + + if start: + self._start_generator(startbycondition) +#### GuiMods + # bypass the minimum run time check if External Override is active + elif (self._dbusservice['/Runtime'] >= self._settings['minimumruntime'] * 60 + or activecondition == 'manual') or self._dbusservice['/ExternalOverride']: + self._stop_generator() +#### end GuiMods + + def _update_runtime(self, just_stopped=False): + # Update current and accumulated runtime. + # By performance reasons, accumulated runtime is only updated + # once per 60s. When the generator stops is also updated. + if self._is_running or just_stopped: + mtime = monotonic_time.monotonic_time().to_seconds_double() + if (mtime - self._starttime_fb) - self._last_runtime_update >= 60 or just_stopped: + self._dbusservice['/Runtime'] = int(mtime - self._starttime_fb) + self._update_accumulated_time() + elif self._last_runtime_update == 0: + self._dbusservice['/Runtime'] = int(mtime - self._starttime_fb) + + def _evaluate_autostart_disabled_alarm(self): + + if self._settings['autostartdisabledalarm'] == 0: + self._autostart_last_time = self._currentTime + self._remote_start_mode_last_time = self._currentTime + if self._dbusservice['/Alarms/AutoStartDisabled'] != 0: + self._dbusservice['/Alarms/AutoStartDisabled'] = 0 + if self._dbusservice['/Alarms/RemoteStartModeDisabled'] != 0: + self._dbusservice['/Alarms/RemoteStartModeDisabled'] = 0 + return + + # GX auto start/stop alarm + if self._settings['autostart'] == 1: + self._autostart_last_time = self._currentTime + if self._dbusservice['/Alarms/AutoStartDisabled'] != 0: + self._dbusservice['/Alarms/AutoStartDisabled'] = 0 + else: + timedisabled = self._currentTime - self._autostart_last_time + if timedisabled > AUTOSTART_DISABLED_ALARM_TIME and self._dbusservice['/Alarms/AutoStartDisabled'] != 2: + self.log_info("Autostart was left for more than %i seconds, triggering alarm." % int(timedisabled)) + self._dbusservice['/Alarms/AutoStartDisabled'] = 2 + + # Genset remote start mode alarm + if self.get_error() != Errors.REMOTEDISABLED: + self._remote_start_mode_last_time = self._currentTime + if self._dbusservice['/Alarms/RemoteStartModeDisabled'] != 0: + self._dbusservice['/Alarms/RemoteStartModeDisabled'] = 0 + else: + timedisabled = self._currentTime - self._remote_start_mode_last_time + if timedisabled > AUTOSTART_DISABLED_ALARM_TIME and self._dbusservice['/Alarms/RemoteStartModeDisabled'] != 2: + self.log_info("Autostart was left for more than %i seconds, triggering alarm." % int(timedisabled)) + self._dbusservice['/Alarms/RemoteStartModeDisabled'] = 2 + +#### GuiMods warm-up / cool-down - rewrote so acInIsGenerator is updated even if alarm is disabled + def _detect_generator_at_input(self): + self._acInIsGenerator = False # covers all conditions that result in a return + + state = self._dbusservice['/State'] + if state in [States.STOPPED, States.COOLDOWN, States.WARMUP]: + self._reset_power_input_timer() + return + + if self._settings['nogeneratoratacinalarm'] == 0: + self._reset_acpower_inverter_input() + return + + if self.multiservice_type == 'vebus': + activein_state = self._dbusmonitor.get_value( + self.multiservice, '/Ac/ActiveIn/Connected') + else: + active_input = self._dbusmonitor.get_value( + self.multiservice, '/Ac/ActiveIn/ActiveInput') + activein_state = None if active_input is None else 1 if 0 <= active_input <= 1 else 0 + + # Path not supported, skip evaluation + if activein_state == None: + return + + # Sources 0 = Not available, 1 = Grid, 2 = Generator, 3 = Shore + generator_acsource = self._dbusmonitor.get_value( + SYSTEM_SERVICE, '/Ac/ActiveIn/Source') == 2 + # Not connected = 0, connected = 1 + activein_connected = activein_state == 1 + +#### GuiMods warm-up / cool-down + if self._settings['nogeneratoratacinalarm'] == 0: + processAlarm = False + self._reset_acpower_inverter_input() + else: + processAlarm = True + + if generator_acsource and activein_connected: +#### GuiMods warm-up / cool-down + self._acInIsGenerator = True +#### GuiMods warm-up / cool-down + if processAlarm and self._power_input_timer['unabletostart']: + self.log_info('Generator detected at inverter AC input, alarm removed') + self._reset_power_inverter_input() +#### GuiMods warm-up / cool-down + elif not processAlarm: + self._reset_power_inverter_input() + return + elif self._aower_inverter_input['timeout'] < self.RETRIES_ON_ERROR: + self._power_inverter_input['timeout'] += 1 + elif not self._power_inverter_input['unabletostart']: + self._power_inverter_input['unabletostart'] = True + self._dbusservice['/Alarms/NoGeneratorAtAcIn'] = 2 + self.log_info('Generator not detected at inverter AC input, triggering alarm') +#### end GuiMods + + def _reset_power_input_timer(self): + if self._acpower_inverter_input['timeout'] != 0: + self._acpower_inverter_input['timeout'] = 0 + + if self._acpower_inverter_input['unabletostart'] != 0: + self._acpower_inverter_input['unabletostart'] = 0 + + self._dbusservice['/Alarms/NoGeneratorAtAcIn'] = 0 + + def _reset_condition(self, condition): + condition['reached'] = False + if condition['timed']: + condition['start_timer'] = 0 + condition['stop_timer'] = 0 + + def _check_condition(self, condition, value): + name = condition['name'] + + if self._settings[name + 'enabled'] == 0: + if condition['enabled']: + condition['enabled'] = False + self.log_info('Disabling (%s) condition' % name) + condition['retries'] = 0 + condition['valid'] = True + self._reset_condition(condition) + return False + + elif not condition['enabled']: + condition['enabled'] = True + self.log_info('Enabling (%s) condition' % name) + + if (condition['monitoring'] == 'battery') and (self._settings['batterymeasurement'] == 'nobattery'): + # If no battery monitor is selected reset the condition + self._reset_condition(condition) + return False + + if value is None and condition['valid']: + if condition['retries'] >= self.RETRIES_ON_ERROR: + logging.info('Error getting (%s) value, skipping evaluation till get a valid value' % name) + self._reset_condition(condition) + self._comunnication_lost = True + condition['valid'] = False + else: + condition['retries'] += 1 + if condition['retries'] == 1 or (condition['retries'] % 10) == 0: + self.log_info('Error getting (%s) value, retrying(#%i)' % (name, condition['retries'])) + return False + + elif value is not None and not condition['valid']: + self.log_info('Success getting (%s) value, resuming evaluation' % name) + condition['valid'] = True + condition['retries'] = 0 + + # Reset retries if value is valid + if value is not None and condition['retries'] > 0: + self.log_info('Success getting (%s) value, resuming evaluation' % name) + condition['retries'] = 0 + + return condition['valid'] + + def _evaluate_condition(self, condition): + name = condition['name'] + value = condition.get_value() + setting = ('qh_' if self._dbusservice['/QuietHours'] == 1 else '') + name + startvalue = self._settings[setting + 'start'] if not condition['boolean'] else 1 + stopvalue = self._settings[setting + 'stop'] if not condition['boolean'] else 0 + + # Check if the condition has to be evaluated + if not self._check_condition(condition, value): + # If generator is started by this condition and value is invalid + # wait till RETRIES_ON_ERROR to skip the condition + if condition['reached'] and condition['retries'] <= self.RETRIES_ON_ERROR: + if condition['retries'] > 0: + return True + + return False + + # As this is a generic evaluation method, we need to know how to compare the values + # first check if start value should be greater than stop value and then compare + start_is_greater = startvalue > stopvalue + + # When the condition is already reached only the stop value can set it to False + start = condition['reached'] or (value >= startvalue if start_is_greater else value <= startvalue) + stop = value <= stopvalue if start_is_greater else value >= stopvalue + +#### GuiMods + stop = value <= stopvalue if start_is_greater else value >= stopvalue + # when starting manually and stopping based on auto stop values, + # start if stop condition is not satisfied + + if self._ignoreAutoStartCondition: + start = not stop + else: + # When the condition is already reached only the stop value can set it to False + start = condition['reached'] or (value >= startvalue if start_is_greater else value <= startvalue) +#### end GuiMods + + # Timed conditions must start/stop after the condition has been reached for a minimum + # time. + if condition['timed']: + if not condition['reached'] and start: + condition['start_timer'] += time.time() if condition['start_timer'] == 0 else 0 + start = time.time() - condition['start_timer'] >= self._settings[name + 'starttimer'] + condition['stop_timer'] *= int(not start) + self._timer_runnning = True + else: + condition['start_timer'] = 0 + + if condition['reached'] and stop: + condition['stop_timer'] += time.time() if condition['stop_timer'] == 0 else 0 + stop = time.time() - condition['stop_timer'] >= self._settings[name + 'stoptimer'] + condition['stop_timer'] *= int(not stop) + self._timer_runnning = True + else: + condition['stop_timer'] = 0 + + condition['reached'] = start and not stop + return condition['reached'] + + def _evaluate_manual_start(self): + if self._dbusservice['/ManualStart'] == 0: + if self._dbusservice['/RunningByCondition'] == 'manual': + self._dbusservice['/ManualStartTimer'] = 0 + return False + + start = True + # If /ManualStartTimer has a value greater than zero will use it to set a stop timer. + # If no timer is set, the generator will not stop until the user stops it manually. + # Once started by manual start, each evaluation the timer is decreased +#### GuiMods - change test to > 0 from != 0 to allow for start now / auto stop + if self._dbusservice['/ManualStartTimer'] > 0: + self._manualstarttimer += time.time() if self._manualstarttimer == 0 else 0 + self._dbusservice['/ManualStartTimer'] -= int(time.time()) - int(self._manualstarttimer) + self._manualstarttimer = time.time() + start = self._dbusservice['/ManualStartTimer'] > 0 + self._dbusservice['/ManualStart'] = int(start) + # Reset if timer is finished + self._manualstarttimer *= int(start) + self._dbusservice['/ManualStartTimer'] *= int(start) + + return start + + def _evaluate_testrun_condition(self): + if self._settings['testrunenabled'] == 0: + self._dbusservice['/SkipTestRun'] = None + self._dbusservice['/NextTestRun'] = None + return False + + today = datetime.date.today() + yesterday = today - datetime.timedelta(days=1) # Should deal well with DST + now = time.time() + runtillbatteryfull = self._settings['testruntillbatteryfull'] == 1 + soc = self._condition_stack['soc'].get_value() + batteryisfull = runtillbatteryfull and soc == 100 + duration = 60 if runtillbatteryfull else self._settings['testrunruntime'] + + try: + startdate = datetime.date.fromtimestamp(self._settings['testrunstartdate']) + _starttime = time.mktime(yesterday.timetuple()) + self._settings['testrunstarttimer'] + + # today might in fact still be yesterday, if this test run started + # before midnight and finishes after. If `now` still falls in + # yesterday's window, then by the temporal anthropic principle, + # which I just made up but loosely states that time must have + # these properties for observers to exist, it must be yesterday + # because we are here to observe it. + if _starttime <= now <= _starttime + duration: + today = yesterday + starttime = _starttime + else: + starttime = time.mktime(today.timetuple()) + self._settings['testrunstarttimer'] + except ValueError: + logging.debug('Invalid dates, skipping testrun') + return False + + # If start date is in the future set as NextTestRun and stop evaluating + if startdate > today: + self._dbusservice['/NextTestRun'] = time.mktime(startdate.timetuple()) + return False + + start = False + # If the accumulated runtime during the test run interval is greater than '/TestRunIntervalRuntime' + # the test run must be skipped + needed = (self._settings['testrunskipruntime'] > self._dbusservice['/TestRunIntervalRuntime'] + or self._settings['testrunskipruntime'] == 0) + self._dbusservice['/SkipTestRun'] = int(not needed) + + interval = self._settings['testruninterval'] + stoptime = starttime + duration + elapseddays = (today - startdate).days + mod = elapseddays % interval + + start = not bool(mod) and starttime <= now <= stoptime + + if runtillbatteryfull: + if soc is not None: + self._testrun_soc_retries = 0 + start = (start or self._dbusservice['/RunningByCondition'] == 'testrun') and not batteryisfull + elif self._dbusservice['/RunningByCondition'] == 'testrun': + if self._testrun_soc_retries < self.RETRIES_ON_ERROR: + self._testrun_soc_retries += 1 + start = True + if (self._testrun_soc_retries % 10) == 0: + self.log_info('Test run failed to get SOC value, retrying(#%i)' % self._testrun_soc_retries) + else: + self.log_info('Failed to get SOC after %i retries, terminating test run condition' % self._testrun_soc_retries) + start = False + else: + start = False + + if not bool(mod) and (now <= stoptime): + self._dbusservice['/NextTestRun'] = starttime + else: + self._dbusservice['/NextTestRun'] = (time.mktime((today + datetime.timedelta(days=interval - mod)).timetuple()) + + self._settings['testrunstarttimer']) + return start and needed + + def _check_quiet_hours(self): + active = False + if self._settings['quiethoursenabled'] == 1: + # Seconds after today 00:00 + timeinseconds = time.time() - time.mktime(datetime.date.today().timetuple()) + quiethoursstart = self._settings['quiethoursstarttime'] + quiethoursend = self._settings['quiethoursendtime'] + + # Check if the current time is between the start time and end time + if quiethoursstart < quiethoursend: + active = quiethoursstart <= timeinseconds and timeinseconds < quiethoursend + else: # End time is lower than start time, example Start: 21:00, end: 08:00 + active = not (quiethoursend < timeinseconds and timeinseconds < quiethoursstart) + + if self._dbusservice['/QuietHours'] == 0 and active: + self.log_info('Entering to quiet mode') + + elif self._dbusservice['/QuietHours'] == 1 and not active: + self.log_info('Leaving quiet mode') + + self._dbusservice['/QuietHours'] = int(active) + + return active + + def _update_accumulated_time(self, gensetHours=None): + + # Check if this instance is connected to a genset which reports operating hours. + # If so, synchronize with that. + if (self._useGensetHours): + if (gensetHours is None): + gensetHours = self._dbusmonitor.get_value(self._remoteservice, '/Engine/OperatingHours', None) + + # Failsafe + if (gensetHours is not None): + # If connected genset reports /Engine/OperatingHours, use that and also clear the offset value. + self._settings['accumulatedtotalOffset'] = 0 + self._settings['accumulatedtotal'] = accumulatedtotal = gensetHours + else: + # Do not use genset hours + gensetHours = None + + seconds = self._dbusservice['/Runtime'] + accumulated = seconds - self._last_runtime_update + + self._settings['accumulatedtotal'] = accumulatedtotal = gensetHours or int(self._settings['accumulatedtotal']) + accumulated + # Using calendar to get timestamp in UTC, not local time + today_date = str(calendar.timegm(datetime.date.today().timetuple())) + + # If something goes wrong getting the json string create a new one + try: + accumulated_days = json.loads(self._settings['accumulateddaily']) + except ValueError: + accumulated_days = {today_date: 0} + + if (today_date in accumulated_days): + accumulated_days[today_date] += accumulated + else: + accumulated_days[today_date] = accumulated + + if self._dbusservice['/State'] in (States.RUNNING, States.WARMUP, States.COOLDOWN, States.STOPPING): + mtime = monotonic_time.monotonic_time().to_seconds_double() + self._dbusservice['/Runtime'] = int(mtime - self._starttime_fb) + + self._last_runtime_update = seconds + + # Keep the historical with a maximum of HISTORY_DAYS + while len(accumulated_days) > HISTORY_DAYS: + accumulated_days.pop(min(accumulated_days.keys()), None) + + # Update settings + self._settings['accumulateddaily'] = json.dumps(accumulated_days, sort_keys=True) + self._dbusservice['/TodayRuntime'] = self._interval_runtime(0) + self._dbusservice['/TestRunIntervalRuntime'] = self._interval_runtime(self._settings['testruninterval']) + self._dbusservice['/AccumulatedRuntime'] = accumulatedtotal + + # Service counter + serviceinterval = self._settings['serviceinterval'] + lastservicereset = self._settings['lastservicereset'] + if serviceinterval > 0: + servicecountdown = (lastservicereset + serviceinterval) - accumulatedtotal + self._dbusservice['/ServiceCounter'] = servicecountdown + if servicecountdown <= 0: + self._dbusservice['/Alarms/ServiceIntervalExceeded'] = 1 + elif self._dbusservice['/Alarms/ServiceIntervalExceeded'] != 0: + self._dbusservice['/Alarms/ServiceIntervalExceeded'] = 0 + + + + def _interval_runtime(self, days): + summ = 0 + try: + daily_record = json.loads(self._settings['accumulateddaily']) + except ValueError: + return 0 + + for i in range(days + 1): + previous_day = calendar.timegm((datetime.date.today() - datetime.timedelta(days=i)).timetuple()) + if str(previous_day) in daily_record.keys(): + summ += daily_record[str(previous_day)] if str(previous_day) in daily_record.keys() else 0 + + return summ + + def _get_battery(self): + if self._settings['batterymeasurement'] == 'default': + return Battery(self._dbusmonitor, SYSTEM_SERVICE, BATTERY_PREFIX) + + return Battery(self._dbusmonitor, + self._battery_service if self._battery_service else '', + self._battery_prefix if self._battery_prefix else '') + + def _set_capabilities(self): + # Update capabilities + # The ability to ignore AC1/AC2 came in at the same time as + # AC availability and is used to detect it here. + if self.multiservice_type == 'vebus': + readout_supported = self._dbusmonitor.get_value(self.multiservice, + '/Ac/State/AcIn1Available') is not None + self._dbusservice['/Capabilities'] |= ( + Capabilities.WarmupCooldown if readout_supported else 0) + # /Ac/State/AcIn1Available is not available on acsystem + # The vebus system also has the path '/Ac/Control/IgnoreAcIn1', but we can't use this to check for the + # capabilities because this path was already there before ignoring ac input 2 was supported in the vebus quattro. + # So if we were to use '/Ac/Control/IgnoreAcIn1' to set the capabilities on a vebus system, it would + # also be enabled on systems with older firmware which do not support it on input 2. + elif self.multiservice_type == 'acsystem': + ignore_ac_supported = self._dbusmonitor.get_value(self.multiservice, + '/Ac/Control/IgnoreAcIn1') is not None + self._dbusservice['/Capabilities'] |= ( + Capabilities.WarmupCooldown if ignore_ac_supported else 0) + + def _determineservices(self): + # batterymeasurement is either 'default' or 'com_victronenergy_battery_288/Dc/0'. + # In case it is set to default, we use the AutoSelected battery + # measurement, given by SystemCalc. + batterymeasurement = None + newbatteryservice = None + batteryprefix = '' + selectedbattery = self._settings['batterymeasurement'] + vebusservice = None + + if selectedbattery == 'default': + batterymeasurement = 'default' + elif len(selectedbattery.split('/', 1)) == 2: # Only very basic sanity checking.. + batterymeasurement = self._settings['batterymeasurement'] + elif selectedbattery == 'nobattery': + batterymeasurement = None + else: + # Exception: unexpected value for batterymeasurement + pass + + if batterymeasurement and batterymeasurement != 'default': + batteryprefix = '/' + batterymeasurement.split('/', 1)[1] + + # Get the current battery servicename + if self._battery_service: + oldservice = self._battery_service + else: + oldservice = None + + if batterymeasurement == 'default': + newbatteryservice = 'default' + elif batterymeasurement: + battery_instance = int(batterymeasurement.split('_', 3)[3].split('/')[0]) + service_type = None + + if 'vebus' in batterymeasurement: + service_type = 'vebus' + elif 'battery' in batterymeasurement: + service_type = 'battery' + + newbatteryservice = self._get_servicename_by_instance(battery_instance, service_type) + + if newbatteryservice and newbatteryservice != oldservice: + if selectedbattery == 'default': + self.log_info('Getting battery values from systemcalc.') + if selectedbattery == 'nobattery': + self.log_info('Battery monitoring disabled! Stop evaluating related conditions') + self._battery_service = None + self._battery_prefix = None + self.log_info('Battery service we need (%s) found! Using it for generator start/stop' % batterymeasurement) + self._battery_service = newbatteryservice + self._battery_prefix = batteryprefix + elif not newbatteryservice and newbatteryservice != oldservice: + self.log_info('Error getting battery service!') + self._battery_service = newbatteryservice + self._battery_prefix = batteryprefix + + # Get the default VE.Bus service + vebusservice = self._dbusmonitor.get_value('com.victronenergy.system', '/VebusService') + if vebusservice: + if self.multiservice != vebusservice: + self._vebusservice = vebusservice + self._acsystemservice = None + self._set_capabilities() + self.log_info('Vebus service (%s) found! Using it for generator start/stop' % vebusservice) + else: + acsystemservice = self._get_acsystem_service() + if acsystemservice: + if self.multiservice != acsystemservice: + self._acsystemservice = acsystemservice + self._vebusservice = None + self._set_capabilities() + self.log_info('AC system service (%s) found! Using it for generator start/stop' % acsystemservice) + else: + if self._vebusservice is not None: + self.log_info('Vebus service (%s) dissapeared! Stop evaluating related conditions' % self._vebusservice) + elif self._acsystemservice is not None: + self.log_info('AC system service (%s) dissapeared! Stop evaluating related conditions' % self._acsystemservice) + else: + self.log_info('Error getting Vebus or AC system service!') + self._vebusservice = None + self._acsystemservice = None + + def _get_acsystem_service(self): + services = self._dbusmonitor.get_service_list() + for service in services: + if service.startswith('com.victronenergy.acsystem'): + if self._dbusmonitor.get_value(service, '/DeviceInstance') == 0: + return service + return None + + def _get_servicename_by_instance(self, instance, service_type=None): + sv = None + services = self._dbusmonitor.get_service_list() + + for service in services: + if service_type and service_type not in service: + continue + + if services[service] == instance: + sv = service + break + + return sv + + def _get_monotonic_seconds(self): + return monotonic_time.monotonic_time().to_seconds_double() + + def _start_generator(self, condition): + state = self._dbusservice['/State'] + remote_running = self._get_remote_switch_state() + + # This function will start the generator in the case generator not + # already running. When differs, the RunningByCondition is updated + running = state in (States.WARMUP, States.COOLDOWN, States.STOPPING, States.RUNNING) + if not (running and remote_running): # STOPPED, ERROR + # There is an option to skip warm-up for the inverteroverload condition. +#### GuiMods warm-up / cool-down + self.log_info('Starting generator by %s condition' % condition) + # if there is a warmup time specified, always go through warm-up state + # regardless of AC input in use + warmUpPeriod = self._settings['warmuptime'] + if warmUpPeriod > 0: + self._warmUpEndTime = self._currentTime + warmUpPeriod + self.log_info ("starting warm-up") + self._dbusservice['/State'] = States.WARMUP + # no warm-up go directly to running + else: + self._dbusservice['/State'] = States.RUNNING + self._warmUpEndTime = 0 + + self._coolDownEndTime = 0 + self._stoptime = 0 + + self._update_remote_switch() + else: # WARMUP, COOLDOWN, RUNNING, STOPPING + if state in (States.COOLDOWN, States.STOPPING): + # Start request during cool-down run, go back to RUNNING + self.log_info ("aborting cool-down - returning to running") + self._dbusservice['/State'] = States.RUNNING + + elif state == States.WARMUP: + if self._currentTime > self._warmUpEndTime: + self.log_info ("warm-up complete") + self._dbusservice['/State'] = States.RUNNING + + # Update the RunningByCondition + if self._dbusservice['/RunningByCondition'] != condition: + self.log_info('Generator previously running by %s condition is now running by %s condition' + % (self._dbusservice['/RunningByCondition'], condition)) +#### end GuiMods warm-up / cool-down + + self._dbusservice['/RunningByCondition'] = condition + self._dbusservice['/RunningByConditionCode'] = RunningConditions.lookup(condition) + + def _stop_generator(self): + state = self._dbusservice['/State'] + remote_running = self._get_remote_switch_state() + running = state in (States.WARMUP, States.COOLDOWN, States.STOPPING, States.RUNNING) + + if running or remote_running: +#### GuiMods warm-up / cool-down + if state == States.RUNNING: + state = States.COOLDOWN + if self._currentTime < self._coolDownEndTime: + self.log_info ("starting cool-down") + elif self._settings['cooldowntime'] != 0: + self.log_info ("skipping cool-down -- no AC load on generator") + + # warm-up should also transition to stopping + # cool-down time will have expired since it's set to 0 when starting + # and there has not yet been a load on the generator + if state in (States.WARMUP, States.COOLDOWN): + # cool down complete + if self._currentTime > self._coolDownEndTime: + state = States.STOPPING + self.log_info('Stopping generator that was running by %s condition' % + str(self._dbusservice['/RunningByCondition'])) + self._update_remote_switch() # Stop engine + self._stoptime = self._currentTime + self._settings['generatorstoptime'] + if self._currentTime < self._stoptime: + self.log_info ("waiting for generator so stop") + + if state == States.STOPPING: + # wait for stop period expired - finish up transition to STOPPED + if self._currentTime > self._stoptime: + if self._settings['generatorstoptime'] != 0: + self.log_info ("generator stop time reached - OK to reconnect AC") + state = States.STOPPED + self._update_remote_switch() + self._dbusservice['/RunningByCondition'] = '' + self._dbusservice['/RunningByConditionCode'] = RunningConditions.Stopped + self._update_accumulated_time() + self._starttime = 0 + self._dbusservice['/Runtime'] = 0 + self._dbusservice['/ManualStartTimer'] = 0 + self._manualstarttimer = 0 + self._last_runtime_update = 0 + + self._dbusservice['/State'] = state +#### end GuiMods warm-up / cool-down + + @property + def _ac1_is_generator(self): + return self._dbusmonitor.get_value('com.victronenergy.settings', + '/Settings/SystemSetup/AcInput1') == 2 + + @property + def _ac2_is_generator(self): + return self._dbusmonitor.get_value('com.victronenergy.settings', + '/Settings/SystemSetup/AcInput2') == 2 + +#### GuiMods warm-up / cool-down + # stock code does not handle changes in the input type + # which could happen with an external transfer switch + # doing things this way should handle it + + def _set_ignore_ac(self, ignore): + # This is here so the Multi/Quattro can be told to disconnect AC-in, + # so that we can do warm-up and cool-down. + if self.multiservice is None: + return + self._activeAcInIsIgnored = ignore + ignore1 = False + ignore2 = False + if self._ac1_is_generator: + ignore1 = ignore + elif self._ac2_is_generator: + ignore2 = ignore + + if ignore1 != self._ac1isIgnored: + if ignore1: + self.log_info ("shedding load - AC input 1") + else: + self.log_info ("restoring load - AC input 1") + self._dbusmonitor.set_value_async(self.multiservice, '/Ac/Control/IgnoreAcIn1', dbus.Int32(ignore1, variant_level=1)) + self._ac1isIgnored = ignore1 + + if ignore2 != self._ac2isIgnored: + if ignore2: + self.log_info ("shedding load - AC input 2") + else: + self.log_info ("restoring load - AC input 2") + self._dbusmonitor.set_value_async(self.multiservice, '/Ac/Control/IgnoreAcIn2', dbus.Int32(ignore2, variant_level=1)) + self._ac2isIgnored = ignore2 +#### end GuiMods warm-up / cool-down + + def _update_remote_switch(self): + # Engine should be started in these states + v = self._dbusservice['/State'] in (States.RUNNING, States.WARMUP, States.COOLDOWN) + self._set_remote_switch_state(dbus.Int32(v, variant_level=1)) + + def _running_by_digital_input(self, path, value): + return + + def _generator_started(self): + if (not self._generator_running): + self._starttime_fb = monotonic_time.monotonic_time().to_seconds_double() + self._generator_running = True + + def _generator_stopped(self): + if (self._generator_running): + self._generator_running = False + self._update_runtime(just_stopped=True) + self._dbusservice['/Runtime'] = 0 + self._starttime_fb = 0 + self._last_runtime_update = 0 + + def _get_remote_switch_state(self): + raise Exception('This function should be overridden') + + def _set_remote_switch_state(self, value): + raise Exception('This function should be overridden') + + # Check the remote status, for example errors + def _check_remote_status(self): + raise Exception('This function should be overridden') + + def _remote_setup(self): + raise Exception('This function should be overridden') + + def _create_dbus_monitor(self, *args, **kwargs): + raise Exception('This function should be overridden') + + def _create_settings(self, *args, **kwargs): + raise Exception('This function should be overridden') + + def _create_dbus_service(self): + return create_dbus_service(self._instance) + +#### GuiMods +# this function connects the generator digital input (if any) +# to the generator /ManualStart +# +# if the generator digital input changes from stopped to running +# AND no run conditions are active, a manual start is innitiated +# +# if the generator digital input changes from running to stopped +# AND a manual start is active, a manual stop is innitiated +# +# /ExternalOverride is used by the GUI to alert the user when there is a conflict +# between the running state reported by the generator and the expected state +# /ExternalOverride is True if the states differ +# activation is delayed 5 seconds to allow transitions to settle + + def syncManualRunToExternalState (self): + internalRun = self._dbusservice['/State'] in (States.RUNNING, States.WARMUP, States.COOLDOWN) + externalRun = self._is_running + # forward input state changes to /ManualStart + if self._linkToExternalState and externalRun != self._lastIsRunning: + if externalRun and not internalRun: + self.log_info ("generator was started externally - syncing ManualStart state") + self._dbusservice['/ManualStart'] = 1 + elif not externalRun and internalRun and self._dbusservice['/ManualStart'] == 1: + self.log_info ("generator was stopped externally - syncing ManualStart state") + self._dbusservice['/ManualStart'] = 0 + self._lastIsRunning = externalRun + + # update ExternalOverride + if externalRun != internalRun: + if self._externalOverrideDelay > 5: + self._dbusservice['/ExternalOverride'] = 1 + else: + self._externalOverrideDelay += 1 + else: + self._dbusservice['/ExternalOverride'] = 0 + self._externalOverrideDelay = 0 +#### end GuiMods diff --git a/FileSets/PatchSource/startstop.py-v3.60~25.orig b/FileSets/PatchSource/startstop.py-v3.60~25.orig new file mode 100644 index 00000000..2e6f8804 --- /dev/null +++ b/FileSets/PatchSource/startstop.py-v3.60~25.orig @@ -0,0 +1,1357 @@ +#!/usr/bin/python -u +# -*- coding: utf-8 -*- + +# Function +# dbus_generator monitors the dbus for batteries (com.victronenergy.battery.*) and +# vebus com.victronenergy.vebus.* +# Battery and vebus monitors can be configured through the gui. +# It then monitors SOC, AC loads, battery current and battery voltage,to auto start/stop the generator based +# on the configuration settings. Generator can be started manually or periodically setting a tes trun period. +# Time zones function allows to use different values for the conditions along the day depending on time + +import dbus +import datetime +import calendar +import time +import sys +import json +import os +import logging +from collections import OrderedDict +import monotonic_time +from gen_utils import SettingsPrefix, Errors, States, enum +from gen_utils import create_dbus_service +# Victron packages +sys.path.insert(1, os.path.join(os.path.dirname(__file__), 'ext', 'velib_python')) +from ve_utils import exit_on_error +from settingsdevice import SettingsDevice + +RunningConditions = enum( + Stopped = 0, + Manual = 1, + TestRun = 2, + LossOfCommunication = 3, + Soc = 4, + Acload = 5, + BatteryCurrent = 6, + BatteryVoltage = 7, + InverterHighTemp = 8, + InverterOverload = 9, + StopOnAc1 = 10, + StopOnAc2 = 11) + +Capabilities = enum( + WarmupCooldown = 1 +) + +SYSTEM_SERVICE = 'com.victronenergy.system' +BATTERY_PREFIX = '/Dc/Battery' +HISTORY_DAYS = 30 +AUTOSTART_DISABLED_ALARM_TIME = 600 + +def safe_max(args): + try: + return max(x for x in args if x is not None) + except ValueError: + return None + +class Condition(object): + def __init__(self, parent): + self.parent = parent + self.reached = False + self.start_timer = 0 + self.stop_timer = 0 + self.valid = True + self.enabled = False + self.retries = 0 + + def __getitem__(self, key): + try: + return getattr(self, key) + except AttributeError: + raise KeyError(key) + + def __setitem__(self, key, value): + setattr(self, key, value) + + def get_value(self): + raise NotImplementedError("get_value") + + @property + def multi_service(self): + return self.parent.multiservice + + @property + def multi_service_type(self): + return self.parent.multiservice_type + + @property + def monitor(self): + return self.parent._dbusmonitor + +class SocCondition(Condition): + name = 'soc' + monitoring = 'battery' + boolean = False + timed = True + + def get_value(self): + return self.parent._get_battery().soc + +class AcLoadCondition(Condition): + name = 'acload' + monitoring = 'vebus' + boolean = False + timed = True + + def get_value(self): + loadOnAcOut = [] + totalConsumption = [] + + for phase in ['L1', 'L2', 'L3']: + # Get the values directly from the inverter, systemcalc doesn't provide raw inverted power + loadOnAcOut.append(self.monitor.get_value(self.multi_service, ('/Ac/Out/%s/P' % phase))) + + # Calculate total consumption, '/Ac/Consumption/%s/Power' is deprecated + c_i = self.monitor.get_value(SYSTEM_SERVICE, ('/Ac/ConsumptionOnInput/%s/Power' % phase)) + c_o = self.monitor.get_value(SYSTEM_SERVICE, ('/Ac/ConsumptionOnOutput/%s/Power' % phase)) + totalConsumption.append(sum(filter(None, (c_i, c_o)))) + + # Invalidate if vebus is not available + if loadOnAcOut[0] == None: + return None + + # Total consumption + if self.parent._settings['acloadmeasurement'] == 0: + return sum(filter(None, totalConsumption)) + + # Load on inverter AC out + if self.parent._settings['acloadmeasurement'] == 1: + return sum(filter(None, loadOnAcOut)) + + # Highest phase load + if self.parent._settings['acloadmeasurement'] == 2: + return safe_max(loadOnAcOut) + +class BatteryCurrentCondition(Condition): + name = 'batterycurrent' + monitoring = 'battery' + boolean = False + timed = True + + def get_value(self): + c = self.parent._get_battery().current + if c is not None: + c *= -1 + return c + +class BatteryVoltageCondition(Condition): + name = 'batteryvoltage' + monitoring = 'battery' + boolean = False + timed = True + + def get_value(self): + return self.parent._get_battery().voltage + +class InverterTempCondition(Condition): + name = 'inverterhightemp' + monitoring = 'vebus' + boolean = True + timed = True + + def get_value(self): + v = self.monitor.get_value(self.multi_service, + '/Alarms/HighTemperature') + + # When multi is connected to CAN-bus, alarms are published to + # /Alarms/HighTemperature... but when connected to vebus alarms are + # splitted in three phases and published to /Alarms/LX/HighTemperature... + if v is None: + inverterHighTemp = [] + for phase in ['L1', 'L2', 'L3']: + # Inverter alarms must be fetched directly from the inverter service + inverterHighTemp.append(self.monitor.get_value(self.multi_service, ('/Alarms/%s/HighTemperature' % phase))) + return safe_max(inverterHighTemp) + return v + +class InverterOverloadCondition(Condition): + name = 'inverteroverload' + monitoring = 'vebus' + boolean = True + timed = True + + def get_value(self): + v = self.monitor.get_value(self.multi_service, + '/Alarms/Overload') + + # When multi is connected to CAN-bus, alarms are published to + # /Alarms/Overload... but when connected to vebus alarms are + # splitted in three phases and published to /Alarms/LX/Overload... + if v is None: + inverterOverload = [] + for phase in ['L1', 'L2', 'L3']: + # Inverter alarms must be fetched directly from the inverter service + inverterOverload.append(self.monitor.get_value(self.multi_service, ('/Alarms/%s/Overload' % phase))) + return safe_max(inverterOverload) + return v + +# The 'Stop on AC [1/2] conditions are disabled for the Multi RS (acsystem) +# The 'stop on AC' condition stops the generator, which is connected to one AC input when there is AC detected on the other. +# Since the Multi RS only has one AC input, these conditions cannot be used. +class StopOnAc1Condition(Condition): + name = 'stoponac1' + monitoring = 'vebus' + boolean = True + timed = False + + def get_value(self): + # AC input 1 + if self.multi_service_type == 'vebus': + available = self.monitor.get_value(self.multi_service, + '/Ac/State/AcIn1Available') + if available is None: + # Not supported in firmware, fall back to old behaviour + activein = self.monitor.get_value(self.multi_service, + '/Ac/ActiveIn/ActiveInput') + + # Active input is connected + connected = self.monitor.get_value(self.multi_service, + '/Ac/ActiveIn/Connected') + if None not in (activein, connected): + return activein == 0 and connected == 1 + return None + + return bool(available) + else: + # Disabled + return False + +# The StopOnAc2 condition cannot have the fallback to evaluating based on`/Ac/ActiveIn/ActiveInput` + `/Ac/ActiveIn/Connected` +# Because these paths were present in the vebus firmware before `/Ac/State/AcIn2Available` was, +# but switching over to AC input 2 was not supported in that firmware version. +# So if the fallback were implemented for this condition as well, it would stop the +# generator when AC in 2 becomes available but the quattro would not switch over. +class StopOnAc2Condition(Condition): + name = 'stoponac2' + monitoring = 'vebus' + boolean = True + timed = False + + def get_value(self): + if self.multi_service_type == 'vebus': + # AC input 2 available (used when grid is on AC-in-2) + available = self.monitor.get_value(self.multi_service, + '/Ac/State/AcIn2Available') + + return None if available is None else bool(available) + else: + # Disabled + return False + +class Battery(object): + def __init__(self, monitor, service, prefix): + self.monitor = monitor + self.service = service + self.prefix = prefix + + @property + def voltage(self): + return self.monitor.get_value(self.service, self.prefix + '/Voltage') + + @property + def current(self): + return self.monitor.get_value(self.service, self.prefix + '/Current') + + @property + def soc(self): + # Soc from the device doesn't have the '/Dc/0' prefix like the current and voltage do, but it does + # have the same prefix on systemcalc + return self.monitor.get_value(self.service, (BATTERY_PREFIX if self.prefix == BATTERY_PREFIX else '') + '/Soc') + +class StartStop(object): + _driver = None + def __init__(self, instance): + self._dbusservice = None + self._settings = None + self._dbusmonitor = None + self._remoteservice = None + self._name = None + self._enabled = False + self._generator_running = False + self._useGensetHours = False # Sync with genset operatinghours. + self._instance = instance + + # One second per retry + self.RETRIES_ON_ERROR = 300 + self._testrun_soc_retries = 0 + self._last_counters_check = 0 + + # Two different starttime values. + # starttime_fb is set by the modules (relay.py, genset.py) and will be set to the current time when + # the feedback mechanism (digital input / `/StatusCode`) indicates that the generator is running. + # The other one is set by startstop when commanding the module to start the generator and is used by the + # warm-up mechanism to ensure warm-up finishes without needing feedback that the generator has actually started. + # If there is no feedback mechanism in place, the values will be equal as the module will call '_generator_started()` + # right after receiving the start command from startstop. + self._starttime_fb = 0 # Starttime of the generator as reported by the feedback mechanism (e.g., digital input), if present + self._starttime = 0 # Starttime of the generator, maintained by startstop. Not influenced by feedback mechanism. + self._stoptime = 0 # Used for cooldown + self._manualstarttimer = 0 + self._last_runtime_update = 0 + self._timer_runnning = 0 + + # The installer left autostart disabled + self._autostart_last_time = self._get_monotonic_seconds() + self._remote_start_mode_last_time = self._get_monotonic_seconds() + + # Manual battery service selection is deprecated in favour + # of getting the values directly from systemcalc, we keep + # manual selected services handling for compatibility reasons. + self._vebusservice = None + self._acsystemservice = None + self._errorstate = 0 + self._battery_service = None + self._battery_prefix = None + + self._power_input_timer = { + 'timeout': 0, + 'unabletostart': False + } + + # Order is important. Conditions are evaluated in the order listed. + self._condition_stack = OrderedDict({ + SocCondition.name: SocCondition(self), + AcLoadCondition.name: AcLoadCondition(self), + BatteryCurrentCondition.name: BatteryCurrentCondition(self), + BatteryVoltageCondition.name: BatteryVoltageCondition(self), + InverterTempCondition.name: InverterTempCondition(self), + InverterOverloadCondition.name: InverterOverloadCondition(self), + StopOnAc1Condition.name: StopOnAc1Condition(self), + StopOnAc2Condition.name: StopOnAc2Condition(self) + }) + + # Return the active vebusservice or the acsystemservice. + @property + def multiservice(self): + return self._vebusservice if self._vebusservice else self._acsystemservice if self._acsystemservice else None + + @property + def multiservice_type(self): + return self.multiservice.split('.')[2] if self.multiservice is not None else None + + def set_sources(self, dbusmonitor, settings, name, remoteservice): + self._settings = SettingsPrefix(settings, name) + self._dbusmonitor = dbusmonitor + self._remoteservice = remoteservice + self._name = name + + self.log_info('Start/stop instance created for %s.' % self._remoteservice) + self._remote_setup() + + def _create_service(self): + self._dbusservice = self._create_dbus_service() + + # The driver used for this start/stop service + self._dbusservice.add_path('/Type', value=self._driver) + # State: None = invalid, 0 = stopped, 1 = running, 2=Warm-up, 3=Cool-down + self._dbusservice.add_path('/State', value=None, gettextcallback=lambda p, v: States.get_description(v)) + self._dbusservice.add_path('/Enabled', value=1) + # RunningByConditionCode: Numeric Companion to /RunningByCondition below, but + # also encompassing a Stopped state. + self._dbusservice.add_path('/RunningByConditionCode', value=None) + # Error + self._dbusservice.add_path('/Error', value=None, gettextcallback=lambda p, v: Errors.get_description(v)) + # Condition that made the generator start + self._dbusservice.add_path('/RunningByCondition', value=None) + # Runtime + self._dbusservice.add_path('/Runtime', value=None, gettextcallback=self._seconds_to_text) + # Today runtime + self._dbusservice.add_path('/TodayRuntime', value=None, gettextcallback=self._seconds_to_text) + # Test run runtime + self._dbusservice.add_path('/TestRunIntervalRuntime', value=None , gettextcallback=self._seconds_to_text) + # Next test run date, values is 0 for test run disabled + self._dbusservice.add_path('/NextTestRun', value=None, gettextcallback=lambda p, v: datetime.datetime.fromtimestamp(v).strftime('%c')) + # Next test run is needed 1, not needed 0 + self._dbusservice.add_path('/SkipTestRun', value=None) + # Manual start + self._dbusservice.add_path('/ManualStart', value=None, writeable=True) + # Manual start timer + self._dbusservice.add_path('/ManualStartTimer', value=None, writeable=True) + # Silent mode active + self._dbusservice.add_path('/QuietHours', value=None) + # Alarms + self._dbusservice.add_path('/Alarms/NoGeneratorAtAcIn', value=None) + self._dbusservice.add_path('/Alarms/NoGeneratorAtDcIn', value=None) + self._dbusservice.add_path('/Alarms/ServiceIntervalExceeded', value=None) + self._dbusservice.add_path('/Alarms/AutoStartDisabled', value=None) + self._dbusservice.add_path('/Alarms/RemoteStartModeDisabled', value=None) + # Autostart + self._dbusservice.add_path('/AutoStartEnabled', value=None, writeable=True, onchangecallback=self._set_autostart) + # Accumulated runtime + self._dbusservice.add_path('/AccumulatedRuntime', value=None) + # Service interval + self._dbusservice.add_path('/ServiceInterval', value=None) + # Capabilities, where we can add bits + self._dbusservice.add_path('/Capabilities', value=0) + # Service countdown, calculated by running time and service interval + self._dbusservice.add_path('/ServiceCounter', value=None) + self._dbusservice.add_path('/ServiceCounterReset', value=None, writeable=True, onchangecallback=self._reset_service_counter) + # Publish what service we're controlling, and the productid + self._dbusservice.add_path('/GensetService', value=self._remoteservice) + self._dbusservice.add_path('/GensetServiceType', + value=self._remoteservice.split('.')[2] if self._remoteservice is not None else None) + self._dbusservice.add_path('/GensetInstance', + value=self._dbusmonitor.get_value(self._remoteservice, '/DeviceInstance')) + self._dbusservice.add_path('/GensetProductId', + value=self._dbusmonitor.get_value(self._remoteservice, '/ProductId')) + self._dbusservice.add_path('/DigitalInput/Running', value=None, writeable=True, onchangecallback=self._running_by_digital_input) + self._dbusservice.add_path('/DigitalInput/Input', value=None, writeable=True, onchangecallback=self._running_by_digital_input) + + self._dbusservice.register() + # We need to set the values after creating the paths to trigger the 'onValueChanged' event for the gui + # otherwise the gui will report the paths as invalid if we remove and recreate the paths without + # restarting the dbusservice. + self._dbusservice['/State'] = 0 + self._dbusservice['/RunningByConditionCode'] = RunningConditions.Stopped + self._dbusservice['/Error'] = 0 + self._dbusservice['/RunningByCondition'] = '' + self._dbusservice['/Runtime'] = 0 + self._dbusservice['/TodayRuntime'] = 0 + self._dbusservice['/TestRunIntervalRuntime'] = self._interval_runtime(self._settings['testruninterval']) + self._dbusservice['/NextTestRun'] = None + self._dbusservice['/SkipTestRun'] = None + self._dbusservice['/ProductName'] = "Generator start/stop" + self._dbusservice['/ManualStart'] = 0 + self._dbusservice['/ManualStartTimer'] = 0 + self._dbusservice['/QuietHours'] = 0 + self._dbusservice['/Alarms/NoGeneratorAtAcIn'] = 0 + self._dbusservice['/Alarms/ServiceIntervalExceeded'] = 0 + self._dbusservice['/Alarms/AutoStartDisabled'] = 0 # GX auto start/stop + self._dbusservice['/Alarms/RemoteStartModeDisabled'] = 0 # Genset remote start mode + self._dbusservice['/AutoStartEnabled'] = self._settings['autostart'] + self._dbusservice['/AccumulatedRuntime'] = int(self._settings['accumulatedtotal']) + self._dbusservice['/ServiceInterval'] = int(self._settings['serviceinterval']) + self._dbusservice['/ServiceCounter'] = None + self._dbusservice['/ServiceCounterReset'] = 0 + self._dbusservice['/DigitalInput/Running'] = 0 + self._dbusservice['/DigitalInput/Input'] = 0 + + # When this startstop instance controls a genset which reports operatinghours, make sure to synchronize with that. + self._useGensetHours = self._dbusmonitor.get_value(self._remoteservice, '/Engine/OperatingHours', None) is not None + + @property + def _is_running(self): + return self._generator_running + + @property + def capabilities(self): + return self._dbusservice['/Capabilities'] + + def _set_autostart(self, path, value): + if 0 <= value <= 1: + self._settings['autostart'] = int(value) + return True + return False + + def enable(self): + if self._enabled: + return + self.log_info('Enabling auto start/stop and taking control of remote switch') + self._create_service() + self._determineservices() + self._update_remote_switch() + # If cooldown or warmup is enabled, the Quattro may be left in a bad + # state if there is an unfortunate crash or a reboot. Set the ignore_ac + # flag to a sane value on startup. + if self._settings['cooldowntime'] > 0 or \ + self._settings['warmuptime'] > 0: + self._set_ignore_ac(False) + self._enabled = True + + def disable(self): + if not self._enabled: + return + self.log_info('Disabling auto start/stop, releasing control of remote switch') + self._remove_service() + self._enabled = False + + def remove(self): + self.disable() + self.log_info('Removed from start/stop instances') + + def _remove_service(self): + self._dbusservice.__del__() + self._dbusservice = None + + def device_added(self, dbusservicename, instance): + self._determineservices() + + def device_removed(self, dbusservicename, instance): + self._determineservices() + + def get_error(self): + return self._dbusservice['/Error'] + + def set_error(self, errorn): + self._dbusservice['/Error'] = errorn + + def clear_error(self): + self._dbusservice['/Error'] = Errors.NONE + + def dbus_value_changed(self, dbusServiceName, dbusPath, options, changes, deviceInstance): + if self._dbusservice is None: + return + + # AcIn1Available is needed to determine capabilities, but may + # only show up later. So we have to wait for it here. + if self.multiservice is not None and \ + dbusServiceName == self.multiservice and \ + dbusPath == '/Ac/State/AcIn1Available' or dbusPath == '/Ac/Control/IgnoreAcIn1': + self._set_capabilities() + + # If gensethours is updated, update the accumulated time. + if dbusPath == '/Engine/OperatingHours' and self._useGensetHours: + self._update_accumulated_time(gensetHours=changes['Value']) + + if dbusServiceName != 'com.victronenergy.system': + return + if dbusPath == '/AutoSelectedBatteryMeasurement' and self._settings['batterymeasurement'] == 'default': + self._determineservices() + + if dbusPath == '/VebusService': + self._determineservices() + + def handlechangedsetting(self, setting, oldvalue, newvalue): + if self._dbusservice is None: + return + if self._name not in setting: + # Not our setting + return + + s = self._settings.removeprefix(setting) + + if s == 'batterymeasurement': + self._determineservices() + # Reset retries and valid if service changes + for condition in self._condition_stack.values(): + if condition['monitoring'] == 'battery': + condition['valid'] = True + condition['retries'] = 0 + + if s == 'autostart': + self.log_info('Autostart function %s.' % ('enabled' if newvalue == 1 else 'disabled')) + self._dbusservice['/AutoStartEnabled'] = self._settings['autostart'] + + if self._dbusservice is not None and s == 'testruninterval': + self._dbusservice['/TestRunIntervalRuntime'] = self._interval_runtime( + self._settings['testruninterval']) + + if s == 'serviceinterval': + try: + self._dbusservice['/ServiceInterval'] = int(newvalue) + except TypeError: + pass + if newvalue == 0: + self._dbusservice['/ServiceCounter'] = None + else: + self._update_accumulated_time() + if s == 'lastservicereset': + self._update_accumulated_time() + + def _reset_service_counter(self, path, value): + if (path == '/ServiceCounterReset' and value == int(1) and self._dbusservice['/AccumulatedRuntime']): + self._settings['lastservicereset'] = self._dbusservice['/AccumulatedRuntime'] + self._update_accumulated_time() + self.log_info('Service counter reset triggered.') + + return True + + def _seconds_to_text(self, path, value): + m, s = divmod(value, 60) + h, m = divmod(m, 60) + return '%dh, %dm, %ds' % (h, m, s) + + def log_info(self, msg): + logging.info(self._name + ': %s' % msg) + + def tick(self): + if not self._enabled: + return + self._check_remote_status() + self._evaluate_startstop_conditions() + self._evaluate_autostart_disabled_alarm() + self._detect_generator_at_input() + if self._dbusservice['/ServiceCounterReset'] == 1: + self._dbusservice['/ServiceCounterReset'] = 0 + + def _evaluate_startstop_conditions(self): + if self.get_error() != Errors.NONE: + # First evaluation after an error, log it + if self._errorstate == 0: + self._errorstate = 1 + self._dbusservice['/State'] = States.ERROR + self.log_info('Error: #%i - %s, stop controlling remote.' % + (self.get_error(), + Errors.get_description(self.get_error()))) + elif self._errorstate == 1: + # Error cleared + self._errorstate = 0 + self._dbusservice['/State'] = States.STOPPED + self.log_info('Error state cleared, taking control of remote switch.') + + start = False + startbycondition = None + activecondition = self._dbusservice['/RunningByCondition'] + today = calendar.timegm(datetime.date.today().timetuple()) + self._timer_runnning = False + connection_lost = False + running = self._dbusservice['/State'] in (States.RUNNING, States.WARMUP) + + self._check_quiet_hours() + + # New day, register it + if self._last_counters_check < today and self._dbusservice['/State'] == States.STOPPED: + self._last_counters_check = today + self._update_accumulated_time() + + self._update_runtime() + + if self._evaluate_manual_start(): + startbycondition = 'manual' + start = True + + # Conditions will only be evaluated if the autostart functionality is enabled + if self._settings['autostart'] == 1: + + if self._evaluate_testrun_condition(): + startbycondition = 'testrun' + start = True + + # Evaluate stop on AC IN conditions first, when this conditions are enabled and reached the generator + # will stop as soon as AC IN in active. Manual and testrun conditions will make the generator start + # or keep it running. + stop_on_ac_reached = (self._evaluate_condition(self._condition_stack[StopOnAc1Condition.name]) or + self._evaluate_condition(self._condition_stack[StopOnAc2Condition.name])) + stop_by_ac1_ac2 = startbycondition not in ['manual', 'testrun'] and stop_on_ac_reached + + if stop_by_ac1_ac2 and running and activecondition not in ['manual', 'testrun']: + self.log_info('AC input available, stopping') + + # Evaluate value conditions + for condition, data in self._condition_stack.items(): + # Do not evaluate rest of conditions if generator is configured to stop + # when AC IN is available + if stop_by_ac1_ac2: + start = False + if running: + self._reset_condition(data) + continue + else: + break + + # Don't short-circuit this, _evaluate_condition sets .reached + start = self._evaluate_condition(data) or start + startbycondition = condition if start and startbycondition is None else startbycondition + # Connection lost is set to true if the number of retries of one or more enabled conditions + # >= RETRIES_ON_ERROR + if data.enabled: + connection_lost = data.retries >= self.RETRIES_ON_ERROR + + # If none condition is reached check if connection is lost and start/keep running the generator + # depending on '/OnLossCommunication' setting + if not start and connection_lost: + # Start always + if self._settings['onlosscommunication'] == 1: + start = True + startbycondition = 'lossofcommunication' + # Keep running if generator already started + if running and self._settings['onlosscommunication'] == 2: + start = True + startbycondition = 'lossofcommunication' + + if not start and self._errorstate: + self._stop_generator() + + if self._errorstate: + return + + mtime = monotonic_time.monotonic_time().to_seconds_double() + if start: + self._start_generator(startbycondition) + elif (int(mtime - self._starttime) >= self._settings['minimumruntime'] * 60 + or activecondition == 'manual'): + self._stop_generator() + + def _update_runtime(self, just_stopped=False): + # Update current and accumulated runtime. + # By performance reasons, accumulated runtime is only updated + # once per 60s. When the generator stops is also updated. + if self._is_running or just_stopped: + mtime = monotonic_time.monotonic_time().to_seconds_double() + if (mtime - self._starttime_fb) - self._last_runtime_update >= 60 or just_stopped: + self._dbusservice['/Runtime'] = int(mtime - self._starttime_fb) + self._update_accumulated_time() + elif self._last_runtime_update == 0: + self._dbusservice['/Runtime'] = int(mtime - self._starttime_fb) + + def _evaluate_autostart_disabled_alarm(self): + + if self._settings['autostartdisabledalarm'] == 0: + self._autostart_last_time = self._get_monotonic_seconds() + self._remote_start_mode_last_time = self._get_monotonic_seconds() + if self._dbusservice['/Alarms/AutoStartDisabled'] != 0: + self._dbusservice['/Alarms/AutoStartDisabled'] = 0 + if self._dbusservice['/Alarms/RemoteStartModeDisabled'] != 0: + self._dbusservice['/Alarms/RemoteStartModeDisabled'] = 0 + return + + # GX auto start/stop alarm + if self._settings['autostart'] == 1: + self._autostart_last_time = self._get_monotonic_seconds() + if self._dbusservice['/Alarms/AutoStartDisabled'] != 0: + self._dbusservice['/Alarms/AutoStartDisabled'] = 0 + else: + timedisabled = self._get_monotonic_seconds() - self._autostart_last_time + if timedisabled > AUTOSTART_DISABLED_ALARM_TIME and self._dbusservice['/Alarms/AutoStartDisabled'] != 2: + self.log_info("Autostart was left for more than %i seconds, triggering alarm." % int(timedisabled)) + self._dbusservice['/Alarms/AutoStartDisabled'] = 2 + + # Genset remote start mode alarm + if self.get_error() != Errors.REMOTEDISABLED: + self._remote_start_mode_last_time = self._get_monotonic_seconds() + if self._dbusservice['/Alarms/RemoteStartModeDisabled'] != 0: + self._dbusservice['/Alarms/RemoteStartModeDisabled'] = 0 + else: + timedisabled = self._get_monotonic_seconds() - self._remote_start_mode_last_time + if timedisabled > AUTOSTART_DISABLED_ALARM_TIME and self._dbusservice['/Alarms/RemoteStartModeDisabled'] != 2: + self.log_info("Autostart was left for more than %i seconds, triggering alarm." % int(timedisabled)) + self._dbusservice['/Alarms/RemoteStartModeDisabled'] = 2 + + def _detect_generator_at_input(self): + state = self._dbusservice['/State'] + + if state in [States.STOPPED, States.COOLDOWN, States.WARMUP]: + self._reset_power_input_timer() + return + + if self._settings['nogeneratoratacinalarm'] == 0: + self._reset_power_input_timer() + return + + if self.multiservice_type == 'vebus': + activein_state = self._dbusmonitor.get_value( + self.multiservice, '/Ac/ActiveIn/Connected') + else: + active_input = self._dbusmonitor.get_value( + self.multiservice, '/Ac/ActiveIn/ActiveInput') + activein_state = None if active_input is None else 1 if 0 <= active_input <= 1 else 0 + + # Path not supported, skip evaluation + if activein_state == None: + return + + # Sources 0 = Not available, 1 = Grid, 2 = Generator, 3 = Shore + generator_acsource = self._dbusmonitor.get_value( + SYSTEM_SERVICE, '/Ac/ActiveIn/Source') == 2 + # Not connected = 0, connected = 1 + activein_connected = activein_state == 1 + + if generator_acsource and activein_connected: + if self._power_input_timer['unabletostart']: + self.log_info('Generator detected at inverter AC input, alarm removed') + self._reset_power_input_timer() + elif self._power_input_timer['timeout'] < self.RETRIES_ON_ERROR: + self._power_input_timer['timeout'] += 1 + elif not self._power_input_timer['unabletostart']: + self._power_input_timer['unabletostart'] = True + self._dbusservice['/Alarms/NoGeneratorAtAcIn'] = 2 + self.log_info('Generator not detected at inverter AC input, triggering alarm') + + def _reset_power_input_timer(self): + if self._power_input_timer['timeout'] != 0: + self._power_input_timer['timeout'] = 0 + + if self._power_input_timer['unabletostart'] != 0: + self._power_input_timer['unabletostart'] = False + + self._dbusservice['/Alarms/NoGeneratorAtAcIn'] = 0 + + def _reset_condition(self, condition): + condition['reached'] = False + if condition['timed']: + condition['start_timer'] = 0 + condition['stop_timer'] = 0 + + def _check_condition(self, condition, value): + name = condition['name'] + + if self._settings[name + 'enabled'] == 0: + if condition['enabled']: + condition['enabled'] = False + self.log_info('Disabling (%s) condition' % name) + condition['retries'] = 0 + condition['valid'] = True + self._reset_condition(condition) + return False + + elif not condition['enabled']: + condition['enabled'] = True + self.log_info('Enabling (%s) condition' % name) + + if (condition['monitoring'] == 'battery') and (self._settings['batterymeasurement'] == 'nobattery'): + # If no battery monitor is selected reset the condition + self._reset_condition(condition) + return False + + if value is None and condition['valid']: + if condition['retries'] >= self.RETRIES_ON_ERROR: + logging.info('Error getting (%s) value, skipping evaluation till get a valid value' % name) + self._reset_condition(condition) + self._comunnication_lost = True + condition['valid'] = False + else: + condition['retries'] += 1 + if condition['retries'] == 1 or (condition['retries'] % 10) == 0: + self.log_info('Error getting (%s) value, retrying(#%i)' % (name, condition['retries'])) + return False + + elif value is not None and not condition['valid']: + self.log_info('Success getting (%s) value, resuming evaluation' % name) + condition['valid'] = True + condition['retries'] = 0 + + # Reset retries if value is valid + if value is not None and condition['retries'] > 0: + self.log_info('Success getting (%s) value, resuming evaluation' % name) + condition['retries'] = 0 + + return condition['valid'] + + def _evaluate_condition(self, condition): + name = condition['name'] + value = condition.get_value() + setting = ('qh_' if self._dbusservice['/QuietHours'] == 1 else '') + name + startvalue = self._settings[setting + 'start'] if not condition['boolean'] else 1 + stopvalue = self._settings[setting + 'stop'] if not condition['boolean'] else 0 + + # Check if the condition has to be evaluated + if not self._check_condition(condition, value): + # If generator is started by this condition and value is invalid + # wait till RETRIES_ON_ERROR to skip the condition + if condition['reached'] and condition['retries'] <= self.RETRIES_ON_ERROR: + if condition['retries'] > 0: + return True + + return False + + # As this is a generic evaluation method, we need to know how to compare the values + # first check if start value should be greater than stop value and then compare + start_is_greater = startvalue > stopvalue + + # When the condition is already reached only the stop value can set it to False + start = condition['reached'] or (value >= startvalue if start_is_greater else value <= startvalue) + stop = value <= stopvalue if start_is_greater else value >= stopvalue + + # Timed conditions must start/stop after the condition has been reached for a minimum + # time. + if condition['timed']: + if not condition['reached'] and start: + condition['start_timer'] += time.time() if condition['start_timer'] == 0 else 0 + start = time.time() - condition['start_timer'] >= self._settings[name + 'starttimer'] + condition['stop_timer'] *= int(not start) + self._timer_runnning = True + else: + condition['start_timer'] = 0 + + if condition['reached'] and stop: + condition['stop_timer'] += time.time() if condition['stop_timer'] == 0 else 0 + stop = time.time() - condition['stop_timer'] >= self._settings[name + 'stoptimer'] + condition['stop_timer'] *= int(not stop) + self._timer_runnning = True + else: + condition['stop_timer'] = 0 + + condition['reached'] = start and not stop + return condition['reached'] + + def _evaluate_manual_start(self): + if self._dbusservice['/ManualStart'] == 0: + if self._dbusservice['/RunningByCondition'] == 'manual': + self._dbusservice['/ManualStartTimer'] = 0 + return False + + start = True + # If /ManualStartTimer has a value greater than zero will use it to set a stop timer. + # If no timer is set, the generator will not stop until the user stops it manually. + # Once started by manual start, each evaluation the timer is decreased + if self._dbusservice['/ManualStartTimer'] != 0: + self._manualstarttimer += time.time() if self._manualstarttimer == 0 else 0 + self._dbusservice['/ManualStartTimer'] -= int(time.time()) - int(self._manualstarttimer) + self._manualstarttimer = time.time() + start = self._dbusservice['/ManualStartTimer'] > 0 + self._dbusservice['/ManualStart'] = int(start) + # Reset if timer is finished + self._manualstarttimer *= int(start) + self._dbusservice['/ManualStartTimer'] *= int(start) + + return start + + def _evaluate_testrun_condition(self): + if self._settings['testrunenabled'] == 0: + self._dbusservice['/SkipTestRun'] = None + self._dbusservice['/NextTestRun'] = None + return False + + today = datetime.date.today() + yesterday = today - datetime.timedelta(days=1) # Should deal well with DST + now = time.time() + runtillbatteryfull = self._settings['testruntillbatteryfull'] == 1 + soc = self._condition_stack['soc'].get_value() + batteryisfull = runtillbatteryfull and soc == 100 + duration = 60 if runtillbatteryfull else self._settings['testrunruntime'] + + try: + startdate = datetime.date.fromtimestamp(self._settings['testrunstartdate']) + _starttime = time.mktime(yesterday.timetuple()) + self._settings['testrunstarttimer'] + + # today might in fact still be yesterday, if this test run started + # before midnight and finishes after. If `now` still falls in + # yesterday's window, then by the temporal anthropic principle, + # which I just made up but loosely states that time must have + # these properties for observers to exist, it must be yesterday + # because we are here to observe it. + if _starttime <= now <= _starttime + duration: + today = yesterday + starttime = _starttime + else: + starttime = time.mktime(today.timetuple()) + self._settings['testrunstarttimer'] + except ValueError: + logging.debug('Invalid dates, skipping testrun') + return False + + # If start date is in the future set as NextTestRun and stop evaluating + if startdate > today: + self._dbusservice['/NextTestRun'] = time.mktime(startdate.timetuple()) + return False + + start = False + # If the accumulated runtime during the test run interval is greater than '/TestRunIntervalRuntime' + # the test run must be skipped + needed = (self._settings['testrunskipruntime'] > self._dbusservice['/TestRunIntervalRuntime'] + or self._settings['testrunskipruntime'] == 0) + self._dbusservice['/SkipTestRun'] = int(not needed) + + interval = self._settings['testruninterval'] + stoptime = starttime + duration + elapseddays = (today - startdate).days + mod = elapseddays % interval + + start = not bool(mod) and starttime <= now <= stoptime + + if runtillbatteryfull: + if soc is not None: + self._testrun_soc_retries = 0 + start = (start or self._dbusservice['/RunningByCondition'] == 'testrun') and not batteryisfull + elif self._dbusservice['/RunningByCondition'] == 'testrun': + if self._testrun_soc_retries < self.RETRIES_ON_ERROR: + self._testrun_soc_retries += 1 + start = True + if (self._testrun_soc_retries % 10) == 0: + self.log_info('Test run failed to get SOC value, retrying(#%i)' % self._testrun_soc_retries) + else: + self.log_info('Failed to get SOC after %i retries, terminating test run condition' % self._testrun_soc_retries) + start = False + else: + start = False + + if not bool(mod) and (now <= stoptime): + self._dbusservice['/NextTestRun'] = starttime + else: + self._dbusservice['/NextTestRun'] = (time.mktime((today + datetime.timedelta(days=interval - mod)).timetuple()) + + self._settings['testrunstarttimer']) + return start and needed + + def _check_quiet_hours(self): + active = False + if self._settings['quiethoursenabled'] == 1: + # Seconds after today 00:00 + timeinseconds = time.time() - time.mktime(datetime.date.today().timetuple()) + quiethoursstart = self._settings['quiethoursstarttime'] + quiethoursend = self._settings['quiethoursendtime'] + + # Check if the current time is between the start time and end time + if quiethoursstart < quiethoursend: + active = quiethoursstart <= timeinseconds and timeinseconds < quiethoursend + else: # End time is lower than start time, example Start: 21:00, end: 08:00 + active = not (quiethoursend < timeinseconds and timeinseconds < quiethoursstart) + + if self._dbusservice['/QuietHours'] == 0 and active: + self.log_info('Entering to quiet mode') + + elif self._dbusservice['/QuietHours'] == 1 and not active: + self.log_info('Leaving quiet mode') + + self._dbusservice['/QuietHours'] = int(active) + + return active + + def _update_accumulated_time(self, gensetHours=None): + + # Check if this instance is connected to a genset which reports operating hours. + # If so, synchronize with that. + if (self._useGensetHours): + if (gensetHours is None): + gensetHours = self._dbusmonitor.get_value(self._remoteservice, '/Engine/OperatingHours', None) + + # Failsafe + if (gensetHours is not None): + # If connected genset reports /Engine/OperatingHours, use that and also clear the offset value. + self._settings['accumulatedtotalOffset'] = 0 + self._settings['accumulatedtotal'] = accumulatedtotal = gensetHours + else: + # Do not use genset hours + gensetHours = None + + seconds = self._dbusservice['/Runtime'] + accumulated = seconds - self._last_runtime_update + + self._settings['accumulatedtotal'] = accumulatedtotal = gensetHours or int(self._settings['accumulatedtotal']) + accumulated + # Using calendar to get timestamp in UTC, not local time + today_date = str(calendar.timegm(datetime.date.today().timetuple())) + + # If something goes wrong getting the json string create a new one + try: + accumulated_days = json.loads(self._settings['accumulateddaily']) + except ValueError: + accumulated_days = {today_date: 0} + + if (today_date in accumulated_days): + accumulated_days[today_date] += accumulated + else: + accumulated_days[today_date] = accumulated + + if self._dbusservice['/State'] in (States.RUNNING, States.WARMUP, States.COOLDOWN, States.STOPPING): + mtime = monotonic_time.monotonic_time().to_seconds_double() + self._dbusservice['/Runtime'] = int(mtime - self._starttime_fb) + + self._last_runtime_update = seconds + + # Keep the historical with a maximum of HISTORY_DAYS + while len(accumulated_days) > HISTORY_DAYS: + accumulated_days.pop(min(accumulated_days.keys()), None) + + # Update settings + self._settings['accumulateddaily'] = json.dumps(accumulated_days, sort_keys=True) + self._dbusservice['/TodayRuntime'] = self._interval_runtime(0) + self._dbusservice['/TestRunIntervalRuntime'] = self._interval_runtime(self._settings['testruninterval']) + self._dbusservice['/AccumulatedRuntime'] = accumulatedtotal + + # Service counter + serviceinterval = self._settings['serviceinterval'] + lastservicereset = self._settings['lastservicereset'] + if serviceinterval > 0: + servicecountdown = (lastservicereset + serviceinterval) - accumulatedtotal + self._dbusservice['/ServiceCounter'] = servicecountdown + if servicecountdown <= 0: + self._dbusservice['/Alarms/ServiceIntervalExceeded'] = 1 + elif self._dbusservice['/Alarms/ServiceIntervalExceeded'] != 0: + self._dbusservice['/Alarms/ServiceIntervalExceeded'] = 0 + + + + def _interval_runtime(self, days): + summ = 0 + try: + daily_record = json.loads(self._settings['accumulateddaily']) + except ValueError: + return 0 + + for i in range(days + 1): + previous_day = calendar.timegm((datetime.date.today() - datetime.timedelta(days=i)).timetuple()) + if str(previous_day) in daily_record.keys(): + summ += daily_record[str(previous_day)] if str(previous_day) in daily_record.keys() else 0 + + return summ + + def _get_battery(self): + if self._settings['batterymeasurement'] == 'default': + return Battery(self._dbusmonitor, SYSTEM_SERVICE, BATTERY_PREFIX) + + return Battery(self._dbusmonitor, + self._battery_service if self._battery_service else '', + self._battery_prefix if self._battery_prefix else '') + + def _set_capabilities(self): + # Update capabilities + # The ability to ignore AC1/AC2 came in at the same time as + # AC availability and is used to detect it here. + if self.multiservice_type == 'vebus': + readout_supported = self._dbusmonitor.get_value(self.multiservice, + '/Ac/State/AcIn1Available') is not None + self._dbusservice['/Capabilities'] |= ( + Capabilities.WarmupCooldown if readout_supported else 0) + # /Ac/State/AcIn1Available is not available on acsystem + # The vebus system also has the path '/Ac/Control/IgnoreAcIn1', but we can't use this to check for the + # capabilities because this path was already there before ignoring ac input 2 was supported in the vebus quattro. + # So if we were to use '/Ac/Control/IgnoreAcIn1' to set the capabilities on a vebus system, it would + # also be enabled on systems with older firmware which do not support it on input 2. + elif self.multiservice_type == 'acsystem': + ignore_ac_supported = self._dbusmonitor.get_value(self.multiservice, + '/Ac/Control/IgnoreAcIn1') is not None + self._dbusservice['/Capabilities'] |= ( + Capabilities.WarmupCooldown if ignore_ac_supported else 0) + + def _determineservices(self): + # batterymeasurement is either 'default' or 'com_victronenergy_battery_288/Dc/0'. + # In case it is set to default, we use the AutoSelected battery + # measurement, given by SystemCalc. + batterymeasurement = None + newbatteryservice = None + batteryprefix = '' + selectedbattery = self._settings['batterymeasurement'] + vebusservice = None + + if selectedbattery == 'default': + batterymeasurement = 'default' + elif len(selectedbattery.split('/', 1)) == 2: # Only very basic sanity checking.. + batterymeasurement = self._settings['batterymeasurement'] + elif selectedbattery == 'nobattery': + batterymeasurement = None + else: + # Exception: unexpected value for batterymeasurement + pass + + if batterymeasurement and batterymeasurement != 'default': + batteryprefix = '/' + batterymeasurement.split('/', 1)[1] + + # Get the current battery servicename + if self._battery_service: + oldservice = self._battery_service + else: + oldservice = None + + if batterymeasurement == 'default': + newbatteryservice = 'default' + elif batterymeasurement: + battery_instance = int(batterymeasurement.split('_', 3)[3].split('/')[0]) + service_type = None + + if 'vebus' in batterymeasurement: + service_type = 'vebus' + elif 'battery' in batterymeasurement: + service_type = 'battery' + + newbatteryservice = self._get_servicename_by_instance(battery_instance, service_type) + + if newbatteryservice and newbatteryservice != oldservice: + if selectedbattery == 'default': + self.log_info('Getting battery values from systemcalc.') + if selectedbattery == 'nobattery': + self.log_info('Battery monitoring disabled! Stop evaluating related conditions') + self._battery_service = None + self._battery_prefix = None + self.log_info('Battery service we need (%s) found! Using it for generator start/stop' % batterymeasurement) + self._battery_service = newbatteryservice + self._battery_prefix = batteryprefix + elif not newbatteryservice and newbatteryservice != oldservice: + self.log_info('Error getting battery service!') + self._battery_service = newbatteryservice + self._battery_prefix = batteryprefix + + # Get the default VE.Bus service + vebusservice = self._dbusmonitor.get_value('com.victronenergy.system', '/VebusService') + if vebusservice: + if self.multiservice != vebusservice: + self._vebusservice = vebusservice + self._acsystemservice = None + self._set_capabilities() + self.log_info('Vebus service (%s) found! Using it for generator start/stop' % vebusservice) + else: + acsystemservice = self._get_acsystem_service() + if acsystemservice: + if self.multiservice != acsystemservice: + self._acsystemservice = acsystemservice + self._vebusservice = None + self._set_capabilities() + self.log_info('AC system service (%s) found! Using it for generator start/stop' % acsystemservice) + else: + if self._vebusservice is not None: + self.log_info('Vebus service (%s) dissapeared! Stop evaluating related conditions' % self._vebusservice) + elif self._acsystemservice is not None: + self.log_info('AC system service (%s) dissapeared! Stop evaluating related conditions' % self._acsystemservice) + else: + self.log_info('Error getting Vebus or AC system service!') + self._vebusservice = None + self._acsystemservice = None + + def _get_acsystem_service(self): + services = self._dbusmonitor.get_service_list() + for service in services: + if service.startswith('com.victronenergy.acsystem'): + if self._dbusmonitor.get_value(service, '/DeviceInstance') == 0: + return service + return None + + def _get_servicename_by_instance(self, instance, service_type=None): + sv = None + services = self._dbusmonitor.get_service_list() + + for service in services: + if service_type and service_type not in service: + continue + + if services[service] == instance: + sv = service + break + + return sv + + def _get_monotonic_seconds(self): + return monotonic_time.monotonic_time().to_seconds_double() + + def _start_generator(self, condition): + state = self._dbusservice['/State'] + remote_running = self._get_remote_switch_state() + + # This function will start the generator in the case generator not + # already running. When differs, the RunningByCondition is updated + running = state in (States.WARMUP, States.COOLDOWN, States.STOPPING, States.RUNNING) + if not (running and remote_running): # STOPPED, ERROR + # There is an option to skip warm-up for the inverteroverload condition. + if self._settings['warmuptime'] and not (condition == "inverteroverload" and self._settings['inverteroverloadskipwarmup'] == 1): + # Remove load while warming up + self._set_ignore_ac(True) + self._dbusservice['/State'] = States.WARMUP + else: + self._dbusservice['/State'] = States.RUNNING + + self._update_remote_switch() + self._starttime = monotonic_time.monotonic_time().to_seconds_double() + + self.log_info('Starting generator by %s condition' % condition) + else: # WARMUP, COOLDOWN, RUNNING, STOPPING + if state == States.WARMUP: + if monotonic_time.monotonic_time().to_seconds_double() - self._starttime > self._settings['warmuptime']: + self._set_ignore_ac(False) # Release load onto Generator + self._dbusservice['/State'] = States.RUNNING + elif state in (States.COOLDOWN, States.STOPPING): + # Start request during cool-down run, go back to RUNNING + self._set_ignore_ac(False) # Put load back onto Generator + self._dbusservice['/State'] = States.RUNNING + + # Update the RunningByCondition + if self._dbusservice['/RunningByCondition'] != condition: + self.log_info('Generator previously running by %s condition is now running by %s condition' + % (self._dbusservice['/RunningByCondition'], condition)) + + self._dbusservice['/RunningByCondition'] = condition + self._dbusservice['/RunningByConditionCode'] = RunningConditions.lookup(condition) + + def _stop_generator(self): + state = self._dbusservice['/State'] + remote_running = self._get_remote_switch_state() + running = state in (States.WARMUP, States.COOLDOWN, States.STOPPING, States.RUNNING) + + if running or remote_running: + if self._settings['cooldowntime'] > 0: + if state == States.RUNNING: + self._dbusservice['/State'] = States.COOLDOWN + self._stoptime = monotonic_time.monotonic_time().to_seconds_double() + + # Remove load from Generator + self._set_ignore_ac(True) + + return + elif state == States.COOLDOWN: + if monotonic_time.monotonic_time().to_seconds_double() - \ + self._stoptime <= self._settings['cooldowntime']: + return # Don't stop engine yet + + # When we arrive here, a stop command was given during warmup, the + # cooldown timer expired, or no cooldown was configured. Stop + # the engine, but if we're coming from cooldown, delay another + # while in the STOPPING state before reactivating AC-in. + if state == States.COOLDOWN: + self._dbusservice['/State'] = States.STOPPING + self._update_remote_switch() # Stop engine + return + elif state == States.STOPPING: + if monotonic_time.monotonic_time().to_seconds_double() - \ + self._stoptime <= self._settings['cooldowntime'] + self._settings['generatorstoptime']: + return # Wait for engine stop + + # All other possibilities are handled now. Cooldown is over or not + # configured and we waited for the generator to shut down. + self.log_info('Stopping generator that was running by %s condition' % + str(self._dbusservice['/RunningByCondition'])) + self._dbusservice['/RunningByCondition'] = '' + self._dbusservice['/RunningByConditionCode'] = RunningConditions.Stopped + self._dbusservice['/State'] = States.STOPPED + self._update_remote_switch() + self._set_ignore_ac(False) + self._dbusservice['/ManualStartTimer'] = 0 + self._manualstarttimer = 0 + self._starttime = 0 + + @property + def _ac1_is_generator(self): + return self._dbusmonitor.get_value('com.victronenergy.settings', + '/Settings/SystemSetup/AcInput1') == 2 + + @property + def _ac2_is_generator(self): + return self._dbusmonitor.get_value('com.victronenergy.settings', + '/Settings/SystemSetup/AcInput2') == 2 + + def _set_ignore_ac(self, ignore): + # This is here so the Multi/Quattro can be told to disconnect AC-in, + # so that we can do warm-up and cool-down. + if self.multiservice is not None: + if self._ac1_is_generator: + self._dbusmonitor.set_value_async(self.multiservice, '/Ac/Control/IgnoreAcIn1', dbus.Int32(ignore, variant_level=1)) + if self._ac2_is_generator: + self._dbusmonitor.set_value_async(self.multiservice, '/Ac/Control/IgnoreAcIn2', dbus.Int32(ignore, variant_level=1)) + + def _update_remote_switch(self): + # Engine should be started in these states + v = self._dbusservice['/State'] in (States.RUNNING, States.WARMUP, States.COOLDOWN) + self._set_remote_switch_state(dbus.Int32(v, variant_level=1)) + + def _running_by_digital_input(self, path, value): + return + + def _generator_started(self): + if (not self._generator_running): + self._starttime_fb = monotonic_time.monotonic_time().to_seconds_double() + self._generator_running = True + + def _generator_stopped(self): + if (self._generator_running): + self._generator_running = False + self._update_runtime(just_stopped=True) + self._dbusservice['/Runtime'] = 0 + self._starttime_fb = 0 + self._last_runtime_update = 0 + + def _get_remote_switch_state(self): + raise Exception('This function should be overridden') + + def _set_remote_switch_state(self, value): + raise Exception('This function should be overridden') + + # Check the remote status, for example errors + def _check_remote_status(self): + raise Exception('This function should be overridden') + + def _remote_setup(self): + raise Exception('This function should be overridden') + + def _create_dbus_monitor(self, *args, **kwargs): + raise Exception('This function should be overridden') + + def _create_settings(self, *args, **kwargs): + raise Exception('This function should be overridden') + + def _create_dbus_service(self): + return create_dbus_service(self._instance) diff --git a/FileSets/PatchSource/startstop.py-v3.60~25.patch b/FileSets/PatchSource/startstop.py-v3.60~25.patch new file mode 100644 index 00000000..872a82cb --- /dev/null +++ b/FileSets/PatchSource/startstop.py-v3.60~25.patch @@ -0,0 +1,539 @@ +--- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/startstop.py-v3.60~25.orig 2025-02-25 09:42:59 ++++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/startstop.py-v3.60~25 2025-02-25 09:42:55 +@@ -1,6 +1,17 @@ + #!/usr/bin/python -u + # -*- coding: utf-8 -*- + ++#### GuiMods ++#### This file has been modified to allow the generator running state derived from the generator digital input ++#### Previous versions also used the genset AC input but this has been removed from this version with recent changes to stock code !!!!! ++#### If the incoming generator state changes, the manual start state is updated ++#### A switch in the generator settings menu controls whethter the incoming state affects manual start or time accumulaiton ++#### It is now possible to start the generator manually and have it stop automatically based on the preset conditions ++#### for automaitc start / stop ++#### warm-up and cool-down periods have been modified in order to work with an external transfer switch ++#### selecting grid or generator ahead of a MultiPlus input. ++#### Search for #### GuiMods to find changes ++ + # Function + # dbus_generator monitors the dbus for batteries (com.victronenergy.battery.*) and + # vebus com.victronenergy.vebus.* +@@ -272,6 +283,23 @@ + class StartStop(object): + _driver = None + def __init__(self, instance): ++#### GuiMods #### TODO: check if any of these are needed ++ logging.info ("GuiMods version of startstop.py") ++ self._currentTime = self._get_monotonic_seconds() ++ self._last_update_mtime = 0 ++ self._accumulatedRunTime = 0 ++ self._lastIsRunning = False ++ self._externalOverrideDelay = 99 ++ self._linkToExternalState = False ++#### GuiMods warm-up / cool-down ++ self._warmUpEndTime = 0 ++ self._coolDownEndTime = 0 ++ self._ac1isIgnored = False ++ self._ac2isIgnored = False ++ self._activeAcInIsIgnored = False ++ self._acInIsGenerator = False ++#### end GuiMods ++ + self._dbusservice = None + self._settings = None + self._dbusmonitor = None +@@ -302,9 +330,10 @@ + self._timer_runnning = 0 + + # The installer left autostart disabled +- self._autostart_last_time = self._get_monotonic_seconds() +- self._remote_start_mode_last_time = self._get_monotonic_seconds() ++ self._autostart_last_time = self._currentTime ++ self._remote_start_mode_last_time = self._currentTime + ++ + # Manual battery service selection is deprecated in favour + # of getting the values directly from systemcalc, we keep + # manual selected services handling for compatibility reasons. +@@ -440,6 +469,14 @@ + # When this startstop instance controls a genset which reports operatinghours, make sure to synchronize with that. + self._useGensetHours = self._dbusmonitor.get_value(self._remoteservice, '/Engine/OperatingHours', None) is not None + ++#### GuiMods ++ # generator input running state ++ # external override active ++ self._dbusservice.add_path('/ExternalOverride', value=None) ++ self._dbusservice['/ExternalOverride'] = False ++ self._ignoreAutoStartCondition = False ++ ++ + @property + def _is_running(self): + return self._generator_running +@@ -578,13 +615,44 @@ + def tick(self): + if not self._enabled: + return ++ ++#### GuiMods warm-up / cool-down ++ self._currentTime = self._get_monotonic_seconds () ++ + self._check_remote_status() ++#### GuiMods ++ self._linkToExternalState = self._settings['linkManualStartToExternal'] == 1 ++ self.syncManualRunToExternalState () ++ + self._evaluate_startstop_conditions() + self._evaluate_autostart_disabled_alarm() + self._detect_generator_at_input() + if self._dbusservice['/ServiceCounterReset'] == 1: + self._dbusservice['/ServiceCounterReset'] = 0 + ++#### GuiMods warm-up / cool-down ++ ++ # shed load for active generator input in warm-up and cool-down ++ # note that external transfer switch might change the state of on generator ++ # so this needs to be checked and load adjusted every pass ++ # restore load for sources no longer in use or if state is not in warm-up/cool-down ++ # restoring load is delayed 1following end of cool-down ++ # to allow the generator to actually stop producing power ++ state = self._dbusservice['/State'] ++ if state in (States.WARMUP, States.COOLDOWN, States.STOPPING): ++ self._set_ignore_ac (True) ++ else: ++ self._set_ignore_ac (False) ++ ++ # update cool down end time while running and generator has the load ++ # this is done because acInIsGenerator may change by an external transfer switch ++ # and we want an accurate picture of the cool down end time ++ # based on the last time the generatot was loaded ++ if state == States.RUNNING and self._acInIsGenerator: ++ self._coolDownEndTime = self._currentTime + self._settings['cooldowntime'] ++#### end GuiMods warm-up / cool-down ++ ++ + def _evaluate_startstop_conditions(self): + if self.get_error() != Errors.NONE: + # First evaluation after an error, log it +@@ -617,10 +685,24 @@ + + self._update_runtime() + +- if self._evaluate_manual_start(): +- startbycondition = 'manual' +- start = True ++#### GuiMods ++ # A negative /ManualStartTimer is used by the GUI to signal the generator should start now ++ # but stop when all auto stop conditions have been met ++ # so we skip manual start evaluation if this is the case ++ # and set a flag for use below to ignore auto start conditions ++ # the generator is actually started by the auto start/stop logic below ++ if self._dbusservice['/ManualStartTimer'] < 0 and self._dbusservice['/ManualStart'] == 1: ++ self._dbusservice['/ManualStartTimer'] = 0 ++ self._dbusservice['/ManualStart'] = 0 ++ self._ignoreAutoStartCondition = True + ++ else: ++ self._ignoreAutoStartCondition = False ++ if self._evaluate_manual_start(): ++ startbycondition = 'manual' ++ start = True ++#### end GuiMods ++ + # Conditions will only be evaluated if the autostart functionality is enabled + if self._settings['autostart'] == 1: + +@@ -669,6 +751,12 @@ + if running and self._settings['onlosscommunication'] == 2: + start = True + startbycondition = 'lossofcommunication' ++ ++#### GuiMods ++ ## auto start disabled and generator is stopped - clear the 'reached' flags ++ elif self._dbusservice['/State'] == States.STOPPED: ++ for condition, data in self._condition_stack.items(): ++ self._reset_condition(data) + + if not start and self._errorstate: + self._stop_generator() +@@ -676,12 +764,14 @@ + if self._errorstate: + return + +- mtime = monotonic_time.monotonic_time().to_seconds_double() + if start: + self._start_generator(startbycondition) +- elif (int(mtime - self._starttime) >= self._settings['minimumruntime'] * 60 +- or activecondition == 'manual'): ++#### GuiMods ++ # bypass the minimum run time check if External Override is active ++ elif (self._dbusservice['/Runtime'] >= self._settings['minimumruntime'] * 60 ++ or activecondition == 'manual') or self._dbusservice['/ExternalOverride']: + self._stop_generator() ++#### end GuiMods + + def _update_runtime(self, just_stopped=False): + # Update current and accumulated runtime. +@@ -698,8 +788,8 @@ + def _evaluate_autostart_disabled_alarm(self): + + if self._settings['autostartdisabledalarm'] == 0: +- self._autostart_last_time = self._get_monotonic_seconds() +- self._remote_start_mode_last_time = self._get_monotonic_seconds() ++ self._autostart_last_time = self._currentTime ++ self._remote_start_mode_last_time = self._currentTime + if self._dbusservice['/Alarms/AutoStartDisabled'] != 0: + self._dbusservice['/Alarms/AutoStartDisabled'] = 0 + if self._dbusservice['/Alarms/RemoteStartModeDisabled'] != 0: +@@ -708,35 +798,37 @@ + + # GX auto start/stop alarm + if self._settings['autostart'] == 1: +- self._autostart_last_time = self._get_monotonic_seconds() ++ self._autostart_last_time = self._currentTime + if self._dbusservice['/Alarms/AutoStartDisabled'] != 0: + self._dbusservice['/Alarms/AutoStartDisabled'] = 0 + else: +- timedisabled = self._get_monotonic_seconds() - self._autostart_last_time ++ timedisabled = self._currentTime - self._autostart_last_time + if timedisabled > AUTOSTART_DISABLED_ALARM_TIME and self._dbusservice['/Alarms/AutoStartDisabled'] != 2: + self.log_info("Autostart was left for more than %i seconds, triggering alarm." % int(timedisabled)) + self._dbusservice['/Alarms/AutoStartDisabled'] = 2 + + # Genset remote start mode alarm + if self.get_error() != Errors.REMOTEDISABLED: +- self._remote_start_mode_last_time = self._get_monotonic_seconds() ++ self._remote_start_mode_last_time = self._currentTime + if self._dbusservice['/Alarms/RemoteStartModeDisabled'] != 0: + self._dbusservice['/Alarms/RemoteStartModeDisabled'] = 0 + else: +- timedisabled = self._get_monotonic_seconds() - self._remote_start_mode_last_time ++ timedisabled = self._currentTime - self._remote_start_mode_last_time + if timedisabled > AUTOSTART_DISABLED_ALARM_TIME and self._dbusservice['/Alarms/RemoteStartModeDisabled'] != 2: + self.log_info("Autostart was left for more than %i seconds, triggering alarm." % int(timedisabled)) + self._dbusservice['/Alarms/RemoteStartModeDisabled'] = 2 + ++#### GuiMods warm-up / cool-down - rewrote so acInIsGenerator is updated even if alarm is disabled + def _detect_generator_at_input(self): +- state = self._dbusservice['/State'] ++ self._acInIsGenerator = False # covers all conditions that result in a return + ++ state = self._dbusservice['/State'] + if state in [States.STOPPED, States.COOLDOWN, States.WARMUP]: + self._reset_power_input_timer() + return + + if self._settings['nogeneratoratacinalarm'] == 0: +- self._reset_power_input_timer() ++ self._reset_acpower_inverter_input() + return + + if self.multiservice_type == 'vebus': +@@ -757,23 +849,38 @@ + # Not connected = 0, connected = 1 + activein_connected = activein_state == 1 + ++#### GuiMods warm-up / cool-down ++ if self._settings['nogeneratoratacinalarm'] == 0: ++ processAlarm = False ++ self._reset_acpower_inverter_input() ++ else: ++ processAlarm = True ++ + if generator_acsource and activein_connected: +- if self._power_input_timer['unabletostart']: ++#### GuiMods warm-up / cool-down ++ self._acInIsGenerator = True ++#### GuiMods warm-up / cool-down ++ if processAlarm and self._power_input_timer['unabletostart']: + self.log_info('Generator detected at inverter AC input, alarm removed') +- self._reset_power_input_timer() +- elif self._power_input_timer['timeout'] < self.RETRIES_ON_ERROR: +- self._power_input_timer['timeout'] += 1 +- elif not self._power_input_timer['unabletostart']: +- self._power_input_timer['unabletostart'] = True ++ self._reset_power_inverter_input() ++#### GuiMods warm-up / cool-down ++ elif not processAlarm: ++ self._reset_power_inverter_input() ++ return ++ elif self._aower_inverter_input['timeout'] < self.RETRIES_ON_ERROR: ++ self._power_inverter_input['timeout'] += 1 ++ elif not self._power_inverter_input['unabletostart']: ++ self._power_inverter_input['unabletostart'] = True + self._dbusservice['/Alarms/NoGeneratorAtAcIn'] = 2 + self.log_info('Generator not detected at inverter AC input, triggering alarm') ++#### end GuiMods + + def _reset_power_input_timer(self): +- if self._power_input_timer['timeout'] != 0: +- self._power_input_timer['timeout'] = 0 ++ if self._acpower_inverter_input['timeout'] != 0: ++ self._acpower_inverter_input['timeout'] = 0 + +- if self._power_input_timer['unabletostart'] != 0: +- self._power_input_timer['unabletostart'] = False ++ if self._acpower_inverter_input['unabletostart'] != 0: ++ self._acpower_inverter_input['unabletostart'] = 0 + + self._dbusservice['/Alarms/NoGeneratorAtAcIn'] = 0 + +@@ -851,8 +958,20 @@ + + # When the condition is already reached only the stop value can set it to False + start = condition['reached'] or (value >= startvalue if start_is_greater else value <= startvalue) ++ stop = value <= stopvalue if start_is_greater else value >= stopvalue ++ ++#### GuiMods + stop = value <= stopvalue if start_is_greater else value >= stopvalue ++ # when starting manually and stopping based on auto stop values, ++ # start if stop condition is not satisfied + ++ if self._ignoreAutoStartCondition: ++ start = not stop ++ else: ++ # When the condition is already reached only the stop value can set it to False ++ start = condition['reached'] or (value >= startvalue if start_is_greater else value <= startvalue) ++#### end GuiMods ++ + # Timed conditions must start/stop after the condition has been reached for a minimum + # time. + if condition['timed']: +@@ -885,7 +1004,8 @@ + # If /ManualStartTimer has a value greater than zero will use it to set a stop timer. + # If no timer is set, the generator will not stop until the user stops it manually. + # Once started by manual start, each evaluation the timer is decreased +- if self._dbusservice['/ManualStartTimer'] != 0: ++#### GuiMods - change test to > 0 from != 0 to allow for start now / auto stop ++ if self._dbusservice['/ManualStartTimer'] > 0: + self._manualstarttimer += time.time() if self._manualstarttimer == 0 else 0 + self._dbusservice['/ManualStartTimer'] -= int(time.time()) - int(self._manualstarttimer) + self._manualstarttimer = time.time() +@@ -1219,31 +1339,40 @@ + running = state in (States.WARMUP, States.COOLDOWN, States.STOPPING, States.RUNNING) + if not (running and remote_running): # STOPPED, ERROR + # There is an option to skip warm-up for the inverteroverload condition. +- if self._settings['warmuptime'] and not (condition == "inverteroverload" and self._settings['inverteroverloadskipwarmup'] == 1): +- # Remove load while warming up +- self._set_ignore_ac(True) ++#### GuiMods warm-up / cool-down ++ self.log_info('Starting generator by %s condition' % condition) ++ # if there is a warmup time specified, always go through warm-up state ++ # regardless of AC input in use ++ warmUpPeriod = self._settings['warmuptime'] ++ if warmUpPeriod > 0: ++ self._warmUpEndTime = self._currentTime + warmUpPeriod ++ self.log_info ("starting warm-up") + self._dbusservice['/State'] = States.WARMUP ++ # no warm-up go directly to running + else: + self._dbusservice['/State'] = States.RUNNING ++ self._warmUpEndTime = 0 + +- self._update_remote_switch() +- self._starttime = monotonic_time.monotonic_time().to_seconds_double() ++ self._coolDownEndTime = 0 ++ self._stoptime = 0 + +- self.log_info('Starting generator by %s condition' % condition) ++ self._update_remote_switch() + else: # WARMUP, COOLDOWN, RUNNING, STOPPING +- if state == States.WARMUP: +- if monotonic_time.monotonic_time().to_seconds_double() - self._starttime > self._settings['warmuptime']: +- self._set_ignore_ac(False) # Release load onto Generator +- self._dbusservice['/State'] = States.RUNNING +- elif state in (States.COOLDOWN, States.STOPPING): ++ if state in (States.COOLDOWN, States.STOPPING): + # Start request during cool-down run, go back to RUNNING +- self._set_ignore_ac(False) # Put load back onto Generator ++ self.log_info ("aborting cool-down - returning to running") + self._dbusservice['/State'] = States.RUNNING + ++ elif state == States.WARMUP: ++ if self._currentTime > self._warmUpEndTime: ++ self.log_info ("warm-up complete") ++ self._dbusservice['/State'] = States.RUNNING ++ + # Update the RunningByCondition + if self._dbusservice['/RunningByCondition'] != condition: + self.log_info('Generator previously running by %s condition is now running by %s condition' + % (self._dbusservice['/RunningByCondition'], condition)) ++#### end GuiMods warm-up / cool-down + + self._dbusservice['/RunningByCondition'] = condition + self._dbusservice['/RunningByConditionCode'] = RunningConditions.lookup(condition) +@@ -1254,46 +1383,47 @@ + running = state in (States.WARMUP, States.COOLDOWN, States.STOPPING, States.RUNNING) + + if running or remote_running: +- if self._settings['cooldowntime'] > 0: +- if state == States.RUNNING: +- self._dbusservice['/State'] = States.COOLDOWN +- self._stoptime = monotonic_time.monotonic_time().to_seconds_double() ++#### GuiMods warm-up / cool-down ++ if state == States.RUNNING: ++ state = States.COOLDOWN ++ if self._currentTime < self._coolDownEndTime: ++ self.log_info ("starting cool-down") ++ elif self._settings['cooldowntime'] != 0: ++ self.log_info ("skipping cool-down -- no AC load on generator") + +- # Remove load from Generator +- self._set_ignore_ac(True) ++ # warm-up should also transition to stopping ++ # cool-down time will have expired since it's set to 0 when starting ++ # and there has not yet been a load on the generator ++ if state in (States.WARMUP, States.COOLDOWN): ++ # cool down complete ++ if self._currentTime > self._coolDownEndTime: ++ state = States.STOPPING ++ self.log_info('Stopping generator that was running by %s condition' % ++ str(self._dbusservice['/RunningByCondition'])) ++ self._update_remote_switch() # Stop engine ++ self._stoptime = self._currentTime + self._settings['generatorstoptime'] ++ if self._currentTime < self._stoptime: ++ self.log_info ("waiting for generator so stop") ++ ++ if state == States.STOPPING: ++ # wait for stop period expired - finish up transition to STOPPED ++ if self._currentTime > self._stoptime: ++ if self._settings['generatorstoptime'] != 0: ++ self.log_info ("generator stop time reached - OK to reconnect AC") ++ state = States.STOPPED ++ self._update_remote_switch() ++ self._dbusservice['/RunningByCondition'] = '' ++ self._dbusservice['/RunningByConditionCode'] = RunningConditions.Stopped ++ self._update_accumulated_time() ++ self._starttime = 0 ++ self._dbusservice['/Runtime'] = 0 ++ self._dbusservice['/ManualStartTimer'] = 0 ++ self._manualstarttimer = 0 ++ self._last_runtime_update = 0 + +- return +- elif state == States.COOLDOWN: +- if monotonic_time.monotonic_time().to_seconds_double() - \ +- self._stoptime <= self._settings['cooldowntime']: +- return # Don't stop engine yet ++ self._dbusservice['/State'] = state ++#### end GuiMods warm-up / cool-down + +- # When we arrive here, a stop command was given during warmup, the +- # cooldown timer expired, or no cooldown was configured. Stop +- # the engine, but if we're coming from cooldown, delay another +- # while in the STOPPING state before reactivating AC-in. +- if state == States.COOLDOWN: +- self._dbusservice['/State'] = States.STOPPING +- self._update_remote_switch() # Stop engine +- return +- elif state == States.STOPPING: +- if monotonic_time.monotonic_time().to_seconds_double() - \ +- self._stoptime <= self._settings['cooldowntime'] + self._settings['generatorstoptime']: +- return # Wait for engine stop +- +- # All other possibilities are handled now. Cooldown is over or not +- # configured and we waited for the generator to shut down. +- self.log_info('Stopping generator that was running by %s condition' % +- str(self._dbusservice['/RunningByCondition'])) +- self._dbusservice['/RunningByCondition'] = '' +- self._dbusservice['/RunningByConditionCode'] = RunningConditions.Stopped +- self._dbusservice['/State'] = States.STOPPED +- self._update_remote_switch() +- self._set_ignore_ac(False) +- self._dbusservice['/ManualStartTimer'] = 0 +- self._manualstarttimer = 0 +- self._starttime = 0 +- + @property + def _ac1_is_generator(self): + return self._dbusmonitor.get_value('com.victronenergy.settings', +@@ -1304,15 +1434,41 @@ + return self._dbusmonitor.get_value('com.victronenergy.settings', + '/Settings/SystemSetup/AcInput2') == 2 + ++#### GuiMods warm-up / cool-down ++ # stock code does not handle changes in the input type ++ # which could happen with an external transfer switch ++ # doing things this way should handle it ++ + def _set_ignore_ac(self, ignore): + # This is here so the Multi/Quattro can be told to disconnect AC-in, + # so that we can do warm-up and cool-down. +- if self.multiservice is not None: +- if self._ac1_is_generator: +- self._dbusmonitor.set_value_async(self.multiservice, '/Ac/Control/IgnoreAcIn1', dbus.Int32(ignore, variant_level=1)) +- if self._ac2_is_generator: +- self._dbusmonitor.set_value_async(self.multiservice, '/Ac/Control/IgnoreAcIn2', dbus.Int32(ignore, variant_level=1)) ++ if self.multiservice is None: ++ return ++ self._activeAcInIsIgnored = ignore ++ ignore1 = False ++ ignore2 = False ++ if self._ac1_is_generator: ++ ignore1 = ignore ++ elif self._ac2_is_generator: ++ ignore2 = ignore + ++ if ignore1 != self._ac1isIgnored: ++ if ignore1: ++ self.log_info ("shedding load - AC input 1") ++ else: ++ self.log_info ("restoring load - AC input 1") ++ self._dbusmonitor.set_value_async(self.multiservice, '/Ac/Control/IgnoreAcIn1', dbus.Int32(ignore1, variant_level=1)) ++ self._ac1isIgnored = ignore1 ++ ++ if ignore2 != self._ac2isIgnored: ++ if ignore2: ++ self.log_info ("shedding load - AC input 2") ++ else: ++ self.log_info ("restoring load - AC input 2") ++ self._dbusmonitor.set_value_async(self.multiservice, '/Ac/Control/IgnoreAcIn2', dbus.Int32(ignore2, variant_level=1)) ++ self._ac2isIgnored = ignore2 ++#### end GuiMods warm-up / cool-down ++ + def _update_remote_switch(self): + # Engine should be started in these states + v = self._dbusservice['/State'] in (States.RUNNING, States.WARMUP, States.COOLDOWN) +@@ -1355,3 +1511,42 @@ + + def _create_dbus_service(self): + return create_dbus_service(self._instance) ++ ++#### GuiMods ++# this function connects the generator digital input (if any) ++# to the generator /ManualStart ++# ++# if the generator digital input changes from stopped to running ++# AND no run conditions are active, a manual start is innitiated ++# ++# if the generator digital input changes from running to stopped ++# AND a manual start is active, a manual stop is innitiated ++# ++# /ExternalOverride is used by the GUI to alert the user when there is a conflict ++# between the running state reported by the generator and the expected state ++# /ExternalOverride is True if the states differ ++# activation is delayed 5 seconds to allow transitions to settle ++ ++ def syncManualRunToExternalState (self): ++ internalRun = self._dbusservice['/State'] in (States.RUNNING, States.WARMUP, States.COOLDOWN) ++ externalRun = self._is_running ++ # forward input state changes to /ManualStart ++ if self._linkToExternalState and externalRun != self._lastIsRunning: ++ if externalRun and not internalRun: ++ self.log_info ("generator was started externally - syncing ManualStart state") ++ self._dbusservice['/ManualStart'] = 1 ++ elif not externalRun and internalRun and self._dbusservice['/ManualStart'] == 1: ++ self.log_info ("generator was stopped externally - syncing ManualStart state") ++ self._dbusservice['/ManualStart'] = 0 ++ self._lastIsRunning = externalRun ++ ++ # update ExternalOverride ++ if externalRun != internalRun: ++ if self._externalOverrideDelay > 5: ++ self._dbusservice['/ExternalOverride'] = 1 ++ else: ++ self._externalOverrideDelay += 1 ++ else: ++ self._dbusservice['/ExternalOverride'] = 0 ++ self._externalOverrideDelay = 0 ++#### end GuiMods diff --git a/FileSets/VersionIndependent/PageSettingsGuiModsGauges.qml b/FileSets/VersionIndependent/PageSettingsGuiModsGauges.qml index 0c6edd3f..a2527fcd 100644 --- a/FileSets/VersionIndependent/PageSettingsGuiModsGauges.qml +++ b/FileSets/VersionIndependent/PageSettingsGuiModsGauges.qml @@ -9,7 +9,7 @@ MbPage { title: qsTr("Gui Mods power gauges") property string bindPrefixGuiMods: "com.victronenergy.settings/Settings/GuiMods" - model: VisualItemModel + model: VisibleItemModel { MbSwitch { diff --git a/FileSets/v3.10/Battery.qml b/FileSets/v3.10/Battery.qml deleted file mode 120000 index fafc7b36..00000000 --- a/FileSets/v3.10/Battery.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Battery.qml \ No newline at end of file diff --git a/FileSets/v3.10/MbEditBox.qml b/FileSets/v3.10/MbEditBox.qml deleted file mode 120000 index 8381b7d5..00000000 --- a/FileSets/v3.10/MbEditBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~10/MbEditBox.qml \ No newline at end of file diff --git a/FileSets/v3.10/MbEditBoxDateTime.qml b/FileSets/v3.10/MbEditBoxDateTime.qml deleted file mode 120000 index 7fb351ea..00000000 --- a/FileSets/v3.10/MbEditBoxDateTime.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbEditBoxDateTime.qml \ No newline at end of file diff --git a/FileSets/v3.10/MbItem.qml b/FileSets/v3.10/MbItem.qml deleted file mode 120000 index 048a313c..00000000 --- a/FileSets/v3.10/MbItem.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbItem.qml \ No newline at end of file diff --git a/FileSets/v3.10/MbItemDigitalInput.qml b/FileSets/v3.10/MbItemDigitalInput.qml deleted file mode 120000 index 4404b520..00000000 --- a/FileSets/v3.10/MbItemDigitalInput.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbItemDigitalInput.qml \ No newline at end of file diff --git a/FileSets/v3.10/MbSpinBox.qml b/FileSets/v3.10/MbSpinBox.qml deleted file mode 120000 index bc7530c8..00000000 --- a/FileSets/v3.10/MbSpinBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbSpinBox.qml \ No newline at end of file diff --git a/FileSets/v3.10/MbStyle.qml b/FileSets/v3.10/MbStyle.qml deleted file mode 120000 index d1940446..00000000 --- a/FileSets/v3.10/MbStyle.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbStyle.qml \ No newline at end of file diff --git a/FileSets/v3.10/MbSubMenu.qml b/FileSets/v3.10/MbSubMenu.qml deleted file mode 120000 index 35516a9a..00000000 --- a/FileSets/v3.10/MbSubMenu.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbSubMenu.qml \ No newline at end of file diff --git a/FileSets/v3.10/Multi.qml b/FileSets/v3.10/Multi.qml deleted file mode 120000 index aa938460..00000000 --- a/FileSets/v3.10/Multi.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Multi.qml \ No newline at end of file diff --git a/FileSets/v3.10/OverviewBox.qml b/FileSets/v3.10/OverviewBox.qml deleted file mode 120000 index 96aa4214..00000000 --- a/FileSets/v3.10/OverviewBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewBox.qml \ No newline at end of file diff --git a/FileSets/v3.10/OverviewConnection.qml b/FileSets/v3.10/OverviewConnection.qml deleted file mode 120000 index d833fd05..00000000 --- a/FileSets/v3.10/OverviewConnection.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewConnection.qml \ No newline at end of file diff --git a/FileSets/v3.10/OverviewConnectionEnd.qml b/FileSets/v3.10/OverviewConnectionEnd.qml deleted file mode 120000 index 50c4b471..00000000 --- a/FileSets/v3.10/OverviewConnectionEnd.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewConnectionEnd.qml \ No newline at end of file diff --git a/FileSets/v3.10/OverviewSolarCharger.qml b/FileSets/v3.10/OverviewSolarCharger.qml deleted file mode 120000 index 7353cec9..00000000 --- a/FileSets/v3.10/OverviewSolarCharger.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewSolarCharger.qml \ No newline at end of file diff --git a/FileSets/v3.10/OverviewSolarInverter.qml b/FileSets/v3.10/OverviewSolarInverter.qml deleted file mode 120000 index 4048703e..00000000 --- a/FileSets/v3.10/OverviewSolarInverter.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewSolarInverter.qml \ No newline at end of file diff --git a/FileSets/v3.10/OverviewTankDelegate.qml b/FileSets/v3.10/OverviewTankDelegate.qml deleted file mode 120000 index faeb9aeb..00000000 --- a/FileSets/v3.10/OverviewTankDelegate.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewTankDelegate.qml \ No newline at end of file diff --git a/FileSets/v3.10/OverviewTanks.qml b/FileSets/v3.10/OverviewTanks.qml deleted file mode 120000 index 645a324b..00000000 --- a/FileSets/v3.10/OverviewTanks.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewTanks.qml \ No newline at end of file diff --git a/FileSets/v3.10/PageDigitalInput.qml b/FileSets/v3.10/PageDigitalInput.qml deleted file mode 120000 index 5b768f8f..00000000 --- a/FileSets/v3.10/PageDigitalInput.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/PageDigitalInput.qml \ No newline at end of file diff --git a/FileSets/v3.10/PageMain.qml b/FileSets/v3.10/PageMain.qml deleted file mode 120000 index 2f68fb1c..00000000 --- a/FileSets/v3.10/PageMain.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.14/PageMain.qml \ No newline at end of file diff --git a/FileSets/v3.10/PageSettingsDisplay.qml b/FileSets/v3.10/PageSettingsDisplay.qml deleted file mode 120000 index 9860c031..00000000 --- a/FileSets/v3.10/PageSettingsDisplay.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.14/PageSettingsDisplay.qml \ No newline at end of file diff --git a/FileSets/v3.10/Tile.qml b/FileSets/v3.10/Tile.qml deleted file mode 120000 index 0558cecf..00000000 --- a/FileSets/v3.10/Tile.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Tile.qml \ No newline at end of file diff --git a/FileSets/v3.10/TileText.qml b/FileSets/v3.10/TileText.qml deleted file mode 120000 index 8d7c0778..00000000 --- a/FileSets/v3.10/TileText.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/TileText.qml \ No newline at end of file diff --git a/FileSets/v3.10/attributes.csv b/FileSets/v3.10/attributes.csv deleted file mode 120000 index 61a61302..00000000 --- a/FileSets/v3.10/attributes.csv +++ /dev/null @@ -1 +0,0 @@ -../v3.14/attributes.csv \ No newline at end of file diff --git a/FileSets/v3.10/dbus_generator.py b/FileSets/v3.10/dbus_generator.py deleted file mode 120000 index b22d702a..00000000 --- a/FileSets/v3.10/dbus_generator.py +++ /dev/null @@ -1 +0,0 @@ -../v3.14/dbus_generator.py \ No newline at end of file diff --git a/FileSets/v3.10/styles.css b/FileSets/v3.10/styles.css deleted file mode 120000 index ef8557ec..00000000 --- a/FileSets/v3.10/styles.css +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/styles.css \ No newline at end of file diff --git a/FileSets/v3.11/Battery.qml b/FileSets/v3.11/Battery.qml deleted file mode 120000 index fafc7b36..00000000 --- a/FileSets/v3.11/Battery.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Battery.qml \ No newline at end of file diff --git a/FileSets/v3.11/MbEditBox.qml b/FileSets/v3.11/MbEditBox.qml deleted file mode 120000 index 8381b7d5..00000000 --- a/FileSets/v3.11/MbEditBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~10/MbEditBox.qml \ No newline at end of file diff --git a/FileSets/v3.11/MbEditBoxDateTime.qml b/FileSets/v3.11/MbEditBoxDateTime.qml deleted file mode 120000 index 7fb351ea..00000000 --- a/FileSets/v3.11/MbEditBoxDateTime.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbEditBoxDateTime.qml \ No newline at end of file diff --git a/FileSets/v3.11/MbItem.qml b/FileSets/v3.11/MbItem.qml deleted file mode 120000 index 048a313c..00000000 --- a/FileSets/v3.11/MbItem.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbItem.qml \ No newline at end of file diff --git a/FileSets/v3.11/MbItemDigitalInput.qml b/FileSets/v3.11/MbItemDigitalInput.qml deleted file mode 120000 index 4404b520..00000000 --- a/FileSets/v3.11/MbItemDigitalInput.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbItemDigitalInput.qml \ No newline at end of file diff --git a/FileSets/v3.11/MbSpinBox.qml b/FileSets/v3.11/MbSpinBox.qml deleted file mode 120000 index bc7530c8..00000000 --- a/FileSets/v3.11/MbSpinBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbSpinBox.qml \ No newline at end of file diff --git a/FileSets/v3.11/MbStyle.qml b/FileSets/v3.11/MbStyle.qml deleted file mode 120000 index d1940446..00000000 --- a/FileSets/v3.11/MbStyle.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbStyle.qml \ No newline at end of file diff --git a/FileSets/v3.11/MbSubMenu.qml b/FileSets/v3.11/MbSubMenu.qml deleted file mode 120000 index 35516a9a..00000000 --- a/FileSets/v3.11/MbSubMenu.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbSubMenu.qml \ No newline at end of file diff --git a/FileSets/v3.11/Multi.qml b/FileSets/v3.11/Multi.qml deleted file mode 120000 index aa938460..00000000 --- a/FileSets/v3.11/Multi.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Multi.qml \ No newline at end of file diff --git a/FileSets/v3.11/OverviewBox.qml b/FileSets/v3.11/OverviewBox.qml deleted file mode 120000 index 96aa4214..00000000 --- a/FileSets/v3.11/OverviewBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewBox.qml \ No newline at end of file diff --git a/FileSets/v3.11/OverviewConnection.qml b/FileSets/v3.11/OverviewConnection.qml deleted file mode 120000 index d833fd05..00000000 --- a/FileSets/v3.11/OverviewConnection.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewConnection.qml \ No newline at end of file diff --git a/FileSets/v3.11/OverviewConnectionEnd.qml b/FileSets/v3.11/OverviewConnectionEnd.qml deleted file mode 120000 index 50c4b471..00000000 --- a/FileSets/v3.11/OverviewConnectionEnd.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewConnectionEnd.qml \ No newline at end of file diff --git a/FileSets/v3.11/OverviewSolarCharger.qml b/FileSets/v3.11/OverviewSolarCharger.qml deleted file mode 120000 index 7353cec9..00000000 --- a/FileSets/v3.11/OverviewSolarCharger.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewSolarCharger.qml \ No newline at end of file diff --git a/FileSets/v3.11/OverviewSolarInverter.qml b/FileSets/v3.11/OverviewSolarInverter.qml deleted file mode 120000 index 4048703e..00000000 --- a/FileSets/v3.11/OverviewSolarInverter.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewSolarInverter.qml \ No newline at end of file diff --git a/FileSets/v3.11/OverviewTankDelegate.qml b/FileSets/v3.11/OverviewTankDelegate.qml deleted file mode 120000 index faeb9aeb..00000000 --- a/FileSets/v3.11/OverviewTankDelegate.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewTankDelegate.qml \ No newline at end of file diff --git a/FileSets/v3.11/OverviewTanks.qml b/FileSets/v3.11/OverviewTanks.qml deleted file mode 120000 index 645a324b..00000000 --- a/FileSets/v3.11/OverviewTanks.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewTanks.qml \ No newline at end of file diff --git a/FileSets/v3.11/PageDigitalInput.qml b/FileSets/v3.11/PageDigitalInput.qml deleted file mode 120000 index 5b768f8f..00000000 --- a/FileSets/v3.11/PageDigitalInput.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/PageDigitalInput.qml \ No newline at end of file diff --git a/FileSets/v3.11/PageMain.qml b/FileSets/v3.11/PageMain.qml deleted file mode 120000 index 2f68fb1c..00000000 --- a/FileSets/v3.11/PageMain.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.14/PageMain.qml \ No newline at end of file diff --git a/FileSets/v3.11/PageSettingsDisplay.qml b/FileSets/v3.11/PageSettingsDisplay.qml deleted file mode 120000 index 9860c031..00000000 --- a/FileSets/v3.11/PageSettingsDisplay.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.14/PageSettingsDisplay.qml \ No newline at end of file diff --git a/FileSets/v3.11/Tile.qml b/FileSets/v3.11/Tile.qml deleted file mode 120000 index 0558cecf..00000000 --- a/FileSets/v3.11/Tile.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Tile.qml \ No newline at end of file diff --git a/FileSets/v3.11/TileText.qml b/FileSets/v3.11/TileText.qml deleted file mode 120000 index 8d7c0778..00000000 --- a/FileSets/v3.11/TileText.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/TileText.qml \ No newline at end of file diff --git a/FileSets/v3.11/attributes.csv b/FileSets/v3.11/attributes.csv deleted file mode 120000 index 61a61302..00000000 --- a/FileSets/v3.11/attributes.csv +++ /dev/null @@ -1 +0,0 @@ -../v3.14/attributes.csv \ No newline at end of file diff --git a/FileSets/v3.11/dbus_generator.py b/FileSets/v3.11/dbus_generator.py deleted file mode 120000 index b22d702a..00000000 --- a/FileSets/v3.11/dbus_generator.py +++ /dev/null @@ -1 +0,0 @@ -../v3.14/dbus_generator.py \ No newline at end of file diff --git a/FileSets/v3.11/dbus_systemcalc.py b/FileSets/v3.11/dbus_systemcalc.py deleted file mode 120000 index fd94027f..00000000 --- a/FileSets/v3.11/dbus_systemcalc.py +++ /dev/null @@ -1 +0,0 @@ -../v3.12/dbus_systemcalc.py \ No newline at end of file diff --git a/FileSets/v3.11/main.qml b/FileSets/v3.11/main.qml deleted file mode 120000 index 492167a2..00000000 --- a/FileSets/v3.11/main.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.22/main.qml \ No newline at end of file diff --git a/FileSets/v3.11/styles.css b/FileSets/v3.11/styles.css deleted file mode 120000 index ef8557ec..00000000 --- a/FileSets/v3.11/styles.css +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/styles.css \ No newline at end of file diff --git a/FileSets/v3.12/Battery.qml b/FileSets/v3.12/Battery.qml deleted file mode 120000 index fafc7b36..00000000 --- a/FileSets/v3.12/Battery.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Battery.qml \ No newline at end of file diff --git a/FileSets/v3.12/MbEditBox.qml b/FileSets/v3.12/MbEditBox.qml deleted file mode 120000 index 8381b7d5..00000000 --- a/FileSets/v3.12/MbEditBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~10/MbEditBox.qml \ No newline at end of file diff --git a/FileSets/v3.12/MbEditBoxDateTime.qml b/FileSets/v3.12/MbEditBoxDateTime.qml deleted file mode 120000 index 7fb351ea..00000000 --- a/FileSets/v3.12/MbEditBoxDateTime.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbEditBoxDateTime.qml \ No newline at end of file diff --git a/FileSets/v3.12/MbItem.qml b/FileSets/v3.12/MbItem.qml deleted file mode 120000 index 048a313c..00000000 --- a/FileSets/v3.12/MbItem.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbItem.qml \ No newline at end of file diff --git a/FileSets/v3.12/MbItemDigitalInput.qml b/FileSets/v3.12/MbItemDigitalInput.qml deleted file mode 120000 index 4404b520..00000000 --- a/FileSets/v3.12/MbItemDigitalInput.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbItemDigitalInput.qml \ No newline at end of file diff --git a/FileSets/v3.12/MbSpinBox.qml b/FileSets/v3.12/MbSpinBox.qml deleted file mode 120000 index bc7530c8..00000000 --- a/FileSets/v3.12/MbSpinBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbSpinBox.qml \ No newline at end of file diff --git a/FileSets/v3.12/MbStyle.qml b/FileSets/v3.12/MbStyle.qml deleted file mode 120000 index d1940446..00000000 --- a/FileSets/v3.12/MbStyle.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbStyle.qml \ No newline at end of file diff --git a/FileSets/v3.12/MbSubMenu.qml b/FileSets/v3.12/MbSubMenu.qml deleted file mode 120000 index 35516a9a..00000000 --- a/FileSets/v3.12/MbSubMenu.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbSubMenu.qml \ No newline at end of file diff --git a/FileSets/v3.12/Multi.qml b/FileSets/v3.12/Multi.qml deleted file mode 120000 index aa938460..00000000 --- a/FileSets/v3.12/Multi.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Multi.qml \ No newline at end of file diff --git a/FileSets/v3.12/OverviewBox.qml b/FileSets/v3.12/OverviewBox.qml deleted file mode 120000 index 96aa4214..00000000 --- a/FileSets/v3.12/OverviewBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewBox.qml \ No newline at end of file diff --git a/FileSets/v3.12/OverviewConnection.qml b/FileSets/v3.12/OverviewConnection.qml deleted file mode 120000 index d833fd05..00000000 --- a/FileSets/v3.12/OverviewConnection.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewConnection.qml \ No newline at end of file diff --git a/FileSets/v3.12/OverviewConnectionEnd.qml b/FileSets/v3.12/OverviewConnectionEnd.qml deleted file mode 120000 index 50c4b471..00000000 --- a/FileSets/v3.12/OverviewConnectionEnd.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewConnectionEnd.qml \ No newline at end of file diff --git a/FileSets/v3.12/OverviewSolarCharger.qml b/FileSets/v3.12/OverviewSolarCharger.qml deleted file mode 120000 index 7353cec9..00000000 --- a/FileSets/v3.12/OverviewSolarCharger.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewSolarCharger.qml \ No newline at end of file diff --git a/FileSets/v3.12/OverviewSolarInverter.qml b/FileSets/v3.12/OverviewSolarInverter.qml deleted file mode 120000 index 4048703e..00000000 --- a/FileSets/v3.12/OverviewSolarInverter.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewSolarInverter.qml \ No newline at end of file diff --git a/FileSets/v3.12/OverviewTankDelegate.qml b/FileSets/v3.12/OverviewTankDelegate.qml deleted file mode 120000 index faeb9aeb..00000000 --- a/FileSets/v3.12/OverviewTankDelegate.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewTankDelegate.qml \ No newline at end of file diff --git a/FileSets/v3.12/OverviewTanks.qml b/FileSets/v3.12/OverviewTanks.qml deleted file mode 120000 index 645a324b..00000000 --- a/FileSets/v3.12/OverviewTanks.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewTanks.qml \ No newline at end of file diff --git a/FileSets/v3.12/PageDigitalInput.qml b/FileSets/v3.12/PageDigitalInput.qml deleted file mode 120000 index 5b768f8f..00000000 --- a/FileSets/v3.12/PageDigitalInput.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/PageDigitalInput.qml \ No newline at end of file diff --git a/FileSets/v3.12/PageMain.qml b/FileSets/v3.12/PageMain.qml deleted file mode 120000 index 2f68fb1c..00000000 --- a/FileSets/v3.12/PageMain.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.14/PageMain.qml \ No newline at end of file diff --git a/FileSets/v3.12/PageSettingsDisplay.qml b/FileSets/v3.12/PageSettingsDisplay.qml deleted file mode 120000 index 9860c031..00000000 --- a/FileSets/v3.12/PageSettingsDisplay.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.14/PageSettingsDisplay.qml \ No newline at end of file diff --git a/FileSets/v3.12/Tile.qml b/FileSets/v3.12/Tile.qml deleted file mode 120000 index 0558cecf..00000000 --- a/FileSets/v3.12/Tile.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Tile.qml \ No newline at end of file diff --git a/FileSets/v3.12/TileText.qml b/FileSets/v3.12/TileText.qml deleted file mode 120000 index 8d7c0778..00000000 --- a/FileSets/v3.12/TileText.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/TileText.qml \ No newline at end of file diff --git a/FileSets/v3.12/attributes.csv b/FileSets/v3.12/attributes.csv deleted file mode 120000 index 61a61302..00000000 --- a/FileSets/v3.12/attributes.csv +++ /dev/null @@ -1 +0,0 @@ -../v3.14/attributes.csv \ No newline at end of file diff --git a/FileSets/v3.12/dbus_generator.py b/FileSets/v3.12/dbus_generator.py deleted file mode 120000 index b22d702a..00000000 --- a/FileSets/v3.12/dbus_generator.py +++ /dev/null @@ -1 +0,0 @@ -../v3.14/dbus_generator.py \ No newline at end of file diff --git a/FileSets/v3.12/main.qml b/FileSets/v3.12/main.qml deleted file mode 120000 index 492167a2..00000000 --- a/FileSets/v3.12/main.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.22/main.qml \ No newline at end of file diff --git a/FileSets/v3.12/styles.css b/FileSets/v3.12/styles.css deleted file mode 120000 index ef8557ec..00000000 --- a/FileSets/v3.12/styles.css +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/styles.css \ No newline at end of file diff --git a/FileSets/v3.13/Battery.qml b/FileSets/v3.13/Battery.qml deleted file mode 120000 index fafc7b36..00000000 --- a/FileSets/v3.13/Battery.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Battery.qml \ No newline at end of file diff --git a/FileSets/v3.13/MbEditBox.qml b/FileSets/v3.13/MbEditBox.qml deleted file mode 120000 index 8381b7d5..00000000 --- a/FileSets/v3.13/MbEditBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~10/MbEditBox.qml \ No newline at end of file diff --git a/FileSets/v3.13/MbEditBoxDateTime.qml b/FileSets/v3.13/MbEditBoxDateTime.qml deleted file mode 120000 index 7fb351ea..00000000 --- a/FileSets/v3.13/MbEditBoxDateTime.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbEditBoxDateTime.qml \ No newline at end of file diff --git a/FileSets/v3.13/MbItem.qml b/FileSets/v3.13/MbItem.qml deleted file mode 120000 index 048a313c..00000000 --- a/FileSets/v3.13/MbItem.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbItem.qml \ No newline at end of file diff --git a/FileSets/v3.13/MbItemDigitalInput.qml b/FileSets/v3.13/MbItemDigitalInput.qml deleted file mode 120000 index 4404b520..00000000 --- a/FileSets/v3.13/MbItemDigitalInput.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbItemDigitalInput.qml \ No newline at end of file diff --git a/FileSets/v3.13/MbSpinBox.qml b/FileSets/v3.13/MbSpinBox.qml deleted file mode 120000 index bc7530c8..00000000 --- a/FileSets/v3.13/MbSpinBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbSpinBox.qml \ No newline at end of file diff --git a/FileSets/v3.13/MbStyle.qml b/FileSets/v3.13/MbStyle.qml deleted file mode 120000 index d1940446..00000000 --- a/FileSets/v3.13/MbStyle.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbStyle.qml \ No newline at end of file diff --git a/FileSets/v3.13/MbSubMenu.qml b/FileSets/v3.13/MbSubMenu.qml deleted file mode 120000 index 35516a9a..00000000 --- a/FileSets/v3.13/MbSubMenu.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbSubMenu.qml \ No newline at end of file diff --git a/FileSets/v3.13/Multi.qml b/FileSets/v3.13/Multi.qml deleted file mode 120000 index aa938460..00000000 --- a/FileSets/v3.13/Multi.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Multi.qml \ No newline at end of file diff --git a/FileSets/v3.13/OverviewBox.qml b/FileSets/v3.13/OverviewBox.qml deleted file mode 120000 index 96aa4214..00000000 --- a/FileSets/v3.13/OverviewBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewBox.qml \ No newline at end of file diff --git a/FileSets/v3.13/OverviewConnection.qml b/FileSets/v3.13/OverviewConnection.qml deleted file mode 120000 index d833fd05..00000000 --- a/FileSets/v3.13/OverviewConnection.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewConnection.qml \ No newline at end of file diff --git a/FileSets/v3.13/OverviewConnectionEnd.qml b/FileSets/v3.13/OverviewConnectionEnd.qml deleted file mode 120000 index 50c4b471..00000000 --- a/FileSets/v3.13/OverviewConnectionEnd.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewConnectionEnd.qml \ No newline at end of file diff --git a/FileSets/v3.13/OverviewSolarCharger.qml b/FileSets/v3.13/OverviewSolarCharger.qml deleted file mode 120000 index 7353cec9..00000000 --- a/FileSets/v3.13/OverviewSolarCharger.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewSolarCharger.qml \ No newline at end of file diff --git a/FileSets/v3.13/OverviewSolarInverter.qml b/FileSets/v3.13/OverviewSolarInverter.qml deleted file mode 120000 index 4048703e..00000000 --- a/FileSets/v3.13/OverviewSolarInverter.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewSolarInverter.qml \ No newline at end of file diff --git a/FileSets/v3.13/OverviewTankDelegate.qml b/FileSets/v3.13/OverviewTankDelegate.qml deleted file mode 120000 index faeb9aeb..00000000 --- a/FileSets/v3.13/OverviewTankDelegate.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewTankDelegate.qml \ No newline at end of file diff --git a/FileSets/v3.13/OverviewTanks.qml b/FileSets/v3.13/OverviewTanks.qml deleted file mode 120000 index 645a324b..00000000 --- a/FileSets/v3.13/OverviewTanks.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewTanks.qml \ No newline at end of file diff --git a/FileSets/v3.13/PageDigitalInput.qml b/FileSets/v3.13/PageDigitalInput.qml deleted file mode 120000 index 5b768f8f..00000000 --- a/FileSets/v3.13/PageDigitalInput.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/PageDigitalInput.qml \ No newline at end of file diff --git a/FileSets/v3.13/PageMain.qml b/FileSets/v3.13/PageMain.qml deleted file mode 120000 index 2f68fb1c..00000000 --- a/FileSets/v3.13/PageMain.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.14/PageMain.qml \ No newline at end of file diff --git a/FileSets/v3.13/PageSettingsDisplay.qml b/FileSets/v3.13/PageSettingsDisplay.qml deleted file mode 120000 index 9860c031..00000000 --- a/FileSets/v3.13/PageSettingsDisplay.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.14/PageSettingsDisplay.qml \ No newline at end of file diff --git a/FileSets/v3.13/Tile.qml b/FileSets/v3.13/Tile.qml deleted file mode 120000 index 0558cecf..00000000 --- a/FileSets/v3.13/Tile.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Tile.qml \ No newline at end of file diff --git a/FileSets/v3.13/TileText.qml b/FileSets/v3.13/TileText.qml deleted file mode 120000 index 8d7c0778..00000000 --- a/FileSets/v3.13/TileText.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/TileText.qml \ No newline at end of file diff --git a/FileSets/v3.13/attributes.csv b/FileSets/v3.13/attributes.csv deleted file mode 120000 index 61a61302..00000000 --- a/FileSets/v3.13/attributes.csv +++ /dev/null @@ -1 +0,0 @@ -../v3.14/attributes.csv \ No newline at end of file diff --git a/FileSets/v3.13/dbus_generator.py b/FileSets/v3.13/dbus_generator.py deleted file mode 120000 index b22d702a..00000000 --- a/FileSets/v3.13/dbus_generator.py +++ /dev/null @@ -1 +0,0 @@ -../v3.14/dbus_generator.py \ No newline at end of file diff --git a/FileSets/v3.13/dbus_systemcalc.py b/FileSets/v3.13/dbus_systemcalc.py deleted file mode 120000 index 7595dd75..00000000 --- a/FileSets/v3.13/dbus_systemcalc.py +++ /dev/null @@ -1 +0,0 @@ -../v3.14/dbus_systemcalc.py \ No newline at end of file diff --git a/FileSets/v3.13/main.qml b/FileSets/v3.13/main.qml deleted file mode 120000 index 492167a2..00000000 --- a/FileSets/v3.13/main.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.22/main.qml \ No newline at end of file diff --git a/FileSets/v3.13/styles.css b/FileSets/v3.13/styles.css deleted file mode 120000 index ef8557ec..00000000 --- a/FileSets/v3.13/styles.css +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/styles.css \ No newline at end of file diff --git a/FileSets/v3.14/Battery.qml b/FileSets/v3.14/Battery.qml deleted file mode 120000 index fafc7b36..00000000 --- a/FileSets/v3.14/Battery.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Battery.qml \ No newline at end of file diff --git a/FileSets/v3.14/MbEditBox.qml b/FileSets/v3.14/MbEditBox.qml deleted file mode 120000 index 8381b7d5..00000000 --- a/FileSets/v3.14/MbEditBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~10/MbEditBox.qml \ No newline at end of file diff --git a/FileSets/v3.14/MbEditBoxDateTime.qml b/FileSets/v3.14/MbEditBoxDateTime.qml deleted file mode 120000 index 7fb351ea..00000000 --- a/FileSets/v3.14/MbEditBoxDateTime.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbEditBoxDateTime.qml \ No newline at end of file diff --git a/FileSets/v3.14/MbItem.qml b/FileSets/v3.14/MbItem.qml deleted file mode 120000 index 048a313c..00000000 --- a/FileSets/v3.14/MbItem.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbItem.qml \ No newline at end of file diff --git a/FileSets/v3.14/MbItemDigitalInput.qml b/FileSets/v3.14/MbItemDigitalInput.qml deleted file mode 120000 index 4404b520..00000000 --- a/FileSets/v3.14/MbItemDigitalInput.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbItemDigitalInput.qml \ No newline at end of file diff --git a/FileSets/v3.14/MbSpinBox.qml b/FileSets/v3.14/MbSpinBox.qml deleted file mode 120000 index bc7530c8..00000000 --- a/FileSets/v3.14/MbSpinBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbSpinBox.qml \ No newline at end of file diff --git a/FileSets/v3.14/MbStyle.qml b/FileSets/v3.14/MbStyle.qml deleted file mode 120000 index d1940446..00000000 --- a/FileSets/v3.14/MbStyle.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbStyle.qml \ No newline at end of file diff --git a/FileSets/v3.14/MbSubMenu.qml b/FileSets/v3.14/MbSubMenu.qml deleted file mode 120000 index 35516a9a..00000000 --- a/FileSets/v3.14/MbSubMenu.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbSubMenu.qml \ No newline at end of file diff --git a/FileSets/v3.14/Multi.qml b/FileSets/v3.14/Multi.qml deleted file mode 120000 index aa938460..00000000 --- a/FileSets/v3.14/Multi.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Multi.qml \ No newline at end of file diff --git a/FileSets/v3.14/OverviewBox.qml b/FileSets/v3.14/OverviewBox.qml deleted file mode 120000 index 96aa4214..00000000 --- a/FileSets/v3.14/OverviewBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewBox.qml \ No newline at end of file diff --git a/FileSets/v3.14/OverviewConnection.qml b/FileSets/v3.14/OverviewConnection.qml deleted file mode 120000 index d833fd05..00000000 --- a/FileSets/v3.14/OverviewConnection.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewConnection.qml \ No newline at end of file diff --git a/FileSets/v3.14/OverviewConnectionEnd.qml b/FileSets/v3.14/OverviewConnectionEnd.qml deleted file mode 120000 index 50c4b471..00000000 --- a/FileSets/v3.14/OverviewConnectionEnd.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewConnectionEnd.qml \ No newline at end of file diff --git a/FileSets/v3.14/OverviewSolarCharger.qml b/FileSets/v3.14/OverviewSolarCharger.qml deleted file mode 120000 index 7353cec9..00000000 --- a/FileSets/v3.14/OverviewSolarCharger.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewSolarCharger.qml \ No newline at end of file diff --git a/FileSets/v3.14/OverviewSolarInverter.qml b/FileSets/v3.14/OverviewSolarInverter.qml deleted file mode 120000 index 4048703e..00000000 --- a/FileSets/v3.14/OverviewSolarInverter.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewSolarInverter.qml \ No newline at end of file diff --git a/FileSets/v3.14/OverviewTankDelegate.qml b/FileSets/v3.14/OverviewTankDelegate.qml deleted file mode 120000 index faeb9aeb..00000000 --- a/FileSets/v3.14/OverviewTankDelegate.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewTankDelegate.qml \ No newline at end of file diff --git a/FileSets/v3.14/OverviewTanks.qml b/FileSets/v3.14/OverviewTanks.qml deleted file mode 120000 index 645a324b..00000000 --- a/FileSets/v3.14/OverviewTanks.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewTanks.qml \ No newline at end of file diff --git a/FileSets/v3.14/PageDigitalInput.qml b/FileSets/v3.14/PageDigitalInput.qml deleted file mode 120000 index 5b768f8f..00000000 --- a/FileSets/v3.14/PageDigitalInput.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/PageDigitalInput.qml \ No newline at end of file diff --git a/FileSets/v3.14/Tile.qml b/FileSets/v3.14/Tile.qml deleted file mode 120000 index 0558cecf..00000000 --- a/FileSets/v3.14/Tile.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Tile.qml \ No newline at end of file diff --git a/FileSets/v3.14/TileText.qml b/FileSets/v3.14/TileText.qml deleted file mode 120000 index 8d7c0778..00000000 --- a/FileSets/v3.14/TileText.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/TileText.qml \ No newline at end of file diff --git a/FileSets/v3.14/main.qml b/FileSets/v3.14/main.qml deleted file mode 120000 index 492167a2..00000000 --- a/FileSets/v3.14/main.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.22/main.qml \ No newline at end of file diff --git a/FileSets/v3.14/styles.css b/FileSets/v3.14/styles.css deleted file mode 120000 index ef8557ec..00000000 --- a/FileSets/v3.14/styles.css +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/styles.css \ No newline at end of file diff --git a/FileSets/v3.20/Battery.qml b/FileSets/v3.20/Battery.qml deleted file mode 120000 index fafc7b36..00000000 --- a/FileSets/v3.20/Battery.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Battery.qml \ No newline at end of file diff --git a/FileSets/v3.20/MbEditBox.qml b/FileSets/v3.20/MbEditBox.qml deleted file mode 120000 index 8381b7d5..00000000 --- a/FileSets/v3.20/MbEditBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~10/MbEditBox.qml \ No newline at end of file diff --git a/FileSets/v3.20/MbEditBoxDateTime.qml b/FileSets/v3.20/MbEditBoxDateTime.qml deleted file mode 120000 index 7fb351ea..00000000 --- a/FileSets/v3.20/MbEditBoxDateTime.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbEditBoxDateTime.qml \ No newline at end of file diff --git a/FileSets/v3.20/MbItem.qml b/FileSets/v3.20/MbItem.qml deleted file mode 120000 index 048a313c..00000000 --- a/FileSets/v3.20/MbItem.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbItem.qml \ No newline at end of file diff --git a/FileSets/v3.20/MbItemDigitalInput.qml b/FileSets/v3.20/MbItemDigitalInput.qml deleted file mode 120000 index 4404b520..00000000 --- a/FileSets/v3.20/MbItemDigitalInput.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbItemDigitalInput.qml \ No newline at end of file diff --git a/FileSets/v3.20/MbSpinBox.qml b/FileSets/v3.20/MbSpinBox.qml deleted file mode 120000 index bc7530c8..00000000 --- a/FileSets/v3.20/MbSpinBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbSpinBox.qml \ No newline at end of file diff --git a/FileSets/v3.20/MbStyle.qml b/FileSets/v3.20/MbStyle.qml deleted file mode 120000 index d1940446..00000000 --- a/FileSets/v3.20/MbStyle.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbStyle.qml \ No newline at end of file diff --git a/FileSets/v3.20/MbSubMenu.qml b/FileSets/v3.20/MbSubMenu.qml deleted file mode 120000 index 35516a9a..00000000 --- a/FileSets/v3.20/MbSubMenu.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbSubMenu.qml \ No newline at end of file diff --git a/FileSets/v3.20/Multi.qml b/FileSets/v3.20/Multi.qml deleted file mode 120000 index aa938460..00000000 --- a/FileSets/v3.20/Multi.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Multi.qml \ No newline at end of file diff --git a/FileSets/v3.20/OverviewBox.qml b/FileSets/v3.20/OverviewBox.qml deleted file mode 120000 index 96aa4214..00000000 --- a/FileSets/v3.20/OverviewBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewBox.qml \ No newline at end of file diff --git a/FileSets/v3.20/OverviewConnection.qml b/FileSets/v3.20/OverviewConnection.qml deleted file mode 120000 index d833fd05..00000000 --- a/FileSets/v3.20/OverviewConnection.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewConnection.qml \ No newline at end of file diff --git a/FileSets/v3.20/OverviewConnectionEnd.qml b/FileSets/v3.20/OverviewConnectionEnd.qml deleted file mode 120000 index 50c4b471..00000000 --- a/FileSets/v3.20/OverviewConnectionEnd.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewConnectionEnd.qml \ No newline at end of file diff --git a/FileSets/v3.20/OverviewSolarCharger.qml b/FileSets/v3.20/OverviewSolarCharger.qml deleted file mode 120000 index 7353cec9..00000000 --- a/FileSets/v3.20/OverviewSolarCharger.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewSolarCharger.qml \ No newline at end of file diff --git a/FileSets/v3.20/OverviewSolarInverter.qml b/FileSets/v3.20/OverviewSolarInverter.qml deleted file mode 120000 index 4048703e..00000000 --- a/FileSets/v3.20/OverviewSolarInverter.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewSolarInverter.qml \ No newline at end of file diff --git a/FileSets/v3.20/OverviewTankDelegate.qml b/FileSets/v3.20/OverviewTankDelegate.qml deleted file mode 120000 index faeb9aeb..00000000 --- a/FileSets/v3.20/OverviewTankDelegate.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewTankDelegate.qml \ No newline at end of file diff --git a/FileSets/v3.20/OverviewTanks.qml b/FileSets/v3.20/OverviewTanks.qml deleted file mode 120000 index 645a324b..00000000 --- a/FileSets/v3.20/OverviewTanks.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewTanks.qml \ No newline at end of file diff --git a/FileSets/v3.20/PageDigitalInput.qml b/FileSets/v3.20/PageDigitalInput.qml deleted file mode 120000 index 5b768f8f..00000000 --- a/FileSets/v3.20/PageDigitalInput.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/PageDigitalInput.qml \ No newline at end of file diff --git a/FileSets/v3.20/PageMain.qml b/FileSets/v3.20/PageMain.qml deleted file mode 120000 index 4839c5d8..00000000 --- a/FileSets/v3.20/PageMain.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.22/PageMain.qml \ No newline at end of file diff --git a/FileSets/v3.20/PageSettingsDisplay.qml b/FileSets/v3.20/PageSettingsDisplay.qml deleted file mode 120000 index a3d107e3..00000000 --- a/FileSets/v3.20/PageSettingsDisplay.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.22/PageSettingsDisplay.qml \ No newline at end of file diff --git a/FileSets/v3.20/Tile.qml b/FileSets/v3.20/Tile.qml deleted file mode 120000 index 0558cecf..00000000 --- a/FileSets/v3.20/Tile.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Tile.qml \ No newline at end of file diff --git a/FileSets/v3.20/TileText.qml b/FileSets/v3.20/TileText.qml deleted file mode 120000 index 8d7c0778..00000000 --- a/FileSets/v3.20/TileText.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/TileText.qml \ No newline at end of file diff --git a/FileSets/v3.20/attributes.csv b/FileSets/v3.20/attributes.csv deleted file mode 120000 index 2e8b8b10..00000000 --- a/FileSets/v3.20/attributes.csv +++ /dev/null @@ -1 +0,0 @@ -../v3.22/attributes.csv \ No newline at end of file diff --git a/FileSets/v3.20/dbus_generator.py b/FileSets/v3.20/dbus_generator.py deleted file mode 120000 index fc553c91..00000000 --- a/FileSets/v3.20/dbus_generator.py +++ /dev/null @@ -1 +0,0 @@ -../v3.40/dbus_generator.py \ No newline at end of file diff --git a/FileSets/v3.20/dbus_systemcalc.py b/FileSets/v3.20/dbus_systemcalc.py deleted file mode 120000 index c4df2742..00000000 --- a/FileSets/v3.20/dbus_systemcalc.py +++ /dev/null @@ -1 +0,0 @@ -../v3.22/dbus_systemcalc.py \ No newline at end of file diff --git a/FileSets/v3.20/main.qml b/FileSets/v3.20/main.qml deleted file mode 120000 index 492167a2..00000000 --- a/FileSets/v3.20/main.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.22/main.qml \ No newline at end of file diff --git a/FileSets/v3.20/styles.css b/FileSets/v3.20/styles.css deleted file mode 120000 index ef8557ec..00000000 --- a/FileSets/v3.20/styles.css +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/styles.css \ No newline at end of file diff --git a/FileSets/v3.21/Battery.qml b/FileSets/v3.21/Battery.qml deleted file mode 120000 index fafc7b36..00000000 --- a/FileSets/v3.21/Battery.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Battery.qml \ No newline at end of file diff --git a/FileSets/v3.21/MbEditBox.qml b/FileSets/v3.21/MbEditBox.qml deleted file mode 120000 index 8381b7d5..00000000 --- a/FileSets/v3.21/MbEditBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~10/MbEditBox.qml \ No newline at end of file diff --git a/FileSets/v3.21/MbEditBoxDateTime.qml b/FileSets/v3.21/MbEditBoxDateTime.qml deleted file mode 120000 index 7fb351ea..00000000 --- a/FileSets/v3.21/MbEditBoxDateTime.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbEditBoxDateTime.qml \ No newline at end of file diff --git a/FileSets/v3.21/MbItem.qml b/FileSets/v3.21/MbItem.qml deleted file mode 120000 index 048a313c..00000000 --- a/FileSets/v3.21/MbItem.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbItem.qml \ No newline at end of file diff --git a/FileSets/v3.21/MbItemDigitalInput.qml b/FileSets/v3.21/MbItemDigitalInput.qml deleted file mode 120000 index 4404b520..00000000 --- a/FileSets/v3.21/MbItemDigitalInput.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbItemDigitalInput.qml \ No newline at end of file diff --git a/FileSets/v3.21/MbSpinBox.qml b/FileSets/v3.21/MbSpinBox.qml deleted file mode 120000 index bc7530c8..00000000 --- a/FileSets/v3.21/MbSpinBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbSpinBox.qml \ No newline at end of file diff --git a/FileSets/v3.21/MbStyle.qml b/FileSets/v3.21/MbStyle.qml deleted file mode 120000 index d1940446..00000000 --- a/FileSets/v3.21/MbStyle.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbStyle.qml \ No newline at end of file diff --git a/FileSets/v3.21/MbSubMenu.qml b/FileSets/v3.21/MbSubMenu.qml deleted file mode 120000 index 35516a9a..00000000 --- a/FileSets/v3.21/MbSubMenu.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbSubMenu.qml \ No newline at end of file diff --git a/FileSets/v3.21/Multi.qml b/FileSets/v3.21/Multi.qml deleted file mode 120000 index aa938460..00000000 --- a/FileSets/v3.21/Multi.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Multi.qml \ No newline at end of file diff --git a/FileSets/v3.21/OverviewBox.qml b/FileSets/v3.21/OverviewBox.qml deleted file mode 120000 index 96aa4214..00000000 --- a/FileSets/v3.21/OverviewBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewBox.qml \ No newline at end of file diff --git a/FileSets/v3.21/OverviewConnection.qml b/FileSets/v3.21/OverviewConnection.qml deleted file mode 120000 index d833fd05..00000000 --- a/FileSets/v3.21/OverviewConnection.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewConnection.qml \ No newline at end of file diff --git a/FileSets/v3.21/OverviewConnectionEnd.qml b/FileSets/v3.21/OverviewConnectionEnd.qml deleted file mode 120000 index 50c4b471..00000000 --- a/FileSets/v3.21/OverviewConnectionEnd.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewConnectionEnd.qml \ No newline at end of file diff --git a/FileSets/v3.21/OverviewSolarCharger.qml b/FileSets/v3.21/OverviewSolarCharger.qml deleted file mode 120000 index 7353cec9..00000000 --- a/FileSets/v3.21/OverviewSolarCharger.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewSolarCharger.qml \ No newline at end of file diff --git a/FileSets/v3.21/OverviewSolarInverter.qml b/FileSets/v3.21/OverviewSolarInverter.qml deleted file mode 120000 index 4048703e..00000000 --- a/FileSets/v3.21/OverviewSolarInverter.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewSolarInverter.qml \ No newline at end of file diff --git a/FileSets/v3.21/OverviewTankDelegate.qml b/FileSets/v3.21/OverviewTankDelegate.qml deleted file mode 120000 index faeb9aeb..00000000 --- a/FileSets/v3.21/OverviewTankDelegate.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewTankDelegate.qml \ No newline at end of file diff --git a/FileSets/v3.21/OverviewTanks.qml b/FileSets/v3.21/OverviewTanks.qml deleted file mode 120000 index 645a324b..00000000 --- a/FileSets/v3.21/OverviewTanks.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewTanks.qml \ No newline at end of file diff --git a/FileSets/v3.21/PageDigitalInput.qml b/FileSets/v3.21/PageDigitalInput.qml deleted file mode 120000 index 5b768f8f..00000000 --- a/FileSets/v3.21/PageDigitalInput.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/PageDigitalInput.qml \ No newline at end of file diff --git a/FileSets/v3.21/PageMain.qml b/FileSets/v3.21/PageMain.qml deleted file mode 120000 index 4839c5d8..00000000 --- a/FileSets/v3.21/PageMain.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.22/PageMain.qml \ No newline at end of file diff --git a/FileSets/v3.21/PageSettingsDisplay.qml b/FileSets/v3.21/PageSettingsDisplay.qml deleted file mode 120000 index a3d107e3..00000000 --- a/FileSets/v3.21/PageSettingsDisplay.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.22/PageSettingsDisplay.qml \ No newline at end of file diff --git a/FileSets/v3.21/Tile.qml b/FileSets/v3.21/Tile.qml deleted file mode 120000 index 0558cecf..00000000 --- a/FileSets/v3.21/Tile.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Tile.qml \ No newline at end of file diff --git a/FileSets/v3.21/TileText.qml b/FileSets/v3.21/TileText.qml deleted file mode 120000 index 8d7c0778..00000000 --- a/FileSets/v3.21/TileText.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/TileText.qml \ No newline at end of file diff --git a/FileSets/v3.21/attributes.csv b/FileSets/v3.21/attributes.csv deleted file mode 120000 index 2e8b8b10..00000000 --- a/FileSets/v3.21/attributes.csv +++ /dev/null @@ -1 +0,0 @@ -../v3.22/attributes.csv \ No newline at end of file diff --git a/FileSets/v3.21/dbus_generator.py b/FileSets/v3.21/dbus_generator.py deleted file mode 120000 index fc553c91..00000000 --- a/FileSets/v3.21/dbus_generator.py +++ /dev/null @@ -1 +0,0 @@ -../v3.40/dbus_generator.py \ No newline at end of file diff --git a/FileSets/v3.21/dbus_systemcalc.py b/FileSets/v3.21/dbus_systemcalc.py deleted file mode 120000 index c4df2742..00000000 --- a/FileSets/v3.21/dbus_systemcalc.py +++ /dev/null @@ -1 +0,0 @@ -../v3.22/dbus_systemcalc.py \ No newline at end of file diff --git a/FileSets/v3.21/main.qml b/FileSets/v3.21/main.qml deleted file mode 120000 index 492167a2..00000000 --- a/FileSets/v3.21/main.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.22/main.qml \ No newline at end of file diff --git a/FileSets/v3.21/styles.css b/FileSets/v3.21/styles.css deleted file mode 120000 index ef8557ec..00000000 --- a/FileSets/v3.21/styles.css +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/styles.css \ No newline at end of file diff --git a/FileSets/v3.22/Battery.qml b/FileSets/v3.22/Battery.qml deleted file mode 120000 index fafc7b36..00000000 --- a/FileSets/v3.22/Battery.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Battery.qml \ No newline at end of file diff --git a/FileSets/v3.22/MbEditBox.qml b/FileSets/v3.22/MbEditBox.qml deleted file mode 120000 index 8381b7d5..00000000 --- a/FileSets/v3.22/MbEditBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~10/MbEditBox.qml \ No newline at end of file diff --git a/FileSets/v3.22/MbEditBoxDateTime.qml b/FileSets/v3.22/MbEditBoxDateTime.qml deleted file mode 120000 index 7fb351ea..00000000 --- a/FileSets/v3.22/MbEditBoxDateTime.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbEditBoxDateTime.qml \ No newline at end of file diff --git a/FileSets/v3.22/MbItem.qml b/FileSets/v3.22/MbItem.qml deleted file mode 120000 index 048a313c..00000000 --- a/FileSets/v3.22/MbItem.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbItem.qml \ No newline at end of file diff --git a/FileSets/v3.22/MbItemDigitalInput.qml b/FileSets/v3.22/MbItemDigitalInput.qml deleted file mode 120000 index 4404b520..00000000 --- a/FileSets/v3.22/MbItemDigitalInput.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbItemDigitalInput.qml \ No newline at end of file diff --git a/FileSets/v3.22/MbSpinBox.qml b/FileSets/v3.22/MbSpinBox.qml deleted file mode 120000 index bc7530c8..00000000 --- a/FileSets/v3.22/MbSpinBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbSpinBox.qml \ No newline at end of file diff --git a/FileSets/v3.22/MbStyle.qml b/FileSets/v3.22/MbStyle.qml deleted file mode 120000 index d1940446..00000000 --- a/FileSets/v3.22/MbStyle.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbStyle.qml \ No newline at end of file diff --git a/FileSets/v3.22/MbSubMenu.qml b/FileSets/v3.22/MbSubMenu.qml deleted file mode 120000 index 35516a9a..00000000 --- a/FileSets/v3.22/MbSubMenu.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbSubMenu.qml \ No newline at end of file diff --git a/FileSets/v3.22/Multi.qml b/FileSets/v3.22/Multi.qml deleted file mode 120000 index aa938460..00000000 --- a/FileSets/v3.22/Multi.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Multi.qml \ No newline at end of file diff --git a/FileSets/v3.22/OverviewBox.qml b/FileSets/v3.22/OverviewBox.qml deleted file mode 120000 index 96aa4214..00000000 --- a/FileSets/v3.22/OverviewBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewBox.qml \ No newline at end of file diff --git a/FileSets/v3.22/OverviewConnection.qml b/FileSets/v3.22/OverviewConnection.qml deleted file mode 120000 index d833fd05..00000000 --- a/FileSets/v3.22/OverviewConnection.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewConnection.qml \ No newline at end of file diff --git a/FileSets/v3.22/OverviewConnectionEnd.qml b/FileSets/v3.22/OverviewConnectionEnd.qml deleted file mode 120000 index 50c4b471..00000000 --- a/FileSets/v3.22/OverviewConnectionEnd.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewConnectionEnd.qml \ No newline at end of file diff --git a/FileSets/v3.22/OverviewSolarCharger.qml b/FileSets/v3.22/OverviewSolarCharger.qml deleted file mode 120000 index 7353cec9..00000000 --- a/FileSets/v3.22/OverviewSolarCharger.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewSolarCharger.qml \ No newline at end of file diff --git a/FileSets/v3.22/OverviewSolarInverter.qml b/FileSets/v3.22/OverviewSolarInverter.qml deleted file mode 120000 index 4048703e..00000000 --- a/FileSets/v3.22/OverviewSolarInverter.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewSolarInverter.qml \ No newline at end of file diff --git a/FileSets/v3.22/OverviewTankDelegate.qml b/FileSets/v3.22/OverviewTankDelegate.qml deleted file mode 120000 index faeb9aeb..00000000 --- a/FileSets/v3.22/OverviewTankDelegate.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewTankDelegate.qml \ No newline at end of file diff --git a/FileSets/v3.22/OverviewTanks.qml b/FileSets/v3.22/OverviewTanks.qml deleted file mode 120000 index 645a324b..00000000 --- a/FileSets/v3.22/OverviewTanks.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewTanks.qml \ No newline at end of file diff --git a/FileSets/v3.22/PageDigitalInput.qml b/FileSets/v3.22/PageDigitalInput.qml deleted file mode 120000 index 5b768f8f..00000000 --- a/FileSets/v3.22/PageDigitalInput.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/PageDigitalInput.qml \ No newline at end of file diff --git a/FileSets/v3.22/Tile.qml b/FileSets/v3.22/Tile.qml deleted file mode 120000 index 0558cecf..00000000 --- a/FileSets/v3.22/Tile.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Tile.qml \ No newline at end of file diff --git a/FileSets/v3.22/TileText.qml b/FileSets/v3.22/TileText.qml deleted file mode 120000 index 8d7c0778..00000000 --- a/FileSets/v3.22/TileText.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/TileText.qml \ No newline at end of file diff --git a/FileSets/v3.22/dbus_generator.py b/FileSets/v3.22/dbus_generator.py deleted file mode 120000 index fc553c91..00000000 --- a/FileSets/v3.22/dbus_generator.py +++ /dev/null @@ -1 +0,0 @@ -../v3.40/dbus_generator.py \ No newline at end of file diff --git a/FileSets/v3.22/styles.css b/FileSets/v3.22/styles.css deleted file mode 120000 index ef8557ec..00000000 --- a/FileSets/v3.22/styles.css +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/styles.css \ No newline at end of file diff --git a/FileSets/v3.30/Battery.qml b/FileSets/v3.30/Battery.qml deleted file mode 120000 index fafc7b36..00000000 --- a/FileSets/v3.30/Battery.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Battery.qml \ No newline at end of file diff --git a/FileSets/v3.30/MbEditBox.qml b/FileSets/v3.30/MbEditBox.qml deleted file mode 120000 index 8381b7d5..00000000 --- a/FileSets/v3.30/MbEditBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~10/MbEditBox.qml \ No newline at end of file diff --git a/FileSets/v3.30/MbEditBoxDateTime.qml b/FileSets/v3.30/MbEditBoxDateTime.qml deleted file mode 120000 index 7fb351ea..00000000 --- a/FileSets/v3.30/MbEditBoxDateTime.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbEditBoxDateTime.qml \ No newline at end of file diff --git a/FileSets/v3.30/MbItem.qml b/FileSets/v3.30/MbItem.qml deleted file mode 120000 index 048a313c..00000000 --- a/FileSets/v3.30/MbItem.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbItem.qml \ No newline at end of file diff --git a/FileSets/v3.30/MbItemDigitalInput.qml b/FileSets/v3.30/MbItemDigitalInput.qml deleted file mode 120000 index 4404b520..00000000 --- a/FileSets/v3.30/MbItemDigitalInput.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbItemDigitalInput.qml \ No newline at end of file diff --git a/FileSets/v3.30/MbSpinBox.qml b/FileSets/v3.30/MbSpinBox.qml deleted file mode 120000 index bc7530c8..00000000 --- a/FileSets/v3.30/MbSpinBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbSpinBox.qml \ No newline at end of file diff --git a/FileSets/v3.30/MbStyle.qml b/FileSets/v3.30/MbStyle.qml deleted file mode 120000 index d1940446..00000000 --- a/FileSets/v3.30/MbStyle.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbStyle.qml \ No newline at end of file diff --git a/FileSets/v3.30/MbSubMenu.qml b/FileSets/v3.30/MbSubMenu.qml deleted file mode 120000 index 35516a9a..00000000 --- a/FileSets/v3.30/MbSubMenu.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbSubMenu.qml \ No newline at end of file diff --git a/FileSets/v3.30/Multi.qml b/FileSets/v3.30/Multi.qml deleted file mode 120000 index aa938460..00000000 --- a/FileSets/v3.30/Multi.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Multi.qml \ No newline at end of file diff --git a/FileSets/v3.30/OverviewBox.qml b/FileSets/v3.30/OverviewBox.qml deleted file mode 120000 index 96aa4214..00000000 --- a/FileSets/v3.30/OverviewBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewBox.qml \ No newline at end of file diff --git a/FileSets/v3.30/OverviewConnection.qml b/FileSets/v3.30/OverviewConnection.qml deleted file mode 120000 index d833fd05..00000000 --- a/FileSets/v3.30/OverviewConnection.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewConnection.qml \ No newline at end of file diff --git a/FileSets/v3.30/OverviewConnectionEnd.qml b/FileSets/v3.30/OverviewConnectionEnd.qml deleted file mode 120000 index 50c4b471..00000000 --- a/FileSets/v3.30/OverviewConnectionEnd.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewConnectionEnd.qml \ No newline at end of file diff --git a/FileSets/v3.30/OverviewSolarCharger.qml b/FileSets/v3.30/OverviewSolarCharger.qml deleted file mode 120000 index 7353cec9..00000000 --- a/FileSets/v3.30/OverviewSolarCharger.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewSolarCharger.qml \ No newline at end of file diff --git a/FileSets/v3.30/OverviewSolarInverter.qml b/FileSets/v3.30/OverviewSolarInverter.qml deleted file mode 120000 index 4048703e..00000000 --- a/FileSets/v3.30/OverviewSolarInverter.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewSolarInverter.qml \ No newline at end of file diff --git a/FileSets/v3.30/OverviewTankDelegate.qml b/FileSets/v3.30/OverviewTankDelegate.qml deleted file mode 120000 index faeb9aeb..00000000 --- a/FileSets/v3.30/OverviewTankDelegate.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewTankDelegate.qml \ No newline at end of file diff --git a/FileSets/v3.30/OverviewTanks.qml b/FileSets/v3.30/OverviewTanks.qml deleted file mode 120000 index 645a324b..00000000 --- a/FileSets/v3.30/OverviewTanks.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewTanks.qml \ No newline at end of file diff --git a/FileSets/v3.30/PageDigitalInput.qml b/FileSets/v3.30/PageDigitalInput.qml deleted file mode 120000 index 5b768f8f..00000000 --- a/FileSets/v3.30/PageDigitalInput.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/PageDigitalInput.qml \ No newline at end of file diff --git a/FileSets/v3.30/PageMain.qml b/FileSets/v3.30/PageMain.qml deleted file mode 120000 index 33fe83b0..00000000 --- a/FileSets/v3.30/PageMain.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~13/PageMain.qml \ No newline at end of file diff --git a/FileSets/v3.30/PageSettingsDisplay.qml b/FileSets/v3.30/PageSettingsDisplay.qml deleted file mode 120000 index fd80cffd..00000000 --- a/FileSets/v3.30/PageSettingsDisplay.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.34/PageSettingsDisplay.qml \ No newline at end of file diff --git a/FileSets/v3.30/Tile.qml b/FileSets/v3.30/Tile.qml deleted file mode 120000 index 0558cecf..00000000 --- a/FileSets/v3.30/Tile.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Tile.qml \ No newline at end of file diff --git a/FileSets/v3.30/TileText.qml b/FileSets/v3.30/TileText.qml deleted file mode 120000 index 8d7c0778..00000000 --- a/FileSets/v3.30/TileText.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/TileText.qml \ No newline at end of file diff --git a/FileSets/v3.30/attributes.csv b/FileSets/v3.30/attributes.csv deleted file mode 120000 index e226e13c..00000000 --- a/FileSets/v3.30/attributes.csv +++ /dev/null @@ -1 +0,0 @@ -../v3.40~6/attributes.csv \ No newline at end of file diff --git a/FileSets/v3.30/dbus_generator.py b/FileSets/v3.30/dbus_generator.py deleted file mode 120000 index fc553c91..00000000 --- a/FileSets/v3.30/dbus_generator.py +++ /dev/null @@ -1 +0,0 @@ -../v3.40/dbus_generator.py \ No newline at end of file diff --git a/FileSets/v3.30/dbus_systemcalc.py b/FileSets/v3.30/dbus_systemcalc.py deleted file mode 120000 index 1a9e3b3a..00000000 --- a/FileSets/v3.30/dbus_systemcalc.py +++ /dev/null @@ -1 +0,0 @@ -../v3.40~1/dbus_systemcalc.py \ No newline at end of file diff --git a/FileSets/v3.30/main.qml b/FileSets/v3.30/main.qml deleted file mode 120000 index 916fdfbf..00000000 --- a/FileSets/v3.30/main.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~13/main.qml \ No newline at end of file diff --git a/FileSets/v3.30/styles.css b/FileSets/v3.30/styles.css deleted file mode 120000 index ef8557ec..00000000 --- a/FileSets/v3.30/styles.css +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/styles.css \ No newline at end of file diff --git a/FileSets/v3.31/Battery.qml b/FileSets/v3.31/Battery.qml deleted file mode 120000 index fafc7b36..00000000 --- a/FileSets/v3.31/Battery.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Battery.qml \ No newline at end of file diff --git a/FileSets/v3.31/MbEditBox.qml b/FileSets/v3.31/MbEditBox.qml deleted file mode 120000 index 8381b7d5..00000000 --- a/FileSets/v3.31/MbEditBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~10/MbEditBox.qml \ No newline at end of file diff --git a/FileSets/v3.31/MbEditBoxDateTime.qml b/FileSets/v3.31/MbEditBoxDateTime.qml deleted file mode 120000 index 7fb351ea..00000000 --- a/FileSets/v3.31/MbEditBoxDateTime.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbEditBoxDateTime.qml \ No newline at end of file diff --git a/FileSets/v3.31/MbItem.qml b/FileSets/v3.31/MbItem.qml deleted file mode 120000 index 048a313c..00000000 --- a/FileSets/v3.31/MbItem.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbItem.qml \ No newline at end of file diff --git a/FileSets/v3.31/MbItemDigitalInput.qml b/FileSets/v3.31/MbItemDigitalInput.qml deleted file mode 120000 index 4404b520..00000000 --- a/FileSets/v3.31/MbItemDigitalInput.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbItemDigitalInput.qml \ No newline at end of file diff --git a/FileSets/v3.31/MbSpinBox.qml b/FileSets/v3.31/MbSpinBox.qml deleted file mode 120000 index bc7530c8..00000000 --- a/FileSets/v3.31/MbSpinBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbSpinBox.qml \ No newline at end of file diff --git a/FileSets/v3.31/MbStyle.qml b/FileSets/v3.31/MbStyle.qml deleted file mode 120000 index d1940446..00000000 --- a/FileSets/v3.31/MbStyle.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbStyle.qml \ No newline at end of file diff --git a/FileSets/v3.31/MbSubMenu.qml b/FileSets/v3.31/MbSubMenu.qml deleted file mode 120000 index 35516a9a..00000000 --- a/FileSets/v3.31/MbSubMenu.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbSubMenu.qml \ No newline at end of file diff --git a/FileSets/v3.31/Multi.qml b/FileSets/v3.31/Multi.qml deleted file mode 120000 index aa938460..00000000 --- a/FileSets/v3.31/Multi.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Multi.qml \ No newline at end of file diff --git a/FileSets/v3.31/OverviewBox.qml b/FileSets/v3.31/OverviewBox.qml deleted file mode 120000 index 96aa4214..00000000 --- a/FileSets/v3.31/OverviewBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewBox.qml \ No newline at end of file diff --git a/FileSets/v3.31/OverviewConnection.qml b/FileSets/v3.31/OverviewConnection.qml deleted file mode 120000 index d833fd05..00000000 --- a/FileSets/v3.31/OverviewConnection.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewConnection.qml \ No newline at end of file diff --git a/FileSets/v3.31/OverviewConnectionEnd.qml b/FileSets/v3.31/OverviewConnectionEnd.qml deleted file mode 120000 index 50c4b471..00000000 --- a/FileSets/v3.31/OverviewConnectionEnd.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewConnectionEnd.qml \ No newline at end of file diff --git a/FileSets/v3.31/OverviewSolarCharger.qml b/FileSets/v3.31/OverviewSolarCharger.qml deleted file mode 120000 index 7353cec9..00000000 --- a/FileSets/v3.31/OverviewSolarCharger.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewSolarCharger.qml \ No newline at end of file diff --git a/FileSets/v3.31/OverviewSolarInverter.qml b/FileSets/v3.31/OverviewSolarInverter.qml deleted file mode 120000 index 4048703e..00000000 --- a/FileSets/v3.31/OverviewSolarInverter.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewSolarInverter.qml \ No newline at end of file diff --git a/FileSets/v3.31/OverviewTankDelegate.qml b/FileSets/v3.31/OverviewTankDelegate.qml deleted file mode 120000 index faeb9aeb..00000000 --- a/FileSets/v3.31/OverviewTankDelegate.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewTankDelegate.qml \ No newline at end of file diff --git a/FileSets/v3.31/OverviewTanks.qml b/FileSets/v3.31/OverviewTanks.qml deleted file mode 120000 index 645a324b..00000000 --- a/FileSets/v3.31/OverviewTanks.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewTanks.qml \ No newline at end of file diff --git a/FileSets/v3.31/PageDigitalInput.qml b/FileSets/v3.31/PageDigitalInput.qml deleted file mode 120000 index 5b768f8f..00000000 --- a/FileSets/v3.31/PageDigitalInput.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/PageDigitalInput.qml \ No newline at end of file diff --git a/FileSets/v3.31/PageMain.qml b/FileSets/v3.31/PageMain.qml deleted file mode 120000 index 33fe83b0..00000000 --- a/FileSets/v3.31/PageMain.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~13/PageMain.qml \ No newline at end of file diff --git a/FileSets/v3.31/PageSettingsDisplay.qml b/FileSets/v3.31/PageSettingsDisplay.qml deleted file mode 120000 index fd80cffd..00000000 --- a/FileSets/v3.31/PageSettingsDisplay.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.34/PageSettingsDisplay.qml \ No newline at end of file diff --git a/FileSets/v3.31/Tile.qml b/FileSets/v3.31/Tile.qml deleted file mode 120000 index 0558cecf..00000000 --- a/FileSets/v3.31/Tile.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Tile.qml \ No newline at end of file diff --git a/FileSets/v3.31/TileText.qml b/FileSets/v3.31/TileText.qml deleted file mode 120000 index 8d7c0778..00000000 --- a/FileSets/v3.31/TileText.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/TileText.qml \ No newline at end of file diff --git a/FileSets/v3.31/attributes.csv b/FileSets/v3.31/attributes.csv deleted file mode 120000 index e226e13c..00000000 --- a/FileSets/v3.31/attributes.csv +++ /dev/null @@ -1 +0,0 @@ -../v3.40~6/attributes.csv \ No newline at end of file diff --git a/FileSets/v3.31/dbus_generator.py b/FileSets/v3.31/dbus_generator.py deleted file mode 120000 index fc553c91..00000000 --- a/FileSets/v3.31/dbus_generator.py +++ /dev/null @@ -1 +0,0 @@ -../v3.40/dbus_generator.py \ No newline at end of file diff --git a/FileSets/v3.31/dbus_systemcalc.py b/FileSets/v3.31/dbus_systemcalc.py deleted file mode 120000 index 1a9e3b3a..00000000 --- a/FileSets/v3.31/dbus_systemcalc.py +++ /dev/null @@ -1 +0,0 @@ -../v3.40~1/dbus_systemcalc.py \ No newline at end of file diff --git a/FileSets/v3.31/main.qml b/FileSets/v3.31/main.qml deleted file mode 120000 index 916fdfbf..00000000 --- a/FileSets/v3.31/main.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~13/main.qml \ No newline at end of file diff --git a/FileSets/v3.31/styles.css b/FileSets/v3.31/styles.css deleted file mode 120000 index ef8557ec..00000000 --- a/FileSets/v3.31/styles.css +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/styles.css \ No newline at end of file diff --git a/FileSets/v3.33/Battery.qml b/FileSets/v3.33/Battery.qml deleted file mode 120000 index fafc7b36..00000000 --- a/FileSets/v3.33/Battery.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Battery.qml \ No newline at end of file diff --git a/FileSets/v3.33/MbEditBox.qml b/FileSets/v3.33/MbEditBox.qml deleted file mode 120000 index 8381b7d5..00000000 --- a/FileSets/v3.33/MbEditBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~10/MbEditBox.qml \ No newline at end of file diff --git a/FileSets/v3.33/MbEditBoxDateTime.qml b/FileSets/v3.33/MbEditBoxDateTime.qml deleted file mode 120000 index 7fb351ea..00000000 --- a/FileSets/v3.33/MbEditBoxDateTime.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbEditBoxDateTime.qml \ No newline at end of file diff --git a/FileSets/v3.33/MbItem.qml b/FileSets/v3.33/MbItem.qml deleted file mode 120000 index 048a313c..00000000 --- a/FileSets/v3.33/MbItem.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbItem.qml \ No newline at end of file diff --git a/FileSets/v3.33/MbItemDigitalInput.qml b/FileSets/v3.33/MbItemDigitalInput.qml deleted file mode 120000 index 4404b520..00000000 --- a/FileSets/v3.33/MbItemDigitalInput.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbItemDigitalInput.qml \ No newline at end of file diff --git a/FileSets/v3.33/MbSpinBox.qml b/FileSets/v3.33/MbSpinBox.qml deleted file mode 120000 index bc7530c8..00000000 --- a/FileSets/v3.33/MbSpinBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbSpinBox.qml \ No newline at end of file diff --git a/FileSets/v3.33/MbStyle.qml b/FileSets/v3.33/MbStyle.qml deleted file mode 120000 index d1940446..00000000 --- a/FileSets/v3.33/MbStyle.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbStyle.qml \ No newline at end of file diff --git a/FileSets/v3.33/MbSubMenu.qml b/FileSets/v3.33/MbSubMenu.qml deleted file mode 120000 index 35516a9a..00000000 --- a/FileSets/v3.33/MbSubMenu.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbSubMenu.qml \ No newline at end of file diff --git a/FileSets/v3.33/Multi.qml b/FileSets/v3.33/Multi.qml deleted file mode 120000 index aa938460..00000000 --- a/FileSets/v3.33/Multi.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Multi.qml \ No newline at end of file diff --git a/FileSets/v3.33/OverviewBox.qml b/FileSets/v3.33/OverviewBox.qml deleted file mode 120000 index 96aa4214..00000000 --- a/FileSets/v3.33/OverviewBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewBox.qml \ No newline at end of file diff --git a/FileSets/v3.33/OverviewConnection.qml b/FileSets/v3.33/OverviewConnection.qml deleted file mode 120000 index d833fd05..00000000 --- a/FileSets/v3.33/OverviewConnection.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewConnection.qml \ No newline at end of file diff --git a/FileSets/v3.33/OverviewConnectionEnd.qml b/FileSets/v3.33/OverviewConnectionEnd.qml deleted file mode 120000 index 50c4b471..00000000 --- a/FileSets/v3.33/OverviewConnectionEnd.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewConnectionEnd.qml \ No newline at end of file diff --git a/FileSets/v3.33/OverviewSolarCharger.qml b/FileSets/v3.33/OverviewSolarCharger.qml deleted file mode 120000 index 7353cec9..00000000 --- a/FileSets/v3.33/OverviewSolarCharger.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewSolarCharger.qml \ No newline at end of file diff --git a/FileSets/v3.33/OverviewSolarInverter.qml b/FileSets/v3.33/OverviewSolarInverter.qml deleted file mode 120000 index 4048703e..00000000 --- a/FileSets/v3.33/OverviewSolarInverter.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewSolarInverter.qml \ No newline at end of file diff --git a/FileSets/v3.33/OverviewTankDelegate.qml b/FileSets/v3.33/OverviewTankDelegate.qml deleted file mode 120000 index faeb9aeb..00000000 --- a/FileSets/v3.33/OverviewTankDelegate.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewTankDelegate.qml \ No newline at end of file diff --git a/FileSets/v3.33/OverviewTanks.qml b/FileSets/v3.33/OverviewTanks.qml deleted file mode 120000 index 645a324b..00000000 --- a/FileSets/v3.33/OverviewTanks.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewTanks.qml \ No newline at end of file diff --git a/FileSets/v3.33/PageDigitalInput.qml b/FileSets/v3.33/PageDigitalInput.qml deleted file mode 120000 index 5b768f8f..00000000 --- a/FileSets/v3.33/PageDigitalInput.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/PageDigitalInput.qml \ No newline at end of file diff --git a/FileSets/v3.33/PageMain.qml b/FileSets/v3.33/PageMain.qml deleted file mode 120000 index 33fe83b0..00000000 --- a/FileSets/v3.33/PageMain.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~13/PageMain.qml \ No newline at end of file diff --git a/FileSets/v3.33/PageSettingsDisplay.qml b/FileSets/v3.33/PageSettingsDisplay.qml deleted file mode 120000 index fd80cffd..00000000 --- a/FileSets/v3.33/PageSettingsDisplay.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.34/PageSettingsDisplay.qml \ No newline at end of file diff --git a/FileSets/v3.33/Tile.qml b/FileSets/v3.33/Tile.qml deleted file mode 120000 index 0558cecf..00000000 --- a/FileSets/v3.33/Tile.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Tile.qml \ No newline at end of file diff --git a/FileSets/v3.33/TileText.qml b/FileSets/v3.33/TileText.qml deleted file mode 120000 index 8d7c0778..00000000 --- a/FileSets/v3.33/TileText.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/TileText.qml \ No newline at end of file diff --git a/FileSets/v3.33/attributes.csv b/FileSets/v3.33/attributes.csv deleted file mode 120000 index e226e13c..00000000 --- a/FileSets/v3.33/attributes.csv +++ /dev/null @@ -1 +0,0 @@ -../v3.40~6/attributes.csv \ No newline at end of file diff --git a/FileSets/v3.33/dbus_generator.py b/FileSets/v3.33/dbus_generator.py deleted file mode 120000 index fc553c91..00000000 --- a/FileSets/v3.33/dbus_generator.py +++ /dev/null @@ -1 +0,0 @@ -../v3.40/dbus_generator.py \ No newline at end of file diff --git a/FileSets/v3.33/dbus_systemcalc.py b/FileSets/v3.33/dbus_systemcalc.py deleted file mode 120000 index 1a9e3b3a..00000000 --- a/FileSets/v3.33/dbus_systemcalc.py +++ /dev/null @@ -1 +0,0 @@ -../v3.40~1/dbus_systemcalc.py \ No newline at end of file diff --git a/FileSets/v3.33/main.qml b/FileSets/v3.33/main.qml deleted file mode 120000 index 916fdfbf..00000000 --- a/FileSets/v3.33/main.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~13/main.qml \ No newline at end of file diff --git a/FileSets/v3.33/styles.css b/FileSets/v3.33/styles.css deleted file mode 120000 index ef8557ec..00000000 --- a/FileSets/v3.33/styles.css +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/styles.css \ No newline at end of file diff --git a/FileSets/v3.34/Battery.qml b/FileSets/v3.34/Battery.qml deleted file mode 120000 index fafc7b36..00000000 --- a/FileSets/v3.34/Battery.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Battery.qml \ No newline at end of file diff --git a/FileSets/v3.34/MbEditBox.qml b/FileSets/v3.34/MbEditBox.qml deleted file mode 120000 index 8381b7d5..00000000 --- a/FileSets/v3.34/MbEditBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~10/MbEditBox.qml \ No newline at end of file diff --git a/FileSets/v3.34/MbEditBoxDateTime.qml b/FileSets/v3.34/MbEditBoxDateTime.qml deleted file mode 120000 index 7fb351ea..00000000 --- a/FileSets/v3.34/MbEditBoxDateTime.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbEditBoxDateTime.qml \ No newline at end of file diff --git a/FileSets/v3.34/MbItem.qml b/FileSets/v3.34/MbItem.qml deleted file mode 120000 index 048a313c..00000000 --- a/FileSets/v3.34/MbItem.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbItem.qml \ No newline at end of file diff --git a/FileSets/v3.34/MbItemDigitalInput.qml b/FileSets/v3.34/MbItemDigitalInput.qml deleted file mode 120000 index 4404b520..00000000 --- a/FileSets/v3.34/MbItemDigitalInput.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbItemDigitalInput.qml \ No newline at end of file diff --git a/FileSets/v3.34/MbSpinBox.qml b/FileSets/v3.34/MbSpinBox.qml deleted file mode 120000 index bc7530c8..00000000 --- a/FileSets/v3.34/MbSpinBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbSpinBox.qml \ No newline at end of file diff --git a/FileSets/v3.34/MbStyle.qml b/FileSets/v3.34/MbStyle.qml deleted file mode 120000 index d1940446..00000000 --- a/FileSets/v3.34/MbStyle.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbStyle.qml \ No newline at end of file diff --git a/FileSets/v3.34/MbSubMenu.qml b/FileSets/v3.34/MbSubMenu.qml deleted file mode 120000 index 35516a9a..00000000 --- a/FileSets/v3.34/MbSubMenu.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/MbSubMenu.qml \ No newline at end of file diff --git a/FileSets/v3.34/Multi.qml b/FileSets/v3.34/Multi.qml deleted file mode 120000 index aa938460..00000000 --- a/FileSets/v3.34/Multi.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Multi.qml \ No newline at end of file diff --git a/FileSets/v3.34/OverviewBox.qml b/FileSets/v3.34/OverviewBox.qml deleted file mode 120000 index 96aa4214..00000000 --- a/FileSets/v3.34/OverviewBox.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewBox.qml \ No newline at end of file diff --git a/FileSets/v3.34/OverviewConnection.qml b/FileSets/v3.34/OverviewConnection.qml deleted file mode 120000 index d833fd05..00000000 --- a/FileSets/v3.34/OverviewConnection.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewConnection.qml \ No newline at end of file diff --git a/FileSets/v3.34/OverviewConnectionEnd.qml b/FileSets/v3.34/OverviewConnectionEnd.qml deleted file mode 120000 index 50c4b471..00000000 --- a/FileSets/v3.34/OverviewConnectionEnd.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewConnectionEnd.qml \ No newline at end of file diff --git a/FileSets/v3.34/OverviewSolarCharger.qml b/FileSets/v3.34/OverviewSolarCharger.qml deleted file mode 120000 index 7353cec9..00000000 --- a/FileSets/v3.34/OverviewSolarCharger.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewSolarCharger.qml \ No newline at end of file diff --git a/FileSets/v3.34/OverviewSolarInverter.qml b/FileSets/v3.34/OverviewSolarInverter.qml deleted file mode 120000 index 4048703e..00000000 --- a/FileSets/v3.34/OverviewSolarInverter.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewSolarInverter.qml \ No newline at end of file diff --git a/FileSets/v3.34/OverviewTankDelegate.qml b/FileSets/v3.34/OverviewTankDelegate.qml deleted file mode 120000 index faeb9aeb..00000000 --- a/FileSets/v3.34/OverviewTankDelegate.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewTankDelegate.qml \ No newline at end of file diff --git a/FileSets/v3.34/OverviewTanks.qml b/FileSets/v3.34/OverviewTanks.qml deleted file mode 120000 index 645a324b..00000000 --- a/FileSets/v3.34/OverviewTanks.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/OverviewTanks.qml \ No newline at end of file diff --git a/FileSets/v3.34/PageDigitalInput.qml b/FileSets/v3.34/PageDigitalInput.qml deleted file mode 120000 index 5b768f8f..00000000 --- a/FileSets/v3.34/PageDigitalInput.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/PageDigitalInput.qml \ No newline at end of file diff --git a/FileSets/v3.34/PageMain.qml b/FileSets/v3.34/PageMain.qml deleted file mode 120000 index 33fe83b0..00000000 --- a/FileSets/v3.34/PageMain.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~13/PageMain.qml \ No newline at end of file diff --git a/FileSets/v3.34/Tile.qml b/FileSets/v3.34/Tile.qml deleted file mode 120000 index 0558cecf..00000000 --- a/FileSets/v3.34/Tile.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/Tile.qml \ No newline at end of file diff --git a/FileSets/v3.34/TileText.qml b/FileSets/v3.34/TileText.qml deleted file mode 120000 index 8d7c0778..00000000 --- a/FileSets/v3.34/TileText.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/TileText.qml \ No newline at end of file diff --git a/FileSets/v3.34/dbus_generator.py b/FileSets/v3.34/dbus_generator.py deleted file mode 120000 index fc553c91..00000000 --- a/FileSets/v3.34/dbus_generator.py +++ /dev/null @@ -1 +0,0 @@ -../v3.40/dbus_generator.py \ No newline at end of file diff --git a/FileSets/v3.34/dbus_systemcalc.py b/FileSets/v3.34/dbus_systemcalc.py deleted file mode 120000 index 1a9e3b3a..00000000 --- a/FileSets/v3.34/dbus_systemcalc.py +++ /dev/null @@ -1 +0,0 @@ -../v3.40~1/dbus_systemcalc.py \ No newline at end of file diff --git a/FileSets/v3.34/main.qml b/FileSets/v3.34/main.qml deleted file mode 120000 index 916fdfbf..00000000 --- a/FileSets/v3.34/main.qml +++ /dev/null @@ -1 +0,0 @@ -../v3.40~13/main.qml \ No newline at end of file diff --git a/FileSets/v3.34/styles.css b/FileSets/v3.34/styles.css deleted file mode 120000 index ef8557ec..00000000 --- a/FileSets/v3.34/styles.css +++ /dev/null @@ -1 +0,0 @@ -../v3.40~35/styles.css \ No newline at end of file diff --git a/FileSets/v3.50/TileDigIn.qml b/FileSets/v3.50/TileDigIn.qml index 3ea17c12..a0872570 120000 --- a/FileSets/v3.50/TileDigIn.qml +++ b/FileSets/v3.50/TileDigIn.qml @@ -1 +1 @@ -../v3.60~17/TileDigIn.qml \ No newline at end of file +../v3.60~21/TileDigIn.qml \ No newline at end of file diff --git a/FileSets/v3.51/TileDigIn.qml b/FileSets/v3.51/TileDigIn.qml index 3ea17c12..a0872570 120000 --- a/FileSets/v3.51/TileDigIn.qml +++ b/FileSets/v3.51/TileDigIn.qml @@ -1 +1 @@ -../v3.60~17/TileDigIn.qml \ No newline at end of file +../v3.60~21/TileDigIn.qml \ No newline at end of file diff --git a/FileSets/v3.52/TileDigIn.qml b/FileSets/v3.52/TileDigIn.qml index 3ea17c12..a0872570 120000 --- a/FileSets/v3.52/TileDigIn.qml +++ b/FileSets/v3.52/TileDigIn.qml @@ -1 +1 @@ -../v3.60~17/TileDigIn.qml \ No newline at end of file +../v3.60~21/TileDigIn.qml \ No newline at end of file diff --git a/FileSets/v3.53/TileDigIn.qml b/FileSets/v3.53/TileDigIn.qml index 3ea17c12..a0872570 120000 --- a/FileSets/v3.53/TileDigIn.qml +++ b/FileSets/v3.53/TileDigIn.qml @@ -1 +1 @@ -../v3.60~17/TileDigIn.qml \ No newline at end of file +../v3.60~21/TileDigIn.qml \ No newline at end of file diff --git a/FileSets/v3.54/COMPLETE b/FileSets/v3.54/COMPLETE new file mode 100644 index 00000000..e69de29b diff --git a/FileSets/v3.54/DetailAcInput.qml b/FileSets/v3.54/DetailAcInput.qml new file mode 120000 index 00000000..3fa965c3 --- /dev/null +++ b/FileSets/v3.54/DetailAcInput.qml @@ -0,0 +1 @@ +../v3.60~17/DetailAcInput.qml \ No newline at end of file diff --git a/FileSets/v3.54/DetailInverter.qml b/FileSets/v3.54/DetailInverter.qml new file mode 120000 index 00000000..88520fef --- /dev/null +++ b/FileSets/v3.54/DetailInverter.qml @@ -0,0 +1 @@ +../v3.60~17/DetailInverter.qml \ No newline at end of file diff --git a/FileSets/v3.54/DetailLoadsCombined.qml b/FileSets/v3.54/DetailLoadsCombined.qml new file mode 120000 index 00000000..2a0760fc --- /dev/null +++ b/FileSets/v3.54/DetailLoadsCombined.qml @@ -0,0 +1 @@ +../v3.60~17/DetailLoadsCombined.qml \ No newline at end of file diff --git a/FileSets/v3.54/DetailLoadsOnInput.qml b/FileSets/v3.54/DetailLoadsOnInput.qml new file mode 120000 index 00000000..bdd9ee0c --- /dev/null +++ b/FileSets/v3.54/DetailLoadsOnInput.qml @@ -0,0 +1 @@ +../v3.60~17/DetailLoadsOnInput.qml \ No newline at end of file diff --git a/FileSets/v3.54/DetailLoadsOnOutput.qml b/FileSets/v3.54/DetailLoadsOnOutput.qml new file mode 120000 index 00000000..c3185d28 --- /dev/null +++ b/FileSets/v3.54/DetailLoadsOnOutput.qml @@ -0,0 +1 @@ +../v3.60~17/DetailLoadsOnOutput.qml \ No newline at end of file diff --git a/FileSets/v3.54/LINKS_ONLY b/FileSets/v3.54/LINKS_ONLY new file mode 100644 index 00000000..e69de29b diff --git a/FileSets/v3.54/OverviewAcValuesEnhanced.qml b/FileSets/v3.54/OverviewAcValuesEnhanced.qml new file mode 120000 index 00000000..c2d9566e --- /dev/null +++ b/FileSets/v3.54/OverviewAcValuesEnhanced.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewAcValuesEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.54/OverviewFlowComplex.qml b/FileSets/v3.54/OverviewFlowComplex.qml new file mode 120000 index 00000000..1ce6dc7b --- /dev/null +++ b/FileSets/v3.54/OverviewFlowComplex.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewFlowComplex.qml \ No newline at end of file diff --git a/FileSets/v3.54/OverviewGeneratorEnhanced.qml b/FileSets/v3.54/OverviewGeneratorEnhanced.qml new file mode 120000 index 00000000..376fd466 --- /dev/null +++ b/FileSets/v3.54/OverviewGeneratorEnhanced.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewGeneratorEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.54/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.54/OverviewGeneratorRelayEnhanced.qml new file mode 120000 index 00000000..361a181b --- /dev/null +++ b/FileSets/v3.54/OverviewGeneratorRelayEnhanced.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.54/OverviewHubEnhanced.qml b/FileSets/v3.54/OverviewHubEnhanced.qml new file mode 120000 index 00000000..ed64252b --- /dev/null +++ b/FileSets/v3.54/OverviewHubEnhanced.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewHubEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.54/OverviewMobileEnhanced.qml b/FileSets/v3.54/OverviewMobileEnhanced.qml new file mode 120000 index 00000000..b82b1af4 --- /dev/null +++ b/FileSets/v3.54/OverviewMobileEnhanced.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewMobileEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.54/OverviewTanksTempsDigInputs.qml b/FileSets/v3.54/OverviewTanksTempsDigInputs.qml new file mode 120000 index 00000000..6706a905 --- /dev/null +++ b/FileSets/v3.54/OverviewTanksTempsDigInputs.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewTanksTempsDigInputs.qml \ No newline at end of file diff --git a/FileSets/v3.54/PageGenerator.qml.USE_ORIGINAL b/FileSets/v3.54/PageGenerator.qml.USE_ORIGINAL new file mode 100644 index 00000000..e69de29b diff --git a/FileSets/v3.54/PageSettingsGuiMods.qml b/FileSets/v3.54/PageSettingsGuiMods.qml new file mode 120000 index 00000000..2f1127c6 --- /dev/null +++ b/FileSets/v3.54/PageSettingsGuiMods.qml @@ -0,0 +1 @@ +../v3.60~17/PageSettingsGuiMods.qml \ No newline at end of file diff --git a/FileSets/v3.54/PowerGauge.qml b/FileSets/v3.54/PowerGauge.qml new file mode 120000 index 00000000..6d3a152c --- /dev/null +++ b/FileSets/v3.54/PowerGauge.qml @@ -0,0 +1 @@ +../v3.60~17/PowerGauge.qml \ No newline at end of file diff --git a/FileSets/v3.54/TileDigIn.qml b/FileSets/v3.54/TileDigIn.qml new file mode 120000 index 00000000..a0872570 --- /dev/null +++ b/FileSets/v3.54/TileDigIn.qml @@ -0,0 +1 @@ +../v3.60~21/TileDigIn.qml \ No newline at end of file diff --git a/FileSets/v3.54/TileRelay.qml b/FileSets/v3.54/TileRelay.qml new file mode 120000 index 00000000..74deeb6d --- /dev/null +++ b/FileSets/v3.54/TileRelay.qml @@ -0,0 +1 @@ +../v3.60~17/TileRelay.qml \ No newline at end of file diff --git a/FileSets/v3.54~1/COMPLETE b/FileSets/v3.54~1/COMPLETE new file mode 100644 index 00000000..e69de29b diff --git a/FileSets/v3.54~1/DetailAcInput.qml b/FileSets/v3.54~1/DetailAcInput.qml new file mode 120000 index 00000000..3fa965c3 --- /dev/null +++ b/FileSets/v3.54~1/DetailAcInput.qml @@ -0,0 +1 @@ +../v3.60~17/DetailAcInput.qml \ No newline at end of file diff --git a/FileSets/v3.54~1/DetailInverter.qml b/FileSets/v3.54~1/DetailInverter.qml new file mode 120000 index 00000000..88520fef --- /dev/null +++ b/FileSets/v3.54~1/DetailInverter.qml @@ -0,0 +1 @@ +../v3.60~17/DetailInverter.qml \ No newline at end of file diff --git a/FileSets/v3.54~1/DetailLoadsCombined.qml b/FileSets/v3.54~1/DetailLoadsCombined.qml new file mode 120000 index 00000000..2a0760fc --- /dev/null +++ b/FileSets/v3.54~1/DetailLoadsCombined.qml @@ -0,0 +1 @@ +../v3.60~17/DetailLoadsCombined.qml \ No newline at end of file diff --git a/FileSets/v3.54~1/DetailLoadsOnInput.qml b/FileSets/v3.54~1/DetailLoadsOnInput.qml new file mode 120000 index 00000000..bdd9ee0c --- /dev/null +++ b/FileSets/v3.54~1/DetailLoadsOnInput.qml @@ -0,0 +1 @@ +../v3.60~17/DetailLoadsOnInput.qml \ No newline at end of file diff --git a/FileSets/v3.54~1/DetailLoadsOnOutput.qml b/FileSets/v3.54~1/DetailLoadsOnOutput.qml new file mode 120000 index 00000000..c3185d28 --- /dev/null +++ b/FileSets/v3.54~1/DetailLoadsOnOutput.qml @@ -0,0 +1 @@ +../v3.60~17/DetailLoadsOnOutput.qml \ No newline at end of file diff --git a/FileSets/v3.54~1/LINKS_ONLY b/FileSets/v3.54~1/LINKS_ONLY new file mode 100644 index 00000000..e69de29b diff --git a/FileSets/v3.54~1/OverviewAcValuesEnhanced.qml b/FileSets/v3.54~1/OverviewAcValuesEnhanced.qml new file mode 120000 index 00000000..c2d9566e --- /dev/null +++ b/FileSets/v3.54~1/OverviewAcValuesEnhanced.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewAcValuesEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.54~1/OverviewFlowComplex.qml b/FileSets/v3.54~1/OverviewFlowComplex.qml new file mode 120000 index 00000000..1ce6dc7b --- /dev/null +++ b/FileSets/v3.54~1/OverviewFlowComplex.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewFlowComplex.qml \ No newline at end of file diff --git a/FileSets/v3.54~1/OverviewGeneratorEnhanced.qml b/FileSets/v3.54~1/OverviewGeneratorEnhanced.qml new file mode 120000 index 00000000..376fd466 --- /dev/null +++ b/FileSets/v3.54~1/OverviewGeneratorEnhanced.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewGeneratorEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.54~1/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.54~1/OverviewGeneratorRelayEnhanced.qml new file mode 120000 index 00000000..361a181b --- /dev/null +++ b/FileSets/v3.54~1/OverviewGeneratorRelayEnhanced.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.54~1/OverviewHubEnhanced.qml b/FileSets/v3.54~1/OverviewHubEnhanced.qml new file mode 120000 index 00000000..ed64252b --- /dev/null +++ b/FileSets/v3.54~1/OverviewHubEnhanced.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewHubEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.54~1/OverviewMobileEnhanced.qml b/FileSets/v3.54~1/OverviewMobileEnhanced.qml new file mode 120000 index 00000000..b82b1af4 --- /dev/null +++ b/FileSets/v3.54~1/OverviewMobileEnhanced.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewMobileEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.54~1/OverviewTanksTempsDigInputs.qml b/FileSets/v3.54~1/OverviewTanksTempsDigInputs.qml new file mode 120000 index 00000000..6706a905 --- /dev/null +++ b/FileSets/v3.54~1/OverviewTanksTempsDigInputs.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewTanksTempsDigInputs.qml \ No newline at end of file diff --git a/FileSets/v3.54~1/PageGenerator.qml.USE_ORIGINAL b/FileSets/v3.54~1/PageGenerator.qml.USE_ORIGINAL new file mode 100644 index 00000000..e69de29b diff --git a/FileSets/v3.54~1/PageSettingsGuiMods.qml b/FileSets/v3.54~1/PageSettingsGuiMods.qml new file mode 120000 index 00000000..2f1127c6 --- /dev/null +++ b/FileSets/v3.54~1/PageSettingsGuiMods.qml @@ -0,0 +1 @@ +../v3.60~17/PageSettingsGuiMods.qml \ No newline at end of file diff --git a/FileSets/v3.54~1/PowerGauge.qml b/FileSets/v3.54~1/PowerGauge.qml new file mode 120000 index 00000000..6d3a152c --- /dev/null +++ b/FileSets/v3.54~1/PowerGauge.qml @@ -0,0 +1 @@ +../v3.60~17/PowerGauge.qml \ No newline at end of file diff --git a/FileSets/v3.54~1/TileDigIn.qml b/FileSets/v3.54~1/TileDigIn.qml new file mode 120000 index 00000000..a0872570 --- /dev/null +++ b/FileSets/v3.54~1/TileDigIn.qml @@ -0,0 +1 @@ +../v3.60~21/TileDigIn.qml \ No newline at end of file diff --git a/FileSets/v3.54~1/TileRelay.qml b/FileSets/v3.54~1/TileRelay.qml new file mode 120000 index 00000000..74deeb6d --- /dev/null +++ b/FileSets/v3.54~1/TileRelay.qml @@ -0,0 +1 @@ +../v3.60~17/TileRelay.qml \ No newline at end of file diff --git a/FileSets/v3.60~15/COMPLETE b/FileSets/v3.60~15/COMPLETE new file mode 100644 index 00000000..e69de29b diff --git a/FileSets/v3.60~15/DetailAcInput.qml b/FileSets/v3.60~15/DetailAcInput.qml new file mode 120000 index 00000000..3fa965c3 --- /dev/null +++ b/FileSets/v3.60~15/DetailAcInput.qml @@ -0,0 +1 @@ +../v3.60~17/DetailAcInput.qml \ No newline at end of file diff --git a/FileSets/v3.60~15/DetailInverter.qml b/FileSets/v3.60~15/DetailInverter.qml new file mode 120000 index 00000000..88520fef --- /dev/null +++ b/FileSets/v3.60~15/DetailInverter.qml @@ -0,0 +1 @@ +../v3.60~17/DetailInverter.qml \ No newline at end of file diff --git a/FileSets/v3.60~15/DetailLoadsCombined.qml b/FileSets/v3.60~15/DetailLoadsCombined.qml new file mode 120000 index 00000000..2a0760fc --- /dev/null +++ b/FileSets/v3.60~15/DetailLoadsCombined.qml @@ -0,0 +1 @@ +../v3.60~17/DetailLoadsCombined.qml \ No newline at end of file diff --git a/FileSets/v3.60~15/DetailLoadsOnInput.qml b/FileSets/v3.60~15/DetailLoadsOnInput.qml new file mode 120000 index 00000000..bdd9ee0c --- /dev/null +++ b/FileSets/v3.60~15/DetailLoadsOnInput.qml @@ -0,0 +1 @@ +../v3.60~17/DetailLoadsOnInput.qml \ No newline at end of file diff --git a/FileSets/v3.60~15/DetailLoadsOnOutput.qml b/FileSets/v3.60~15/DetailLoadsOnOutput.qml new file mode 120000 index 00000000..c3185d28 --- /dev/null +++ b/FileSets/v3.60~15/DetailLoadsOnOutput.qml @@ -0,0 +1 @@ +../v3.60~17/DetailLoadsOnOutput.qml \ No newline at end of file diff --git a/FileSets/v3.60~15/LINKS_ONLY b/FileSets/v3.60~15/LINKS_ONLY new file mode 100644 index 00000000..e69de29b diff --git a/FileSets/v3.60~15/OverviewAcValuesEnhanced.qml b/FileSets/v3.60~15/OverviewAcValuesEnhanced.qml new file mode 120000 index 00000000..c2d9566e --- /dev/null +++ b/FileSets/v3.60~15/OverviewAcValuesEnhanced.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewAcValuesEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.60~15/OverviewFlowComplex.qml b/FileSets/v3.60~15/OverviewFlowComplex.qml new file mode 120000 index 00000000..1ce6dc7b --- /dev/null +++ b/FileSets/v3.60~15/OverviewFlowComplex.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewFlowComplex.qml \ No newline at end of file diff --git a/FileSets/v3.60~15/OverviewGeneratorEnhanced.qml b/FileSets/v3.60~15/OverviewGeneratorEnhanced.qml new file mode 120000 index 00000000..376fd466 --- /dev/null +++ b/FileSets/v3.60~15/OverviewGeneratorEnhanced.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewGeneratorEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.60~15/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.60~15/OverviewGeneratorRelayEnhanced.qml new file mode 120000 index 00000000..361a181b --- /dev/null +++ b/FileSets/v3.60~15/OverviewGeneratorRelayEnhanced.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.60~15/OverviewHubEnhanced.qml b/FileSets/v3.60~15/OverviewHubEnhanced.qml new file mode 120000 index 00000000..ed64252b --- /dev/null +++ b/FileSets/v3.60~15/OverviewHubEnhanced.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewHubEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.60~15/OverviewMobileEnhanced.qml b/FileSets/v3.60~15/OverviewMobileEnhanced.qml new file mode 120000 index 00000000..b82b1af4 --- /dev/null +++ b/FileSets/v3.60~15/OverviewMobileEnhanced.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewMobileEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.60~15/OverviewTanksTempsDigInputs.qml b/FileSets/v3.60~15/OverviewTanksTempsDigInputs.qml new file mode 120000 index 00000000..6706a905 --- /dev/null +++ b/FileSets/v3.60~15/OverviewTanksTempsDigInputs.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewTanksTempsDigInputs.qml \ No newline at end of file diff --git a/FileSets/v3.60~15/PageGenerator.qml.USE_ORIGINAL b/FileSets/v3.60~15/PageGenerator.qml.USE_ORIGINAL new file mode 100644 index 00000000..e69de29b diff --git a/FileSets/v3.60~15/PageSettingsGuiMods.qml b/FileSets/v3.60~15/PageSettingsGuiMods.qml new file mode 120000 index 00000000..2f1127c6 --- /dev/null +++ b/FileSets/v3.60~15/PageSettingsGuiMods.qml @@ -0,0 +1 @@ +../v3.60~17/PageSettingsGuiMods.qml \ No newline at end of file diff --git a/FileSets/v3.60~15/PowerGauge.qml b/FileSets/v3.60~15/PowerGauge.qml new file mode 120000 index 00000000..6d3a152c --- /dev/null +++ b/FileSets/v3.60~15/PowerGauge.qml @@ -0,0 +1 @@ +../v3.60~17/PowerGauge.qml \ No newline at end of file diff --git a/FileSets/v3.60~15/TileDigIn.qml b/FileSets/v3.60~15/TileDigIn.qml new file mode 120000 index 00000000..a0872570 --- /dev/null +++ b/FileSets/v3.60~15/TileDigIn.qml @@ -0,0 +1 @@ +../v3.60~21/TileDigIn.qml \ No newline at end of file diff --git a/FileSets/v3.60~15/TileRelay.qml b/FileSets/v3.60~15/TileRelay.qml new file mode 120000 index 00000000..74deeb6d --- /dev/null +++ b/FileSets/v3.60~15/TileRelay.qml @@ -0,0 +1 @@ +../v3.60~17/TileRelay.qml \ No newline at end of file diff --git a/FileSets/v3.60~16/COMPLETE b/FileSets/v3.60~16/COMPLETE new file mode 100644 index 00000000..e69de29b diff --git a/FileSets/v3.60~16/DetailAcInput.qml b/FileSets/v3.60~16/DetailAcInput.qml new file mode 120000 index 00000000..3fa965c3 --- /dev/null +++ b/FileSets/v3.60~16/DetailAcInput.qml @@ -0,0 +1 @@ +../v3.60~17/DetailAcInput.qml \ No newline at end of file diff --git a/FileSets/v3.60~16/DetailInverter.qml b/FileSets/v3.60~16/DetailInverter.qml new file mode 120000 index 00000000..88520fef --- /dev/null +++ b/FileSets/v3.60~16/DetailInverter.qml @@ -0,0 +1 @@ +../v3.60~17/DetailInverter.qml \ No newline at end of file diff --git a/FileSets/v3.60~16/DetailLoadsCombined.qml b/FileSets/v3.60~16/DetailLoadsCombined.qml new file mode 120000 index 00000000..2a0760fc --- /dev/null +++ b/FileSets/v3.60~16/DetailLoadsCombined.qml @@ -0,0 +1 @@ +../v3.60~17/DetailLoadsCombined.qml \ No newline at end of file diff --git a/FileSets/v3.60~16/DetailLoadsOnInput.qml b/FileSets/v3.60~16/DetailLoadsOnInput.qml new file mode 120000 index 00000000..bdd9ee0c --- /dev/null +++ b/FileSets/v3.60~16/DetailLoadsOnInput.qml @@ -0,0 +1 @@ +../v3.60~17/DetailLoadsOnInput.qml \ No newline at end of file diff --git a/FileSets/v3.60~16/DetailLoadsOnOutput.qml b/FileSets/v3.60~16/DetailLoadsOnOutput.qml new file mode 120000 index 00000000..c3185d28 --- /dev/null +++ b/FileSets/v3.60~16/DetailLoadsOnOutput.qml @@ -0,0 +1 @@ +../v3.60~17/DetailLoadsOnOutput.qml \ No newline at end of file diff --git a/FileSets/v3.60~16/LINKS_ONLY b/FileSets/v3.60~16/LINKS_ONLY new file mode 100644 index 00000000..e69de29b diff --git a/FileSets/v3.60~16/OverviewAcValuesEnhanced.qml b/FileSets/v3.60~16/OverviewAcValuesEnhanced.qml new file mode 120000 index 00000000..c2d9566e --- /dev/null +++ b/FileSets/v3.60~16/OverviewAcValuesEnhanced.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewAcValuesEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.60~16/OverviewFlowComplex.qml b/FileSets/v3.60~16/OverviewFlowComplex.qml new file mode 120000 index 00000000..1ce6dc7b --- /dev/null +++ b/FileSets/v3.60~16/OverviewFlowComplex.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewFlowComplex.qml \ No newline at end of file diff --git a/FileSets/v3.60~16/OverviewGeneratorEnhanced.qml b/FileSets/v3.60~16/OverviewGeneratorEnhanced.qml new file mode 120000 index 00000000..376fd466 --- /dev/null +++ b/FileSets/v3.60~16/OverviewGeneratorEnhanced.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewGeneratorEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.60~16/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.60~16/OverviewGeneratorRelayEnhanced.qml new file mode 120000 index 00000000..361a181b --- /dev/null +++ b/FileSets/v3.60~16/OverviewGeneratorRelayEnhanced.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.60~16/OverviewHubEnhanced.qml b/FileSets/v3.60~16/OverviewHubEnhanced.qml new file mode 120000 index 00000000..ed64252b --- /dev/null +++ b/FileSets/v3.60~16/OverviewHubEnhanced.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewHubEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.60~16/OverviewMobileEnhanced.qml b/FileSets/v3.60~16/OverviewMobileEnhanced.qml new file mode 120000 index 00000000..b82b1af4 --- /dev/null +++ b/FileSets/v3.60~16/OverviewMobileEnhanced.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewMobileEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.60~16/OverviewTanksTempsDigInputs.qml b/FileSets/v3.60~16/OverviewTanksTempsDigInputs.qml new file mode 120000 index 00000000..6706a905 --- /dev/null +++ b/FileSets/v3.60~16/OverviewTanksTempsDigInputs.qml @@ -0,0 +1 @@ +../v3.60~17/OverviewTanksTempsDigInputs.qml \ No newline at end of file diff --git a/FileSets/v3.60~16/PageGenerator.qml.USE_ORIGINAL b/FileSets/v3.60~16/PageGenerator.qml.USE_ORIGINAL new file mode 100644 index 00000000..e69de29b diff --git a/FileSets/v3.60~16/PageSettingsGuiMods.qml b/FileSets/v3.60~16/PageSettingsGuiMods.qml new file mode 120000 index 00000000..2f1127c6 --- /dev/null +++ b/FileSets/v3.60~16/PageSettingsGuiMods.qml @@ -0,0 +1 @@ +../v3.60~17/PageSettingsGuiMods.qml \ No newline at end of file diff --git a/FileSets/v3.60~16/PowerGauge.qml b/FileSets/v3.60~16/PowerGauge.qml new file mode 120000 index 00000000..6d3a152c --- /dev/null +++ b/FileSets/v3.60~16/PowerGauge.qml @@ -0,0 +1 @@ +../v3.60~17/PowerGauge.qml \ No newline at end of file diff --git a/FileSets/v3.60~16/TileDigIn.qml b/FileSets/v3.60~16/TileDigIn.qml new file mode 120000 index 00000000..a0872570 --- /dev/null +++ b/FileSets/v3.60~16/TileDigIn.qml @@ -0,0 +1 @@ +../v3.60~21/TileDigIn.qml \ No newline at end of file diff --git a/FileSets/v3.60~16/TileRelay.qml b/FileSets/v3.60~16/TileRelay.qml new file mode 120000 index 00000000..74deeb6d --- /dev/null +++ b/FileSets/v3.60~16/TileRelay.qml @@ -0,0 +1 @@ +../v3.60~17/TileRelay.qml \ No newline at end of file diff --git a/FileSets/v3.60~17/TileDigIn.qml b/FileSets/v3.60~17/TileDigIn.qml deleted file mode 100644 index 492b8b2d..00000000 --- a/FileSets/v3.60~17/TileDigIn.qml +++ /dev/null @@ -1,133 +0,0 @@ -// New for GuiMods to display digital inputs -// based on TileTank.qml - -import QtQuick 1.1 -import "utils.js" as Utils -import "tanksensor.js" as TankSensor - -Tile { - id: root - - property string bindPrefix: serviceName - property VBusItem nameItem: VBusItem { bind: Utils.path(bindPrefix, "/CustomName") } - property VBusItem deviceItem: VBusItem { bind: Utils.path(bindPrefix, "/DeviceInstance") } - property VBusItem aggregateItem: VBusItem { bind: Utils.path(bindPrefix, "/Aggregate") } - property string digInName: nameItem.valid && nameItem.value != "" ? nameItem.value : getType (type) - property VBusItem typeItem: VBusItem { bind: Utils.path(bindPrefix, "/Type") } - property VBusItem stateItem: VBusItem { bind: Utils.path(bindPrefix, "/State") } - property bool isPulseCounter: aggregateItem.valid - // pulse counter doesn't have /Type so fill it in here - property int type: isPulseCounter ? 1 : typeItem.valid ? typeItem.value : 0 - - property variant bkgdColors: [ "#b3b3b3", "#4aa3df", "#1abc9c", "#F39C12", "#95a5a6", "#95a5a6","#dcc6e0", "#f1a9a0", "#7f8c8d", "#ebbc3a" ] - property color bkgdColor: type > 0 && type < 10 ? bkgdColors [type] : "#b3b3b3" - property variant units: ["m3", "L", "gal", "gal"] - - - function getType(type) - { - switch (type) - { - case 0: - return qsTr("Disabled") - case 1: - return qsTr("Pulse meter") - case 2: - return qsTr("Door alarm") - case 3: - return qsTr("Bilge pump") - case 4: - return qsTr("Bilge alarm") - case 5: - return qsTr("Burglar alarm") - case 6: - return qsTr("Smoke alarm") - case 7: - return qsTr("Fire alarm") - case 8: - return qsTr("CO2 alarm") - case 9: - return qsTr("Generator") - case 10: - return qsTr("Generic I/O") -//// added for ExtTransferSwitch package - case 11: - return qsTr("Touch enable") - case 12: - return qsTr("Transfer switch") - default: - return "Unknown" - } - } - - function getState(st) - { - switch (st) - { - case 0: - return qsTr("Low") - case 1: - return qsTr("High") - case 2: - return qsTr("Off") - case 3: - return qsTr("On") - case 4: - return qsTr("No") - case 5: - return qsTr("Yes") - case 6: - return qsTr("Open") - case 7: - return qsTr("Closed") - case 8: - return qsTr("Ok") - case 9: - return qsTr("Alarm") - case 10: - return qsTr("Running") - case 11: - return qsTr("Stopped") -//// added for ExtTransferSwitch package - case 12: - return qsTr("On Generator") - case 13: - return qsTr("On Grid") - default: - return qsTr("Unknown") - } - - } - - title: digInName + " (In " + (deviceItem.valid ? (deviceItem.value.toString ()) : "?") + ")" - - color: bkgdColor - - VBusItem - { - id: unitItem - bind: Utils.path("com.victronenergy.settings/Settings/System/VolumeUnit") - } - - values: Item - { - width: root.width - 10 - height: 12 - TileText - { - width: root.width - text: - { - if (isPulseCounter) - return aggregateItem.value.toString() + (unitItem.valid ? units[unitItem.value] : "??") - else - return stateItem.valid ? getState (stateItem.value) : "??" - } - horizontalAlignment: Text.AlignHCenter - anchors - { - horizontalCenter: parent.horizontalCenter - } - } - } -} diff --git a/FileSets/v3.60~17/TileDigIn.qml b/FileSets/v3.60~17/TileDigIn.qml new file mode 120000 index 00000000..a0872570 --- /dev/null +++ b/FileSets/v3.60~17/TileDigIn.qml @@ -0,0 +1 @@ +../v3.60~21/TileDigIn.qml \ No newline at end of file diff --git a/FileSets/v3.60~21/COMPLETE b/FileSets/v3.60~21/COMPLETE new file mode 100644 index 00000000..e69de29b diff --git a/FileSets/v3.60~21/DetailAcInput.qml b/FileSets/v3.60~21/DetailAcInput.qml new file mode 120000 index 00000000..9e3016f7 --- /dev/null +++ b/FileSets/v3.60~21/DetailAcInput.qml @@ -0,0 +1 @@ +../v3.60~25/DetailAcInput.qml \ No newline at end of file diff --git a/FileSets/v3.60~21/DetailInverter.qml b/FileSets/v3.60~21/DetailInverter.qml new file mode 120000 index 00000000..b702a885 --- /dev/null +++ b/FileSets/v3.60~21/DetailInverter.qml @@ -0,0 +1 @@ +../v3.60~25/DetailInverter.qml \ No newline at end of file diff --git a/FileSets/v3.60~21/DetailLoadsCombined.qml b/FileSets/v3.60~21/DetailLoadsCombined.qml new file mode 120000 index 00000000..e5f3e68e --- /dev/null +++ b/FileSets/v3.60~21/DetailLoadsCombined.qml @@ -0,0 +1 @@ +../v3.60~25/DetailLoadsCombined.qml \ No newline at end of file diff --git a/FileSets/v3.60~21/DetailLoadsOnInput.qml b/FileSets/v3.60~21/DetailLoadsOnInput.qml new file mode 120000 index 00000000..11f4c173 --- /dev/null +++ b/FileSets/v3.60~21/DetailLoadsOnInput.qml @@ -0,0 +1 @@ +../v3.60~25/DetailLoadsOnInput.qml \ No newline at end of file diff --git a/FileSets/v3.60~21/DetailLoadsOnOutput.qml b/FileSets/v3.60~21/DetailLoadsOnOutput.qml new file mode 120000 index 00000000..54ab28e5 --- /dev/null +++ b/FileSets/v3.60~21/DetailLoadsOnOutput.qml @@ -0,0 +1 @@ +../v3.60~25/DetailLoadsOnOutput.qml \ No newline at end of file diff --git a/FileSets/v3.60~21/LINKS_ONLY b/FileSets/v3.60~21/LINKS_ONLY new file mode 100644 index 00000000..e69de29b diff --git a/FileSets/v3.60~21/OverviewAcValuesEnhanced.qml b/FileSets/v3.60~21/OverviewAcValuesEnhanced.qml new file mode 120000 index 00000000..09f7989b --- /dev/null +++ b/FileSets/v3.60~21/OverviewAcValuesEnhanced.qml @@ -0,0 +1 @@ +../v3.60~25/OverviewAcValuesEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.60~21/OverviewFlowComplex.qml b/FileSets/v3.60~21/OverviewFlowComplex.qml new file mode 120000 index 00000000..fb316922 --- /dev/null +++ b/FileSets/v3.60~21/OverviewFlowComplex.qml @@ -0,0 +1 @@ +../v3.60~25/OverviewFlowComplex.qml \ No newline at end of file diff --git a/FileSets/v3.60~21/OverviewGeneratorEnhanced.qml b/FileSets/v3.60~21/OverviewGeneratorEnhanced.qml new file mode 120000 index 00000000..3a94fc62 --- /dev/null +++ b/FileSets/v3.60~21/OverviewGeneratorEnhanced.qml @@ -0,0 +1 @@ +../v3.60~25/OverviewGeneratorEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.60~21/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.60~21/OverviewGeneratorRelayEnhanced.qml new file mode 120000 index 00000000..c592dd97 --- /dev/null +++ b/FileSets/v3.60~21/OverviewGeneratorRelayEnhanced.qml @@ -0,0 +1 @@ +../v3.60~25/OverviewGeneratorRelayEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.60~21/OverviewHubEnhanced.qml b/FileSets/v3.60~21/OverviewHubEnhanced.qml new file mode 120000 index 00000000..5134a4c8 --- /dev/null +++ b/FileSets/v3.60~21/OverviewHubEnhanced.qml @@ -0,0 +1 @@ +../v3.60~25/OverviewHubEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.60~21/OverviewMobileEnhanced.qml b/FileSets/v3.60~21/OverviewMobileEnhanced.qml new file mode 120000 index 00000000..633340cc --- /dev/null +++ b/FileSets/v3.60~21/OverviewMobileEnhanced.qml @@ -0,0 +1 @@ +../v3.60~25/OverviewMobileEnhanced.qml \ No newline at end of file diff --git a/FileSets/v3.60~21/OverviewTanksTempsDigInputs.qml b/FileSets/v3.60~21/OverviewTanksTempsDigInputs.qml new file mode 120000 index 00000000..81f97bbb --- /dev/null +++ b/FileSets/v3.60~21/OverviewTanksTempsDigInputs.qml @@ -0,0 +1 @@ +../v3.60~25/OverviewTanksTempsDigInputs.qml \ No newline at end of file diff --git a/FileSets/v3.60~21/PageGenerator.qml.USE_ORIGINAL b/FileSets/v3.60~21/PageGenerator.qml.USE_ORIGINAL new file mode 100644 index 00000000..e69de29b diff --git a/FileSets/v3.60~21/PageGenerator.qml.orig b/FileSets/v3.60~21/PageGenerator.qml.orig new file mode 100644 index 00000000..3dd70448 --- /dev/null +++ b/FileSets/v3.60~21/PageGenerator.qml.orig @@ -0,0 +1,96 @@ +import QtQuick 2 +import "utils.js" as Utils + +MbPage { + id: root + title: qsTr("Generator start/stop") + property string settingsBindPrefix + property string startStopBindPrefix + property bool showRunTime: true + property alias startStopModel: _startStopModel + property VBusItem activeCondition: VBusItem { bind: Utils.path(startStopBindPrefix, "/RunningByCondition") } + property VBusItem generatorState: VBusItem { bind: Utils.path(startStopBindPrefix, "/State") } + property VBusItem runningTime: VBusItem { bind: Utils.path(startStopBindPrefix, "/Runtime") } + + FnGeneratorStates { + id: genState + } + + model: startStopModel + + function formatError(text, value) + { + return "#" + value.toString() + " " + text + } + + VisibleItemModel { + id: _startStopModel + + MbSwitch { + name: qsTr("Auto start functionality") + bind: Utils.path(startStopBindPrefix, "/AutoStartEnabled") + show: startStopBindPrefix === "com.victronenergy.generator.startstop0" + } + + MbSubMenu { + description: qsTr("Manual start") + show: startStopBindPrefix === "com.victronenergy.generator.startstop0" + subpage: + Component { + PageGeneratorManualStart { + startStopBindPrefix: root.startStopBindPrefix + } + } + } + + MbItemValue { + description: qsTr("Current run time") + item.text: runningTime.valid ? Utils.secondsToNoSecsString(runningTime.value) : "0" + show: generatorState.value >= 1 && generatorState.value <= 3 // Running, Warm-up, Cool-down + } + + MbItemValue { + description: qsTr("State") + show: startStopBindPrefix === "com.victronenergy.generator.startstop0" + item.text: activeCondition.valid ? genState.getState(generatorState.value, activeCondition.value) : '---' + } + + MbItemOptions { + id: _gensetStatus + description: qsTr("Error") + bind: Utils.path(startStopBindPrefix, "/Error") + readonly: true + show: valid && startStopBindPrefix === "com.victronenergy.generator.startstop0" + possibleValues: [ + MbOption { description: qsTr("No error"); value: 0 }, + MbOption { description: formatError(qsTr("Remote switch control disabled"), 1); value: 1 }, + MbOption { description: formatError(qsTr("Generator in fault condition"), 2); value: 2 }, + MbOption { description: formatError(qsTr("Generator not detected at AC input"), 3); value: 3 } + ] + } + + MbSubMenu { + id: conditions + description: qsTr("Settings") + subpage: Component { + PageSettingsGenerator { + settingsBindPrefix: root.settingsBindPrefix + startStopBindPrefix: root.startStopBindPrefix + } + } + } + + MbSubMenu { + id: runtimePage + description: qsTr("Run time and service") + subpage: + Component { + PageGeneratorRuntimeService { + title: qsTr("Run time and service") + settingsBindPrefix: root.settingsBindPrefix + startStopBindPrefix: root.startStopBindPrefix + } + } + } + } +} diff --git a/FileSets/v3.60~21/PageSettingsGuiMods.qml b/FileSets/v3.60~21/PageSettingsGuiMods.qml new file mode 120000 index 00000000..751bba70 --- /dev/null +++ b/FileSets/v3.60~21/PageSettingsGuiMods.qml @@ -0,0 +1 @@ +../v3.60~25/PageSettingsGuiMods.qml \ No newline at end of file diff --git a/FileSets/v3.60~21/PowerGauge.qml b/FileSets/v3.60~21/PowerGauge.qml new file mode 120000 index 00000000..d11eedb9 --- /dev/null +++ b/FileSets/v3.60~21/PowerGauge.qml @@ -0,0 +1 @@ +../v3.60~25/PowerGauge.qml \ No newline at end of file diff --git a/FileSets/v3.60~21/TileDigIn.qml b/FileSets/v3.60~21/TileDigIn.qml new file mode 120000 index 00000000..66003f49 --- /dev/null +++ b/FileSets/v3.60~21/TileDigIn.qml @@ -0,0 +1 @@ +../v3.60~25/TileDigIn.qml \ No newline at end of file diff --git a/FileSets/v3.60~21/TileRelay.orig b/FileSets/v3.60~21/TileRelay.orig new file mode 100644 index 00000000..6efa32bf --- /dev/null +++ b/FileSets/v3.60~21/TileRelay.orig @@ -0,0 +1,170 @@ +import QtQuick 2 +import "utils.js" as Utils + +Item { + id: root + + property string systemPrefix: "com.victronenergy.system" + property string vebusPrefix: _vebusService.valid ? _vebusService.value : "" + + property variant battery: _battery + property alias dcSystem: _dcSystem + property alias pvCharger: _pvCharger + property alias pvOnAcIn1: _pvOnAcIn1 + property alias pvOnAcIn2: _pvOnAcIn2 + property alias pvOnAcOut: _pvOnAcOut + property alias inverterChargerDc: _inverterChargerDc + property alias acLoad: _acLoad + property alias acInLoad: _acInLoad + property alias acOutLoad: _acOutLoad + property alias grid: _grid + property alias genset: _genset + property alias acInput: _activein + property VBusItem systemType: VBusItem { bind: Utils.path(systemPrefix, "/SystemType") } + property bool hasGridMeter: _hasGridMeter.valid + property variant acSource: _acSource.value + property VBusItem preferRenewableEnergy: VBusItem { bind: Utils.path(vebusPrefix, "/Dc/0/PreferRenewableEnergy") } + property VBusItem remoteGeneratorSelected: VBusItem { bind: Utils.path(vebusPrefix, "/Ac/State/RemoteGeneratorSelected") } + + property alias pvOnGrid: _pvOnAcIn2 + + property int batteryStateIdle: 0 + property int batteryStateCharging: 1 + property int batteryStateDischarging: 2 + + property int acSourceNotAvailable: 0 + property int acSourceGrid: 1 + property int acSourceGenset: 2 + property int acSourceShore: 3 // same as grid + + property alias pvInvertersProductIds: _pvInvertersProductIds + property alias batteryProductId: _batteryProductId + + VBusItem { + id: _pvInvertersProductIds + bind: Utils.path(systemPrefix, "/PvInvertersProductIds") + } + + VBusItem { + id: _batteryProductId + bind: Utils.path(systemPrefix, "/Dc/Battery/ProductId") + } + + VBusItem { + id: _vebusService + bind: Utils.path(systemPrefix, "/VebusService") + } + + QtObject { + id: _pvCharger + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Pv/Power"); unit: "W"} + } + + ObjectAcConnection { + id: _pvOnAcOut + bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnOutput") + } + + ObjectAcConnection { + id: _pvOnAcIn1 + bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGenset") + } + + ObjectAcConnection { + id: _pvOnAcIn2 + bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGrid") + } + + ObjectAcConnection { + id: _genset + splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0 + bindPrefix: Utils.path(systemPrefix, "/Ac/Genset") + } + + VBusItem { + id: _acSource + bind: Utils.path(systemPrefix, "/Ac/ActiveIn/Source") + } + + VBusItem { + id: _hasGridMeter + bind: Utils.path(systemPrefix, "/Ac/Grid/DeviceType") + } + + /* + * Single Multis that can be split-phase reports NrOfPhases of 2 + * When L2 is disconnected from the input the output L1 and L2 + * are shorted. This item indicates if L2 is passed through + * from AC-in to AC-out. + * 1: L2 is being passed through from AC-in to AC-out. + * 0: L1 and L2 are shorted together. + * invalid: The unit is configured in such way that its L2 output is not used. + */ + + VBusItem { + id: _splitPhaseL2Passthru + bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2Passthru") + } + + VBusItem { + id: _l2L1OutSummed + bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2L1OutSummed") + } + + + ObjectAcConnection { + id: _grid + splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0 + bindPrefix: Utils.path(systemPrefix, "/Ac/Grid") + } + + ObjectAcConnection { + id: _activein + bindPrefix: Utils.path(systemPrefix, "/Ac/ActiveIn") + } + + ObjectAcConnection { + id: _acLoad + l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0) + isAcOutput: true + bindPrefix: Utils.path(systemPrefix, "/Ac/Consumption") + } + + ObjectAcConnection { + id: _acOutLoad + l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0) + isAcOutput: true + bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnOutput") + } + + ObjectAcConnection { + id: _acInLoad + splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0 + bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnInput") + } + + ObjectAcConnection { + id: _acUnknown + } + + QtObject { + id: _inverterChargerDc + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/InverterCharger/Power"); unit: "W"} + } + + QtObject { + id: _battery + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Power"); unit: "W"} + property VBusItem voltage: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Voltage"); unit: "V"} + property VBusItem current: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Current"); unit: "A"} + property VBusItem soc: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Soc"); unit: "%"} + + // Get the battery charge state, see batteryState properties + property VBusItem state: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/State")} + } + + QtObject { + id: _dcSystem + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/System/Power"); unit: "W"} + } +} diff --git a/FileSets/v3.60~21/TileRelay.qml b/FileSets/v3.60~21/TileRelay.qml new file mode 120000 index 00000000..ca5e8d2d --- /dev/null +++ b/FileSets/v3.60~21/TileRelay.qml @@ -0,0 +1 @@ +../v3.60~25/TileRelay.qml \ No newline at end of file diff --git a/FileSets/v3.60~25/COMPLETE b/FileSets/v3.60~25/COMPLETE new file mode 100644 index 00000000..e69de29b diff --git a/FileSets/v3.60~25/DetailAcInput.qml b/FileSets/v3.60~25/DetailAcInput.qml new file mode 100644 index 00000000..2ef58a00 --- /dev/null +++ b/FileSets/v3.60~25/DetailAcInput.qml @@ -0,0 +1,606 @@ +////// detail page for setting input current limit +////// and displaying AC input details +////// pushed from Flow overview + +import QtQuick 2 +import "utils.js" as Utils +import com.victron.velib 1.0 +import "enhancedFormat.js" as EnhFmt + +MbPage { + id: root + + title: "AC Input detail" + + property variant sys: theSystem + property string systemPrefix: "com.victronenergy.system" + property string settingsPrefix: "com.victronenergy.settings" + + property int fontPixelSize: 18 + property color buttonColor: "#979797" + property color pressedColor: "#d3d3d3" + property color backgroundColor: "#b3b3b3" + + property int buttonHeight: 40 + + property int dataColumns: 4 + property int rowTitleWidth: 100 + property int totalDataWidth: 340 - rowTitleWidth + property int tableColumnWidth: totalDataWidth / dataColumns + + property int legColumnWidth: phaseCount <= 1 ? tableColumnWidth * 3 : tableColumnWidth * 3 / phaseCount + + property int phaseCount: sys.acInput.phaseCount.valid ? sys.acInput.phaseCount.value : 0 + + VBusItem { id: vebusServiceItem; bind: Utils.path(systemPrefix, "/VebusService") } + property string inverterService: vebusServiceItem.valid ? vebusServiceItem.value : "" + property bool splitPhasePassthruDisabled: sys.acInput.splitPhaseL2PassthruDisabled + + property real actualCurrentLimit: 0 + property real newCurrentLimit: 0 + + VBusItem { id: acLimitPreset1Item; bind: Utils.path(settingsPrefix, "/Settings/GuiMods/AcCurrentLimit/Preset1") } + VBusItem { id: acLimitPreset2Item; bind: Utils.path(settingsPrefix, "/Settings/GuiMods/AcCurrentLimit/Preset2") } + VBusItem { id: acLimitPreset3Item; bind: Utils.path(settingsPrefix, "/Settings/GuiMods/AcCurrentLimit/Preset3") } + VBusItem { id: acLimitPreset4Item; bind: Utils.path(settingsPrefix, "/Settings/GuiMods/AcCurrentLimit/Preset4") } + property real acLimitPreset1: acLimitPreset1Item.valid ? acLimitPreset1Item.value : 0 + property real acLimitPreset2: acLimitPreset2Item.valid ? acLimitPreset2Item.value : 0 + property real acLimitPreset3: acLimitPreset3Item.valid ? acLimitPreset3Item.value : 0 + property real acLimitPreset4: acLimitPreset4Item.valid ? acLimitPreset4Item.value : 0 + + property bool currentLimitIsAdjustable: currentLimitIsAdjustableItem.valid && currentLimitIsAdjustableItem.value == 1 && currentLimitItem.valid + + Component.onCompleted: { getActualCurrent () } + + VBusItem + { + id: currentLimitIsAdjustableItem + bind: Utils.path(inverterService, "/Ac/ActiveIn/CurrentLimitIsAdjustable") + onValueChanged: getActualCurrent () + onValidChanged: getActualCurrent () + } + VBusItem + { + id: currentLimitItem + bind: Utils.path(inverterService, "/Ac/ActiveIn/CurrentLimit") + onValueChanged: getActualCurrent () + onValidChanged: getActualCurrent () + } + VBusItem { id: activeInputItem; bind: Utils.path(inverterService, "/Ac/ActiveIn/ActiveInput") } + VBusItem { id: numberOfAcInputs; bind: Utils.path(inverterService, "/Ac/In/NumberOfAcInputs") } + VBusItem { id: activeSourceItem; bind: Utils.path(systemPrefix, "/Ac/ActiveIn/Source") } + VBusItem { id: acIn1sourceItem; bind: Utils.path(settingsPrefix, "/Settings/SystemSetup/AcInput1") } + VBusItem { id: acIn2sourceItem; bind: Utils.path(settingsPrefix, "/Settings/SystemSetup/AcInput2") } + property int activeSource: activeSourceItem.valid ? activeSourceItem.value : 0 + property int acIn1source: acIn1sourceItem.valid ? acIn1sourceItem.value : 0 + property int acIn2source: acIn2sourceItem.valid ? acIn2sourceItem.value : 0 + property int activeInput: activeInputItem.valid && activeInputItem.value == 1 ? 2 : 1 + property bool hasTwoInputs: numberOfAcInputs.valid && numberOfAcInputs.value == 2 + + property variant acSourceName: [qsTr("---"), qsTr("Grid"), qsTr("Generator"), qsTr("Shore")] + + // background + Rectangle + { + anchors + { + fill: parent + } + color: root.backgroundColor + } + + Row + { + spacing: 5 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + width: parent.width - 6 + Column + { + spacing: 2 + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Total Power") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: tableColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.acInput.power, "W") + } + PowerGauge + { + id: gauge + width: totalDataWidth - tableColumnWidth + height: 15 + connection: sys.acInput + useInputCurrentLimit: true + maxForwardPowerParameter: "" // handled internally - uses input current limit and AC input voltage + maxReversePowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/MaxFeedInPower" + } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth + tableColumnWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Active Source") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: totalDataWidth - tableColumnWidth; horizontalAlignment: Text.AlignHCenter + text: + { + if (activeSource == 240) + return quTr ("no input") + else if (hasTwoInputs) + return acSourceName[activeSource] + " (AC in " + activeInput + ")" + else + return acSourceName[activeSource] + } + } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: "L1" } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: "L2"; visible: phaseCount >= 2 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: "L3"; visible: phaseCount >= 3 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: tableColumnWidth; horizontalAlignment: Text.AlignHCenter + text: qsTr ("Freq") } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Power") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.acInput.powerL1, "W") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: splitPhasePassthruDisabled ? "< < <" : EnhFmt.formatVBusItem (sys.acInput.powerL2, "W"); visible: phaseCount >= 2 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.acInput.powerL3, "W"); visible: phaseCount >= 3 } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Voltage / Freq") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.acInput.voltageL1, "V") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: splitPhasePassthruDisabled ? "< < <" : EnhFmt.formatVBusItem (sys.acInput.voltageL2, "V"); visible: phaseCount >= 2 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.acInput.voltageL3, "V"); visible: phaseCount >= 3 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: tableColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.acInput.frequency, "Hz") } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Current") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.acInput.currentL1, "A") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: splitPhasePassthruDisabled ? "< < <" : EnhFmt.formatVBusItem (sys.acInput.currentL2, "A"); visible: phaseCount >= 2 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.acInput.currentL3, "A"); visible: phaseCount >= 3 } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("CurrentLimit") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: totalDataWidth; horizontalAlignment: Text.AlignHCenter + text: + { + var newText + if (newCurrentLimit != actualCurrentLimit) + newText = qsTr(" New ") + newCurrentLimit.toFixed (1) + " A" + else + newText = "" + return currentLimitItem.valid ? currentLimitItem.value.toFixed (1) + " A" + newText: "--" } + } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth + totalDataWidth; horizontalAlignment: Text.AlignHCenter + text: "L2 values included in L1" + visible: splitPhasePassthruDisabled } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth + totalDataWidth; horizontalAlignment: Text.AlignHCenter + text: qsTr("Avaliable Sources") } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: "L1" } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: "L2"; visible: phaseCount >= 2 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: "L3"; visible: phaseCount >= 3 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: tableColumnWidth; horizontalAlignment: Text.AlignHCenter + text: qsTr ("Freq") } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: 20; horizontalAlignment: Text.AlignHCenter + text: activeSource == 1 || activeSource == 3 ? ">" : "" } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth - 20; horizontalAlignment: Text.AlignRight + text: + { + if (acIn1source == 3 || acIn2source == 3) + return acSourceName[3] + else + return acSourceName[1] + } + } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.grid.voltageL1, "V") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: splitPhasePassthruDisabled ? "< < <" : EnhFmt.formatVBusItem (sys.grid.voltageL2, "V"); visible: phaseCount >= 2 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.grid.voltageL3, "V"); visible: phaseCount >= 3 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: tableColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.grid.frequency, "Hz") } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: 20; horizontalAlignment: Text.AlignHCenter + text: activeSource == 2 ? ">" : "" } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth - 20; horizontalAlignment: Text.AlignRight + text: acSourceName[2] } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.genset.voltageL1, "V") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: splitPhasePassthruDisabled ? "< < <" : EnhFmt.formatVBusItem (sys.genset.voltageL2, "V"); visible: phaseCount >= 2 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.genset.voltageL3, "V"); visible: phaseCount >= 3 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: tableColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.genset.frequency, "Hz") } + } + } + Column + { + id: currentButtonColumn + width: 128 + spacing: 4 + + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: currentButtonColumn.width; horizontalAlignment: Text.AlignHCenter + text: qsTr("Current Limit") } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: currentButtonColumn.width; horizontalAlignment: Text.AlignHCenter + text: qsTr("is not adjustable")} + visible: !currentLimitIsAdjustable + } + Row + { + visible: currentLimitIsAdjustable + width: (parent.width / 2) - 2 + spacing: 4 + DetailButton + { + id: preset1button + baseColor: newCurrentLimit === acLimitPreset1 ? "black" : root.buttonColor + pressedColor: root.pressedColor + opacity: acLimitPreset1 === 0 ? 0.001 : 1 + height: 40 + width: parent.width + onClicked: setNewValue (acLimitPreset1) + enabled: acLimitPreset1 === 0 ? false : true + content: TileText + { + text: qsTr(acLimitPreset1 + " A"); font.bold: true; + color: "white" + } + } + DetailButton + { + id: preset2button + baseColor: newCurrentLimit === acLimitPreset2 ? "black" : root.buttonColor + pressedColor: root.pressedColor + opacity: acLimitPreset2 === 0 ? 0.001 : 1 + height: 40 + width: parent.width + onClicked: setNewValue (acLimitPreset2) + enabled: acLimitPreset2 === 0 ? false : true + content: TileText + { + text: qsTr(acLimitPreset2 + " A"); font.bold: true; + color: "white" + } + } + } + Row + { + visible: currentLimitIsAdjustable + width: (parent.width / 2) - 2 + spacing: 4 + DetailButton + { + id: preset3button + baseColor: newCurrentLimit === acLimitPreset3 ? "black" : root.buttonColor + pressedColor: root.pressedColor + opacity: acLimitPreset3 === 0 ? 0.001 : 1 + height: 40 + width: parent.width + onClicked: setNewValue (acLimitPreset3) + enabled: acLimitPreset3 === 0 ? false : true + content: TileText + { + text: qsTr(acLimitPreset3 + " A"); font.bold: true; + color: "white" + } + } + DetailButton + { + visible: currentLimitIsAdjustable + id: preset4button + baseColor: newCurrentLimit === acLimitPreset4 ? "black" : root.buttonColor + pressedColor: root.pressedColor + opacity: acLimitPreset4 === 0 ? 0.001 : 1 + height: 40 + width: parent.width + onClicked: setNewValue (acLimitPreset4) + enabled: acLimitPreset4 === 0 ? false : true + content: TileText + { + text: qsTr(acLimitPreset4 + " A"); font.bold: true; + color: "white" + } + } + } + Row + { + visible: currentLimitIsAdjustable + width: (parent.width / 2) - 2 + spacing: 4 + DetailButton + { + id: trimMinus + baseColor: root.buttonColor + pressedColor: root.pressedColor + height: 40 + width: parent.width + enablePressAndHold: true + onClicked: trimNewValue (-1) + enabled: newCurrentLimit === acLimitPreset4 ? false : true + content: TileText + { + text: qsTr("-1 A"); font.bold: true; + color: "white" + } + } + DetailButton + { + id: trimPlus + baseColor: root.buttonColor + pressedColor: root.pressedColor + height: 40 + width: parent.width + enablePressAndHold: true + onClicked: trimNewValue (+1) + content: TileText + { + text: qsTr("+1 A"); font.bold: true; + color: "white" + } + } + } + Row + { + visible: currentLimitIsAdjustable + width: parent.width + spacing: 4 + DetailButton + { + id: acceptButton + baseColor: root.buttonColor + pressedColor: root.pressedColor + height: 40 + width: parent.width + onClicked: accept() + content: TileText { text: qsTr ("Accept New"); + font.bold: true; color: newCurrentLimit !== actualCurrentLimit ? "white" : "#d9d9d9" } + } + } + } + } + + function setNewValue (newValue) + { + if (currentLimitIsAdjustable) + newCurrentLimit = newValue + } + + function trimNewValue (trimValue) + { + if (!currentLimitIsAdjustable) + return + + newCurrentLimit += trimValue + if (newCurrentLimit < 0) + newCurrentLimit = 0 + } + + function cancel () + { + newCurrentLimit = actualCurrentLimit + pageStack.pop() + } + + function accept () + { + if (currentLimitIsAdjustable) + { + currentLimitItem.setValue (newCurrentLimit) + pageStack.pop() // return to main screen after changing input current limit + } + } + + function getActualCurrent () + { + actualCurrentLimit = currentLimitItem.valid ? currentLimitItem.value : 0 + newCurrentLimit = actualCurrentLimit + } + + // When new service is found check if is a tank sensor + Connections + { + target: DBusServices + onDbusServiceFound: addService(service) + } + + //// hard key handler + // used to press buttons when touch isn't available + // UP and DOWN buttons cycle through the list of buttons + // "space" button is used to simulate a button press + // button must be highlighted so that other uses of "space" + // will still occur + + // list of buttons to be accessed via hard buttons + property variant buttonList: + [ + preset1button, preset2button, preset3button, preset4button, trimMinus, trimPlus, acceptButton + ] + + property int buttonIndex: 0 + + Timer + { + id: targetTimer + interval: 5000 + repeat: false + running: false + onTriggered: { clearHighlight () } + } + + Keys.forwardTo: [keyHandler] + + Item + { + id: keyHandler + Keys.onDownPressed: + { + nextTarget (+1) + event.accepted = true + } + + Keys.onUpPressed: + { + nextTarget (-1) + event.accepted = true + } + } + + function nextTarget (increment) + { + // make one pass through all possible targets to find an enabled one + // if found, that's the new selectedTarget, + // if not selectedTarget does not change + var newIndex = buttonIndex + for (var i = 0; i < buttonList.length; i++) + { + // just restore highlight if not visible + if ( ! targetTimer.running && buttonList[newIndex].visible) + { + setActiveButton (buttonIndex) + return + } + newIndex += increment + if (newIndex >= buttonList.length) + newIndex = 0 + else if (newIndex < 0) + newIndex = buttonList.length - 1 + if (buttonList[newIndex].visible) + { + setActiveButton (newIndex) + break + } + } + } + + // Keys.onSpacePressed doesn't work - stolen by pageHandler + // so build a custom page handler so "space" can be used to press a button + pageToolbarHandler: detailToolbarHandler + ToolbarHandlerPages + { + id: detailToolbarHandler + isDefault: true + function centerAction() + { + acceptSpaceButton () + } + } + + function acceptSpaceButton () + { + if (targetTimer.running) + { + buttonList[buttonIndex].clicked () + } + } + + function setActiveButton (newIndex) + { + buttonIndex = newIndex + for (var i = 0; i < buttonList.length; i++) + if (i == newIndex) + buttonList[i].highlight = true + else + buttonList[i].highlight = false + targetTimer.restart () + } + + function clearHighlight () + { + for (var i = 0; i < buttonList.length; i++) + buttonList[i].highlight = false + } +} diff --git a/FileSets/v3.60~25/DetailAcInput.qml.orig b/FileSets/v3.60~25/DetailAcInput.qml.orig new file mode 100644 index 00000000..6efa32bf --- /dev/null +++ b/FileSets/v3.60~25/DetailAcInput.qml.orig @@ -0,0 +1,170 @@ +import QtQuick 2 +import "utils.js" as Utils + +Item { + id: root + + property string systemPrefix: "com.victronenergy.system" + property string vebusPrefix: _vebusService.valid ? _vebusService.value : "" + + property variant battery: _battery + property alias dcSystem: _dcSystem + property alias pvCharger: _pvCharger + property alias pvOnAcIn1: _pvOnAcIn1 + property alias pvOnAcIn2: _pvOnAcIn2 + property alias pvOnAcOut: _pvOnAcOut + property alias inverterChargerDc: _inverterChargerDc + property alias acLoad: _acLoad + property alias acInLoad: _acInLoad + property alias acOutLoad: _acOutLoad + property alias grid: _grid + property alias genset: _genset + property alias acInput: _activein + property VBusItem systemType: VBusItem { bind: Utils.path(systemPrefix, "/SystemType") } + property bool hasGridMeter: _hasGridMeter.valid + property variant acSource: _acSource.value + property VBusItem preferRenewableEnergy: VBusItem { bind: Utils.path(vebusPrefix, "/Dc/0/PreferRenewableEnergy") } + property VBusItem remoteGeneratorSelected: VBusItem { bind: Utils.path(vebusPrefix, "/Ac/State/RemoteGeneratorSelected") } + + property alias pvOnGrid: _pvOnAcIn2 + + property int batteryStateIdle: 0 + property int batteryStateCharging: 1 + property int batteryStateDischarging: 2 + + property int acSourceNotAvailable: 0 + property int acSourceGrid: 1 + property int acSourceGenset: 2 + property int acSourceShore: 3 // same as grid + + property alias pvInvertersProductIds: _pvInvertersProductIds + property alias batteryProductId: _batteryProductId + + VBusItem { + id: _pvInvertersProductIds + bind: Utils.path(systemPrefix, "/PvInvertersProductIds") + } + + VBusItem { + id: _batteryProductId + bind: Utils.path(systemPrefix, "/Dc/Battery/ProductId") + } + + VBusItem { + id: _vebusService + bind: Utils.path(systemPrefix, "/VebusService") + } + + QtObject { + id: _pvCharger + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Pv/Power"); unit: "W"} + } + + ObjectAcConnection { + id: _pvOnAcOut + bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnOutput") + } + + ObjectAcConnection { + id: _pvOnAcIn1 + bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGenset") + } + + ObjectAcConnection { + id: _pvOnAcIn2 + bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGrid") + } + + ObjectAcConnection { + id: _genset + splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0 + bindPrefix: Utils.path(systemPrefix, "/Ac/Genset") + } + + VBusItem { + id: _acSource + bind: Utils.path(systemPrefix, "/Ac/ActiveIn/Source") + } + + VBusItem { + id: _hasGridMeter + bind: Utils.path(systemPrefix, "/Ac/Grid/DeviceType") + } + + /* + * Single Multis that can be split-phase reports NrOfPhases of 2 + * When L2 is disconnected from the input the output L1 and L2 + * are shorted. This item indicates if L2 is passed through + * from AC-in to AC-out. + * 1: L2 is being passed through from AC-in to AC-out. + * 0: L1 and L2 are shorted together. + * invalid: The unit is configured in such way that its L2 output is not used. + */ + + VBusItem { + id: _splitPhaseL2Passthru + bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2Passthru") + } + + VBusItem { + id: _l2L1OutSummed + bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2L1OutSummed") + } + + + ObjectAcConnection { + id: _grid + splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0 + bindPrefix: Utils.path(systemPrefix, "/Ac/Grid") + } + + ObjectAcConnection { + id: _activein + bindPrefix: Utils.path(systemPrefix, "/Ac/ActiveIn") + } + + ObjectAcConnection { + id: _acLoad + l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0) + isAcOutput: true + bindPrefix: Utils.path(systemPrefix, "/Ac/Consumption") + } + + ObjectAcConnection { + id: _acOutLoad + l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0) + isAcOutput: true + bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnOutput") + } + + ObjectAcConnection { + id: _acInLoad + splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0 + bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnInput") + } + + ObjectAcConnection { + id: _acUnknown + } + + QtObject { + id: _inverterChargerDc + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/InverterCharger/Power"); unit: "W"} + } + + QtObject { + id: _battery + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Power"); unit: "W"} + property VBusItem voltage: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Voltage"); unit: "V"} + property VBusItem current: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Current"); unit: "A"} + property VBusItem soc: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Soc"); unit: "%"} + + // Get the battery charge state, see batteryState properties + property VBusItem state: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/State")} + } + + QtObject { + id: _dcSystem + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/System/Power"); unit: "W"} + } +} diff --git a/FileSets/v3.60~25/DetailInverter.qml b/FileSets/v3.60~25/DetailInverter.qml new file mode 100644 index 00000000..923761b3 --- /dev/null +++ b/FileSets/v3.60~25/DetailInverter.qml @@ -0,0 +1,650 @@ +////// detail page for setting inverter mode +////// and displaying inverter details +////// pushed from Flow overview + +import QtQuick 2 +import "utils.js" as Utils +import com.victron.velib 1.0 +import "enhancedFormat.js" as EnhFmt + +MbPage { + id: root + + title: "Inverter detail" + + property variant sys: theSystem + property string systemPrefix: "com.victronenergy.system" + + property int fontPixelSize: 18 + property color buttonColor: "#979797" + property color pressedColor: "#d3d3d3" + property color backgroundColor: "#b3b3b3" + + property int inverterMode: inverterModeItem.valid ? inverterModeItem.value : 0 + property bool editable: inverterService != "" && inverterModeItem.valid + + property int buttonHeight: 40 + property int buttonWidth: 72 + property int buttonAreaWidth: buttonWidth * 2 + 4 + + property int rowTitleWidth: 132 + property int dataColumns: 3 + property int totalDataWidth: root.width - rowTitleWidth - buttonAreaWidth - 12 + property int tableColumnWidth: totalDataWidth / dataColumns + property int legColumnWidth: phaseCount <= 1 ? totalDataWidth : totalDataWidth / phaseCount + + property int numberOfMultis: 0 + property int numberOfInverters: 0 + property string inverterService: "" + property bool isInverter: numberOfMultis === 0 && numberOfInverters === 1 + + Component.onCompleted: { discoverServices(); highlightMode () } + + property bool showChargePriority: numberOfMultis > 0 && sys.preferRenewableEnergy.valid + property bool preferRenewableEnergy: showChargePriority && sys.preferRenewableEnergy.value == 1 + property bool autoReturnToRenewable: sys.remoteGeneratorSelected.valid + property bool acInIsGenerator: sys.acSource == 2 + + VBusItem + { + id: inverterModeItem + bind: Utils.path(inverterService, "/Mode") + onValidChanged: highlightMode () + onValueChanged: highlightMode () + } + property VBusItem systemState: VBusItem { bind: Utils.path(systemPrefix, "/SystemState/State") } + SystemState + { + id: vebusState + bind: systemState.valid ? Utils.path(systemPrefix, "/SystemState/State") : Utils.path(inverterService, "/State") + } + VBusItem { id: pInL1; bind: Utils.path(inverterService, "/Ac/ActiveIn/L1/P") } + VBusItem { id: pInL2; bind: Utils.path(inverterService, "/Ac/ActiveIn/L2/P") } + VBusItem { id: pInL3; bind: Utils.path(inverterService, "/Ac/ActiveIn/L2/P") } + VBusItem { id: vInL1; bind: Utils.path(inverterService, "/Ac/ActiveIn/L1/V") } + VBusItem { id: vInL2; bind: Utils.path(inverterService, "/Ac/ActiveIn/L2/V") } + VBusItem { id: vInL3; bind: Utils.path(inverterService, "/Ac/ActiveIn/L3/V") } + VBusItem { id: iInL1; bind: Utils.path(inverterService, "/Ac/ActiveIn/L1/I") } + VBusItem { id: iInL2; bind: Utils.path(inverterService, "/Ac/ActiveIn/L2/I") } + VBusItem { id: iInL3; bind: Utils.path(inverterService, "/Ac/ActiveIn/L3/I") } + VBusItem { id: pOutL1; bind: Utils.path(inverterService, "/Ac/Out/L1/P") } + VBusItem { id: pOutL2; bind: Utils.path(inverterService, "/Ac/Out/L2/P") } + VBusItem { id: pOutL3; bind: Utils.path(inverterService, "/Ac/Out/L3/P") } + VBusItem { id: vOutL1; bind: Utils.path(inverterService, "/Ac/Out/L1/V") } + VBusItem { id: vOutL2; bind: Utils.path(inverterService, "/Ac/Out/L2/V") } + VBusItem { id: vOutL3; bind: Utils.path(inverterService, "/Ac/Out/L3/V") } + VBusItem { id: iOutL1; bind: Utils.path(inverterService, "/Ac/Out/L1/I") } + VBusItem { id: iOutL2; bind: Utils.path(inverterService, "/Ac/Out/L2/I") } + VBusItem { id: iOutL3; bind: Utils.path(inverterService, "/Ac/Out/L3/I") } + VBusItem { id: fInL1; bind: Utils.path(inverterService, "/Ac/ActiveIn/L1/F") } + VBusItem { id: fOutL1; bind: Utils.path(inverterService, "/Ac/Out/L1/F") } + VBusItem { id: dcPower; bind: Utils.path(inverterService, "/Dc/0/Power") } + VBusItem { id: dcCurrent; bind: Utils.path(inverterService, "/Dc/0/Current") } + VBusItem { id: _l2L1OutSummed; bind: Utils.path(inverterService, "/Ac/State/SplitPhaseL2L1OutSummed") } + VBusItem { id: phaseCountItem; bind: Utils.path(inverterService, "/Ac/NumberOfPhases") } + + property bool noL2inverter: _l2L1OutSummed.valid + property bool l2AndL1OutSummed: noL2inverter && _l2L1OutSummed.value === 1 + property int phaseCount: phaseCountItem.valid ? phaseCountItem.value : 0 + + // background + Rectangle + { + anchors + { + fill: parent + } + color: root.backgroundColor + } + + Column + { + spacing: 2 + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left; anchors.leftMargin: 3 + Row + { + PowerGaugeMulti + { + id: gauge + width: rowTitleWidth + totalDataWidth + height: 15 + inverterService: root.inverterService + } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Total Power") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: totalDataWidth; horizontalAlignment: Text.AlignHCenter + text: + { + var total = 0 + var totalValid = false + if (pOutL1.valid && pInL1.valid) + { + total += pOutL1.value - pInL1.value + totalValid = true + } + if (pOutL2.valid && pInL2.valid) + { + total += pOutL2.value - pInL2.value + totalValid = true + } + if (pOutL3.valid && pInL3.valid) + { + total += pOutL3.value - pInL3.value + totalValid = true + } + if (totalValid) + return EnhFmt.formatValue (total, "W") + else + return "--" + } + } + visible: phaseCount >= 2 + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("State") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: totalDataWidth; horizontalAlignment: Text.AlignHCenter + text: vebusState.text } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: "" } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: "L1" } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: "L2" } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: "L3"; visible: phaseCount >= 3 } + visible: phaseCount >= 2 + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Power") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: formatValueDiff (pOutL1, pInL1, "W") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: + { + if (l2AndL1OutSummed) + return "< < <" + else if (noL2inverter) + return qsTr("none") + else + return formatValueDiff (pOutL2, pInL2, "W") + } + visible: phaseCount >= 2 + } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: formatValueDiff (pOutL3, pInL3, "W"); visible: phaseCount >= 3 } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Input Voltage") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (vInL1, "V") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (vInL2, "V"); visible: phaseCount >= 2 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (vInL3, "V"); visible: phaseCount >= 3 } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Output Voltage") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (vOutL1, "V") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (vOutL2, "V"); visible: phaseCount >= 2 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (vOutL3, "V"); visible: phaseCount >= 3 } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Input Current") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (iInL1, "A") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (iInL2, "A"); visible: phaseCount >= 2 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (iInL3, "A"); visible: phaseCount >= 3 } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Output Current") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (iOutL1, "A") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: l2AndL1OutSummed ? "< < <" : EnhFmt.formatVBusItem (iOutL2, "A"); visible: phaseCount >= 2 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (iOutL3, "A"); visible: phaseCount >= 3 } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Frequency In / Out") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: totalDataWidth / 2; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (fInL1, "Hz") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: totalDataWidth / 2; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (fOutL1, "Hz") } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: + { + if (! dcPower.valid) + return "" + else if (dcPower.value > 0) + return qsTr ("DC: supplying") + else if (dcPower.value < 0) + return qsTr ("DC: consuming") + else + return "" + } + } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: totalDataWidth / 2; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItemAbs (dcPower, "W") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: totalDataWidth / 2; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItemAbs (dcCurrent, "A") } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth + totalDataWidth; horizontalAlignment: Text.AlignHCenter + text: l2AndL1OutSummed ? qsTr ("L2 Output values included in L1") : qsTr ("L2 AC out from AC in (no inverter)") + visible: noL2inverter + } + } + } + Column + { + id: inverterModeButtonArea + width: root.buttonAreaWidth + anchors.top: parent.top; anchors.topMargin: 3 + anchors.right: parent.right; anchors.rightMargin: 3 + spacing: 4 + + Row + { + Text + { + font.pixelSize: 12; color: "black" + width: root.buttonAreaWidth; horizontalAlignment: Text.AlignHCenter + text: qsTr("Inverter mode") + visible: showChargePriority + } + } + Row + { + spacing: 4 + DetailButton + { + id: onButton + baseColor: inverterMode === 3 ? "green" : "#e6ffe6" + pressedColor: root.pressedColor + height: root.buttonHeight + width: root.buttonWidth + visible: !isInverter + onClicked: changeMode(3) + content: TileText + { + text: qsTr("On"); font.bold: true; + color: inverterMode === 3 ? "white" : "gray" + } + } + DetailButton + { + id: offButton + baseColor: inverterMode === 4 ? "black" : "#e6e6e6" + pressedColor: root.pressedColor + height: root.buttonHeight + width: root.buttonWidth + onClicked: changeMode(4) + content: TileText + { + text: qsTr("Off"); font.bold: true; + color: inverterMode === 4 ? "white" : "gray" + } + } + } + Row + { + spacing: 4 + DetailButton + { + id: invertOnlyButton + baseColor: inverterMode === 2 ? "blue" : "#ccccff" + pressedColor: root.pressedColor + height: root.buttonHeight + width: root.buttonWidth + onClicked: changeMode(2) + content: TileText + { + text: isInverter ? qsTr("On") : qsTr("Inverter\nOnly"); font.bold: true; + color: inverterMode === 2 ? "white" : "gray" + } + } + DetailButton + { + id: chargeOnlyButton + baseColor: inverterMode === 1 ? "orange" : "#ffedcc" + pressedColor: root.pressedColor + height: root.buttonHeight + width: root.buttonWidth + visible: !isInverter + onClicked: changeMode(1) + content: TileText + { + text: qsTr("Charger\nOnly"); font.bold: true; + color: inverterMode === 1 ? "white" : "gray" + } + } + DetailButton + { + id: ecoButton + baseColor: inverterMode === 5 ? "orange" : "#ffedcc" + pressedColor: root.pressedColor + height: root.buttonHeight + width: root.buttonWidth + visible: isInverter + onClicked: changeMode(5) + content: TileText + { + text: qsTr("Eco"); font.bold: true; + color: inverterMode === 5 ? "white" : "black" + } + } + } + } + Column + { + id: chargePriorityButtonArea + width: root.buttonAreaWidth + anchors.bottom: parent.bottom; anchors.bottomMargin: 3 + anchors.right: parent.right; anchors.rightMargin: 3 + spacing: 4 + + Row + { + Text + { + font.pixelSize: 12; color: "black" + width: root.buttonAreaWidth; horizontalAlignment: Text.AlignHCenter + text: qsTr("Charge priority") + visible: showChargePriority + } + } + Row + { + spacing: 4 + DetailButton + { + id: acPriorityButton + baseColor: ! preferRenewableEnergy && ! acInIsGenerator ? "orange" : "#ffedcc" + pressedColor: root.pressedColor + height: root.buttonHeight + width: root.buttonWidth + onClicked: { if (! acInIsGenerator) sys.preferRenewableEnergy.setValue (0)} + visible: showChargePriority + content: TileText + { + text: "Grid"; font.bold: true + color: ! preferRenewableEnergy && ! acInIsGenerator ? "white" : "gray" + } + } + DetailButton + { + id: renewablePriorityButton + baseColor: preferRenewableEnergy && ! acInIsGenerator ? "green" : "#e6ffe6" + pressedColor: root.pressedColor + height: root.buttonHeight + width: root.buttonWidth + visible: showChargePriority + onClicked: { if (! acInIsGenerator) sys.preferRenewableEnergy.setValue (1)} + content: TileText + { + text: qsTr("Renew\nable"); font.bold: true + color: preferRenewableEnergy && ! acInIsGenerator ? "white" : "gray" + } + } + } + Row + { + Text + { + font.pixelSize: 12; color: "black" + width: root.buttonAreaWidth; horizontalAlignment: Text.AlignHCenter + text: + { + if (acInIsGenerator) + return qsTr ("Generator active\nno priority") + else if (autoReturnToRenewable && ! preferRenewableEnergy) + return qsTr ("returns to Renewable\n at 100% SOC") + else return "\n" + } + visible: showChargePriority + } + } + } + + + function changeMode(newMode) + { + if (editable) + { + inverterModeItem.setValue(newMode) + pageStack.pop() // return to flow screen after changing inverter mode + } + } + + function cancel() + { + pageStack.pop() + } + + function highlightMode () + { + if (editable) + inverterMode = inverterModeItem.value + else + inverterMode = 0 + } + + + // When new service is found check if is a tank sensor + Connections + { + target: DBusServices + onDbusServiceFound: addService(service) + } + + function addService(service) + { + switch (service.type) + { + case DBusService.DBUS_SERVICE_MULTI: + case DBusService.DBUS_SERVICE_MULTI_RS: + numberOfMultis++ + if (numberOfMultis === 1) + inverterService = service.name; + break;; + case DBusService.DBUS_SERVICE_INVERTER: + numberOfInverters++ + if (numberOfInverters === 1 && numberOfMultis === 0) + inverterService = service.name; + break;; + } + } + + // Detect available services of interest + function discoverServices() + { + numberOfMultis = 0 + numberOfInverters = 0 + inverterService = "" + for (var i = 0; i < DBusServices.count; i++) + { + addService(DBusServices.at(i)) + } + } + + function formatValueDiff (item1, item2, unit) + { + if (item1.valid && item2.valid) + return EnhFmt.formatValue (item1.value - item2.value, unit) + else + return "--" + } + + + //// hard key handler + // used to press buttons when touch isn't available + // UP and DOWN buttons cycle through the list of buttons + // "space" button is used to simulate a button press + // button must be highlighted so that other uses of "space" + // will still occur + + // list of buttons to be accessed via hard buttons + property variant buttonList: + [ + onButton, offButton, invertOnlyButton, chargeOnlyButton, ecoButton + ] + + property int buttonIndex: 0 + + Timer + { + id: targetTimer + interval: 5000 + repeat: false + running: false + onTriggered: { clearHighlight () } + } + + Keys.forwardTo: [keyHandler] + + Item + { + id: keyHandler + Keys.onDownPressed: + { + nextTarget (+1) + event.accepted = true + } + + Keys.onUpPressed: + { + nextTarget (-1) + event.accepted = true + } + } + + function nextTarget (increment) + { + // make one pass through all possible targets to find an enabled one + // if found, that's the new selectedTarget, + // if not selectedTarget does not change + var newIndex = buttonIndex + for (var i = 0; i < buttonList.length; i++) + { + // just restore highlight if not visible + if ( ! targetTimer.running && buttonList[newIndex].visible) + { + setActiveButton (buttonIndex) + return + } + newIndex += increment + if (newIndex >= buttonList.length) + newIndex = 0 + else if (newIndex < 0) + newIndex = buttonList.length - 1 + if (buttonList[newIndex].visible) + { + setActiveButton (newIndex) + break + } + } + } + + // Keys.onSpacePressed doesn't work - stolen by pageHandler + // so build a custom page handler so "space" can be used to press a button + pageToolbarHandler: detailToolbarHandler + ToolbarHandlerPages + { + id: detailToolbarHandler + isDefault: true + function centerAction() + { + acceptSpaceButton () + } + } + + function acceptSpaceButton () + { + if (targetTimer.running) + { + buttonList[buttonIndex].clicked () + } + } + + function setActiveButton (newIndex) + { + buttonIndex = newIndex + for (var i = 0; i < buttonList.length; i++) + if (i == newIndex) + buttonList[i].highlight = true + else + buttonList[i].highlight = false + targetTimer.restart () + } + + function clearHighlight () + { + for (var i = 0; i < buttonList.length; i++) + buttonList[i].highlight = false + } +} diff --git a/FileSets/v3.60~25/DetailInverter.qml.orig b/FileSets/v3.60~25/DetailInverter.qml.orig new file mode 100644 index 00000000..6efa32bf --- /dev/null +++ b/FileSets/v3.60~25/DetailInverter.qml.orig @@ -0,0 +1,170 @@ +import QtQuick 2 +import "utils.js" as Utils + +Item { + id: root + + property string systemPrefix: "com.victronenergy.system" + property string vebusPrefix: _vebusService.valid ? _vebusService.value : "" + + property variant battery: _battery + property alias dcSystem: _dcSystem + property alias pvCharger: _pvCharger + property alias pvOnAcIn1: _pvOnAcIn1 + property alias pvOnAcIn2: _pvOnAcIn2 + property alias pvOnAcOut: _pvOnAcOut + property alias inverterChargerDc: _inverterChargerDc + property alias acLoad: _acLoad + property alias acInLoad: _acInLoad + property alias acOutLoad: _acOutLoad + property alias grid: _grid + property alias genset: _genset + property alias acInput: _activein + property VBusItem systemType: VBusItem { bind: Utils.path(systemPrefix, "/SystemType") } + property bool hasGridMeter: _hasGridMeter.valid + property variant acSource: _acSource.value + property VBusItem preferRenewableEnergy: VBusItem { bind: Utils.path(vebusPrefix, "/Dc/0/PreferRenewableEnergy") } + property VBusItem remoteGeneratorSelected: VBusItem { bind: Utils.path(vebusPrefix, "/Ac/State/RemoteGeneratorSelected") } + + property alias pvOnGrid: _pvOnAcIn2 + + property int batteryStateIdle: 0 + property int batteryStateCharging: 1 + property int batteryStateDischarging: 2 + + property int acSourceNotAvailable: 0 + property int acSourceGrid: 1 + property int acSourceGenset: 2 + property int acSourceShore: 3 // same as grid + + property alias pvInvertersProductIds: _pvInvertersProductIds + property alias batteryProductId: _batteryProductId + + VBusItem { + id: _pvInvertersProductIds + bind: Utils.path(systemPrefix, "/PvInvertersProductIds") + } + + VBusItem { + id: _batteryProductId + bind: Utils.path(systemPrefix, "/Dc/Battery/ProductId") + } + + VBusItem { + id: _vebusService + bind: Utils.path(systemPrefix, "/VebusService") + } + + QtObject { + id: _pvCharger + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Pv/Power"); unit: "W"} + } + + ObjectAcConnection { + id: _pvOnAcOut + bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnOutput") + } + + ObjectAcConnection { + id: _pvOnAcIn1 + bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGenset") + } + + ObjectAcConnection { + id: _pvOnAcIn2 + bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGrid") + } + + ObjectAcConnection { + id: _genset + splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0 + bindPrefix: Utils.path(systemPrefix, "/Ac/Genset") + } + + VBusItem { + id: _acSource + bind: Utils.path(systemPrefix, "/Ac/ActiveIn/Source") + } + + VBusItem { + id: _hasGridMeter + bind: Utils.path(systemPrefix, "/Ac/Grid/DeviceType") + } + + /* + * Single Multis that can be split-phase reports NrOfPhases of 2 + * When L2 is disconnected from the input the output L1 and L2 + * are shorted. This item indicates if L2 is passed through + * from AC-in to AC-out. + * 1: L2 is being passed through from AC-in to AC-out. + * 0: L1 and L2 are shorted together. + * invalid: The unit is configured in such way that its L2 output is not used. + */ + + VBusItem { + id: _splitPhaseL2Passthru + bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2Passthru") + } + + VBusItem { + id: _l2L1OutSummed + bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2L1OutSummed") + } + + + ObjectAcConnection { + id: _grid + splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0 + bindPrefix: Utils.path(systemPrefix, "/Ac/Grid") + } + + ObjectAcConnection { + id: _activein + bindPrefix: Utils.path(systemPrefix, "/Ac/ActiveIn") + } + + ObjectAcConnection { + id: _acLoad + l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0) + isAcOutput: true + bindPrefix: Utils.path(systemPrefix, "/Ac/Consumption") + } + + ObjectAcConnection { + id: _acOutLoad + l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0) + isAcOutput: true + bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnOutput") + } + + ObjectAcConnection { + id: _acInLoad + splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0 + bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnInput") + } + + ObjectAcConnection { + id: _acUnknown + } + + QtObject { + id: _inverterChargerDc + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/InverterCharger/Power"); unit: "W"} + } + + QtObject { + id: _battery + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Power"); unit: "W"} + property VBusItem voltage: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Voltage"); unit: "V"} + property VBusItem current: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Current"); unit: "A"} + property VBusItem soc: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Soc"); unit: "%"} + + // Get the battery charge state, see batteryState properties + property VBusItem state: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/State")} + } + + QtObject { + id: _dcSystem + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/System/Power"); unit: "W"} + } +} diff --git a/FileSets/v3.60~25/DetailLoadsCombined.qml b/FileSets/v3.60~25/DetailLoadsCombined.qml new file mode 100644 index 00000000..025e55a7 --- /dev/null +++ b/FileSets/v3.60~25/DetailLoadsCombined.qml @@ -0,0 +1,146 @@ +////// detail page for displaying critical AC output details +////// pushed from Flow overview + +import QtQuick 2 +import "utils.js" as Utils +import com.victron.velib 1.0 +import "enhancedFormat.js" as EnhFmt + +MbPage { + id: root + + title: qsTr ("AC Loads detail") + + property variant sys: theSystem + property string systemPrefix: "com.victronenergy.system" + + property int fontPixelSize: 18 + property color backgroundColor: "#b3b3b3" + + property int dataColumns: 3 + property int rowTitleWidth: 130 + property int totalDataWidth: root.width - rowTitleWidth - 20 + property int tableColumnWidth: totalDataWidth / dataColumns + property int legColumnWidth: phaseCount <= 1 ? totalDataWidth : totalDataWidth / phaseCount + + property int phaseCount: sys.acLoad.phaseCount.valid ? sys.acLoad.phaseCount.value : 0 + + VBusItem { id: vebusServiceItem; bind: Utils.path(systemPrefix, "/VebusService") } + property string inverterService: vebusServiceItem.valid ? vebusServiceItem.value : "" + property bool l2AndL1OutSummed: sys.acLoad.l2AndL1OutSummed + + // background + Rectangle + { + anchors + { + fill: parent + } + color: root.backgroundColor + } + + Row + { + spacing: 5 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + Column + { + spacing: 2 + Row + { + Text { id: totalLabel; font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Total Power") } + Text { id: totalPower; font.pixelSize: 12; font.bold: true; color: "black" + width: tableColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.acLoad.power, "W") + } + PowerGauge + { + id: gauge + width: (root.width * 0.9) - totalLabel.width - totalPower.width + height: 15 + maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/AcOutputMaxPower" + connection: sys.acLoad + } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: "" } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: "L1" } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: "L2"; visible: phaseCount >= 2 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: "L3"; visible: phaseCount >= 3 } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Power") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.acLoad.powerL1, "W") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: l2AndL1OutSummed ? "< < <" : EnhFmt.formatVBusItem (sys.acLoad.powerL2, "W"); visible: phaseCount >= 2 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.acLoad.powerL3, "W"); visible: phaseCount >= 3 } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Voltage") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.acLoad.voltageL1, "V") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: l2AndL1OutSummed ? "< < <" : EnhFmt.formatVBusItem (sys.acLoad.voltageL2, "V"); visible: phaseCount >= 2 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.acLoad.voltageL3, "V"); visible: phaseCount >= 3 } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Current") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.acLoad.currentL1, "A") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: l2AndL1OutSummed ? "< < <" : EnhFmt.formatVBusItem (sys.acLoad.currentL2, "A"); visible: phaseCount >= 2 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.acLoad.currentL3, "A"); visible: phaseCount >= 3 } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Frequency") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: totalDataWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.acLoad.frequency, "Hz") } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth + totalDataWidth; horizontalAlignment: Text.AlignHCenter + text: "L2 values included in L1" + visible: l2AndL1OutSummed } + } + } + } +} diff --git a/FileSets/v3.60~25/DetailLoadsCombined.qml.orig b/FileSets/v3.60~25/DetailLoadsCombined.qml.orig new file mode 100644 index 00000000..6efa32bf --- /dev/null +++ b/FileSets/v3.60~25/DetailLoadsCombined.qml.orig @@ -0,0 +1,170 @@ +import QtQuick 2 +import "utils.js" as Utils + +Item { + id: root + + property string systemPrefix: "com.victronenergy.system" + property string vebusPrefix: _vebusService.valid ? _vebusService.value : "" + + property variant battery: _battery + property alias dcSystem: _dcSystem + property alias pvCharger: _pvCharger + property alias pvOnAcIn1: _pvOnAcIn1 + property alias pvOnAcIn2: _pvOnAcIn2 + property alias pvOnAcOut: _pvOnAcOut + property alias inverterChargerDc: _inverterChargerDc + property alias acLoad: _acLoad + property alias acInLoad: _acInLoad + property alias acOutLoad: _acOutLoad + property alias grid: _grid + property alias genset: _genset + property alias acInput: _activein + property VBusItem systemType: VBusItem { bind: Utils.path(systemPrefix, "/SystemType") } + property bool hasGridMeter: _hasGridMeter.valid + property variant acSource: _acSource.value + property VBusItem preferRenewableEnergy: VBusItem { bind: Utils.path(vebusPrefix, "/Dc/0/PreferRenewableEnergy") } + property VBusItem remoteGeneratorSelected: VBusItem { bind: Utils.path(vebusPrefix, "/Ac/State/RemoteGeneratorSelected") } + + property alias pvOnGrid: _pvOnAcIn2 + + property int batteryStateIdle: 0 + property int batteryStateCharging: 1 + property int batteryStateDischarging: 2 + + property int acSourceNotAvailable: 0 + property int acSourceGrid: 1 + property int acSourceGenset: 2 + property int acSourceShore: 3 // same as grid + + property alias pvInvertersProductIds: _pvInvertersProductIds + property alias batteryProductId: _batteryProductId + + VBusItem { + id: _pvInvertersProductIds + bind: Utils.path(systemPrefix, "/PvInvertersProductIds") + } + + VBusItem { + id: _batteryProductId + bind: Utils.path(systemPrefix, "/Dc/Battery/ProductId") + } + + VBusItem { + id: _vebusService + bind: Utils.path(systemPrefix, "/VebusService") + } + + QtObject { + id: _pvCharger + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Pv/Power"); unit: "W"} + } + + ObjectAcConnection { + id: _pvOnAcOut + bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnOutput") + } + + ObjectAcConnection { + id: _pvOnAcIn1 + bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGenset") + } + + ObjectAcConnection { + id: _pvOnAcIn2 + bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGrid") + } + + ObjectAcConnection { + id: _genset + splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0 + bindPrefix: Utils.path(systemPrefix, "/Ac/Genset") + } + + VBusItem { + id: _acSource + bind: Utils.path(systemPrefix, "/Ac/ActiveIn/Source") + } + + VBusItem { + id: _hasGridMeter + bind: Utils.path(systemPrefix, "/Ac/Grid/DeviceType") + } + + /* + * Single Multis that can be split-phase reports NrOfPhases of 2 + * When L2 is disconnected from the input the output L1 and L2 + * are shorted. This item indicates if L2 is passed through + * from AC-in to AC-out. + * 1: L2 is being passed through from AC-in to AC-out. + * 0: L1 and L2 are shorted together. + * invalid: The unit is configured in such way that its L2 output is not used. + */ + + VBusItem { + id: _splitPhaseL2Passthru + bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2Passthru") + } + + VBusItem { + id: _l2L1OutSummed + bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2L1OutSummed") + } + + + ObjectAcConnection { + id: _grid + splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0 + bindPrefix: Utils.path(systemPrefix, "/Ac/Grid") + } + + ObjectAcConnection { + id: _activein + bindPrefix: Utils.path(systemPrefix, "/Ac/ActiveIn") + } + + ObjectAcConnection { + id: _acLoad + l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0) + isAcOutput: true + bindPrefix: Utils.path(systemPrefix, "/Ac/Consumption") + } + + ObjectAcConnection { + id: _acOutLoad + l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0) + isAcOutput: true + bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnOutput") + } + + ObjectAcConnection { + id: _acInLoad + splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0 + bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnInput") + } + + ObjectAcConnection { + id: _acUnknown + } + + QtObject { + id: _inverterChargerDc + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/InverterCharger/Power"); unit: "W"} + } + + QtObject { + id: _battery + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Power"); unit: "W"} + property VBusItem voltage: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Voltage"); unit: "V"} + property VBusItem current: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Current"); unit: "A"} + property VBusItem soc: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Soc"); unit: "%"} + + // Get the battery charge state, see batteryState properties + property VBusItem state: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/State")} + } + + QtObject { + id: _dcSystem + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/System/Power"); unit: "W"} + } +} diff --git a/FileSets/v3.60~25/DetailLoadsOnInput.qml b/FileSets/v3.60~25/DetailLoadsOnInput.qml new file mode 100644 index 00000000..fd228670 --- /dev/null +++ b/FileSets/v3.60~25/DetailLoadsOnInput.qml @@ -0,0 +1,146 @@ +////// detail page for displaying non-critical AC output details +////// pushed from Flow overview + +import QtQuick 2 +import "utils.js" as Utils +import com.victron.velib 1.0 +import "enhancedFormat.js" as EnhFmt + +MbPage { + id: root + + title: "Loads on AC Input Detail" + + property variant sys: theSystem + property string systemPrefix: "com.victronenergy.system" + + property int fontPixelSize: 18 + property color backgroundColor: "#b3b3b3" + + property int dataColumns: 3 + property int rowTitleWidth: 130 + property int totalDataWidth: root.width - rowTitleWidth - 20 + property int tableColumnWidth: totalDataWidth / dataColumns + property int legColumnWidth: phaseCount <= 1 ? totalDataWidth : totalDataWidth / phaseCount + + property int phaseCount: sys.acInLoad.phaseCount.valid ? sys.acInLoad.phaseCount.value : 0 + + VBusItem { id: vebusServiceItem; bind: Utils.path(systemPrefix, "/VebusService") } + property string inverterService: vebusServiceItem.valid ? vebusServiceItem.value : "" + property bool splitPhasePassthruDisabled: sys.acInput.splitPhasePassthruDisabled + + // background + Rectangle + { + anchors + { + fill: parent + } + color: root.backgroundColor + } + + Row + { + spacing: 5 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + Column + { + spacing: 2 + Row + { + Text { id: totalLabel; font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Total Power") } + Text { id: totalPower; font.pixelSize: 12; font.bold: true; color: "black" + width: tableColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.acInLoad.power, "W") + } + PowerGauge + { + id: gauge + width: (root.width * 0.9) - totalLabel.width - totalPower.width + height: 15 + maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/AcOutputMaxPower" + connection: sys.acInLoad + } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: "" } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: "L1" } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: "L2"; visible: phaseCount >= 2 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: "L3"; visible: phaseCount >= 3 } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Power") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.acInLoad.powerL1, "W") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: splitPhasePassthruDisabled ? "< < <" : EnhFmt.formatVBusItem (sys.acInLoad.powerL2, "W"); visible: phaseCount >= 2 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.acInLoad.powerL3, "W"); visible: phaseCount >= 3 } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Voltage") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.acInLoad.voltageL1, "V") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: splitPhasePassthruDisabled ? "< < <" : EnhFmt.formatVBusItem (sys.acInLoad.voltageL2, "V"); visible: phaseCount >= 2 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.acInLoad.voltageL3, "V"); visible: phaseCount >= 3 } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Current") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.acInLoad.currentL1, "A") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: splitPhasePassthruDisabled ? "< < <" : EnhFmt.formatVBusItem (sys.acInLoad.currentL2, "A"); visible: phaseCount >= 2 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.acInLoad.currentL3, "A"); visible: phaseCount >= 3 } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Frequency") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: totalDataWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (sys.acInLoad.frequency, "Hz") } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth + totalDataWidth; horizontalAlignment: Text.AlignHCenter + text: "L2 values included in L1" + visible: splitPhasePassthruDisabled } + } + } + } +} diff --git a/FileSets/v3.60~25/DetailLoadsOnInput.qml.orig b/FileSets/v3.60~25/DetailLoadsOnInput.qml.orig new file mode 100644 index 00000000..6efa32bf --- /dev/null +++ b/FileSets/v3.60~25/DetailLoadsOnInput.qml.orig @@ -0,0 +1,170 @@ +import QtQuick 2 +import "utils.js" as Utils + +Item { + id: root + + property string systemPrefix: "com.victronenergy.system" + property string vebusPrefix: _vebusService.valid ? _vebusService.value : "" + + property variant battery: _battery + property alias dcSystem: _dcSystem + property alias pvCharger: _pvCharger + property alias pvOnAcIn1: _pvOnAcIn1 + property alias pvOnAcIn2: _pvOnAcIn2 + property alias pvOnAcOut: _pvOnAcOut + property alias inverterChargerDc: _inverterChargerDc + property alias acLoad: _acLoad + property alias acInLoad: _acInLoad + property alias acOutLoad: _acOutLoad + property alias grid: _grid + property alias genset: _genset + property alias acInput: _activein + property VBusItem systemType: VBusItem { bind: Utils.path(systemPrefix, "/SystemType") } + property bool hasGridMeter: _hasGridMeter.valid + property variant acSource: _acSource.value + property VBusItem preferRenewableEnergy: VBusItem { bind: Utils.path(vebusPrefix, "/Dc/0/PreferRenewableEnergy") } + property VBusItem remoteGeneratorSelected: VBusItem { bind: Utils.path(vebusPrefix, "/Ac/State/RemoteGeneratorSelected") } + + property alias pvOnGrid: _pvOnAcIn2 + + property int batteryStateIdle: 0 + property int batteryStateCharging: 1 + property int batteryStateDischarging: 2 + + property int acSourceNotAvailable: 0 + property int acSourceGrid: 1 + property int acSourceGenset: 2 + property int acSourceShore: 3 // same as grid + + property alias pvInvertersProductIds: _pvInvertersProductIds + property alias batteryProductId: _batteryProductId + + VBusItem { + id: _pvInvertersProductIds + bind: Utils.path(systemPrefix, "/PvInvertersProductIds") + } + + VBusItem { + id: _batteryProductId + bind: Utils.path(systemPrefix, "/Dc/Battery/ProductId") + } + + VBusItem { + id: _vebusService + bind: Utils.path(systemPrefix, "/VebusService") + } + + QtObject { + id: _pvCharger + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Pv/Power"); unit: "W"} + } + + ObjectAcConnection { + id: _pvOnAcOut + bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnOutput") + } + + ObjectAcConnection { + id: _pvOnAcIn1 + bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGenset") + } + + ObjectAcConnection { + id: _pvOnAcIn2 + bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGrid") + } + + ObjectAcConnection { + id: _genset + splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0 + bindPrefix: Utils.path(systemPrefix, "/Ac/Genset") + } + + VBusItem { + id: _acSource + bind: Utils.path(systemPrefix, "/Ac/ActiveIn/Source") + } + + VBusItem { + id: _hasGridMeter + bind: Utils.path(systemPrefix, "/Ac/Grid/DeviceType") + } + + /* + * Single Multis that can be split-phase reports NrOfPhases of 2 + * When L2 is disconnected from the input the output L1 and L2 + * are shorted. This item indicates if L2 is passed through + * from AC-in to AC-out. + * 1: L2 is being passed through from AC-in to AC-out. + * 0: L1 and L2 are shorted together. + * invalid: The unit is configured in such way that its L2 output is not used. + */ + + VBusItem { + id: _splitPhaseL2Passthru + bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2Passthru") + } + + VBusItem { + id: _l2L1OutSummed + bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2L1OutSummed") + } + + + ObjectAcConnection { + id: _grid + splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0 + bindPrefix: Utils.path(systemPrefix, "/Ac/Grid") + } + + ObjectAcConnection { + id: _activein + bindPrefix: Utils.path(systemPrefix, "/Ac/ActiveIn") + } + + ObjectAcConnection { + id: _acLoad + l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0) + isAcOutput: true + bindPrefix: Utils.path(systemPrefix, "/Ac/Consumption") + } + + ObjectAcConnection { + id: _acOutLoad + l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0) + isAcOutput: true + bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnOutput") + } + + ObjectAcConnection { + id: _acInLoad + splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0 + bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnInput") + } + + ObjectAcConnection { + id: _acUnknown + } + + QtObject { + id: _inverterChargerDc + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/InverterCharger/Power"); unit: "W"} + } + + QtObject { + id: _battery + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Power"); unit: "W"} + property VBusItem voltage: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Voltage"); unit: "V"} + property VBusItem current: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Current"); unit: "A"} + property VBusItem soc: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Soc"); unit: "%"} + + // Get the battery charge state, see batteryState properties + property VBusItem state: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/State")} + } + + QtObject { + id: _dcSystem + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/System/Power"); unit: "W"} + } +} diff --git a/FileSets/v3.60~25/DetailLoadsOnOutput.qml b/FileSets/v3.60~25/DetailLoadsOnOutput.qml new file mode 100644 index 00000000..9344a5f1 --- /dev/null +++ b/FileSets/v3.60~25/DetailLoadsOnOutput.qml @@ -0,0 +1,151 @@ +////// detail page for displaying critical AC output details +////// pushed from Flow overview + +import QtQuick 2 +import "utils.js" as Utils +import com.victron.velib 1.0 +import "enhancedFormat.js" as EnhFmt + +MbPage { + id: root + + title: combineAcLoads ? qsTr ("AC Loads detail") : qsTr ("Loads on AC Output detail") + + property variant sys: theSystem + property string systemPrefix: "com.victronenergy.system" + property string settingsPrefix: "com.victronenergy.settings" + + property int fontPixelSize: 18 + property color backgroundColor: "#b3b3b3" + + property int dataColumns: 3 + property int rowTitleWidth: 130 + property int totalDataWidth: root.width - rowTitleWidth - 20 + property int tableColumnWidth: totalDataWidth / dataColumns + property int legColumnWidth: phaseCount <= 1 ? totalDataWidth : totalDataWidth / phaseCount + + property int phaseCount: outputLoad.phaseCount.valid ? outputLoad.phaseCount.value : 0 + + VBusItem { id: vebusServiceItem; bind: Utils.path(systemPrefix, "/VebusService") } + property string inverterService: vebusServiceItem.valid ? vebusServiceItem.value : "" + property bool l2AndL1OutSummed: sys.acLoad.l2AndL1OutSummed + + VBusItem { id: _combineAcLoads; bind: "com.victronenergy.settings/Settings/GuiMods/EnhancedFlowCombineLoads" } + property bool combineAcLoads: _combineAcLoads.valid && _combineAcLoads.value === 1 + property variant outputLoad: combineAcLoads ? sys.acLoad : sys.acOutLoad + + // background + Rectangle + { + anchors + { + fill: parent + } + color: root.backgroundColor + } + + Row + { + spacing: 5 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + Column + { + spacing: 2 + Row + { + Text { id: totalLabel; font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Total Power") } + Text { id: totalPower; font.pixelSize: 12; font.bold: true; color: "black" + width: tableColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (outputLoad.power, "W") + } + PowerGauge + { + id: gauge + width: (root.width * 0.9) - totalLabel.width - totalPower.width + height: 15 + maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/AcOutputMaxPower" + connection: outputLoad + } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: "" } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: "L1" } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: "L2"; visible: phaseCount >= 2 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: "L3"; visible: phaseCount >= 3 } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Power") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (outputLoad.powerL1, "W") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: l2AndL1OutSummed ? "< < <" : EnhFmt.formatVBusItem (outputLoad.powerL2, "W"); visible: phaseCount >= 2 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (outputLoad.powerL3, "W"); visible: phaseCount >= 3 } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Voltage") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (outputLoad.voltageL1, "V") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: l2AndL1OutSummed ? "< < <" : EnhFmt.formatVBusItem (outputLoad.voltageL2, "V"); visible: phaseCount >= 2 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (outputLoad.voltageL3, "V"); visible: phaseCount >= 3 } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Current") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (outputLoad.currentL1, "A") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: l2AndL1OutSummed ? "< < <" : EnhFmt.formatVBusItem (outputLoad.currentL2, "A"); visible: phaseCount >= 2 } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: legColumnWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (outputLoad.currentL3, "A"); visible: phaseCount >= 3 } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth; horizontalAlignment: Text.AlignRight + text: qsTr("Frequency") } + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: totalDataWidth; horizontalAlignment: Text.AlignHCenter + text: EnhFmt.formatVBusItem (outputLoad.frequency, "Hz") } + } + Row + { + Text { font.pixelSize: 12; font.bold: true; color: "black" + width: rowTitleWidth + totalDataWidth; horizontalAlignment: Text.AlignHCenter + text: "L2 values included in L1" + visible: l2AndL1OutSummed } + } + } + } +} diff --git a/FileSets/v3.60~25/DetailLoadsOnOutput.qml.orig b/FileSets/v3.60~25/DetailLoadsOnOutput.qml.orig new file mode 100644 index 00000000..6efa32bf --- /dev/null +++ b/FileSets/v3.60~25/DetailLoadsOnOutput.qml.orig @@ -0,0 +1,170 @@ +import QtQuick 2 +import "utils.js" as Utils + +Item { + id: root + + property string systemPrefix: "com.victronenergy.system" + property string vebusPrefix: _vebusService.valid ? _vebusService.value : "" + + property variant battery: _battery + property alias dcSystem: _dcSystem + property alias pvCharger: _pvCharger + property alias pvOnAcIn1: _pvOnAcIn1 + property alias pvOnAcIn2: _pvOnAcIn2 + property alias pvOnAcOut: _pvOnAcOut + property alias inverterChargerDc: _inverterChargerDc + property alias acLoad: _acLoad + property alias acInLoad: _acInLoad + property alias acOutLoad: _acOutLoad + property alias grid: _grid + property alias genset: _genset + property alias acInput: _activein + property VBusItem systemType: VBusItem { bind: Utils.path(systemPrefix, "/SystemType") } + property bool hasGridMeter: _hasGridMeter.valid + property variant acSource: _acSource.value + property VBusItem preferRenewableEnergy: VBusItem { bind: Utils.path(vebusPrefix, "/Dc/0/PreferRenewableEnergy") } + property VBusItem remoteGeneratorSelected: VBusItem { bind: Utils.path(vebusPrefix, "/Ac/State/RemoteGeneratorSelected") } + + property alias pvOnGrid: _pvOnAcIn2 + + property int batteryStateIdle: 0 + property int batteryStateCharging: 1 + property int batteryStateDischarging: 2 + + property int acSourceNotAvailable: 0 + property int acSourceGrid: 1 + property int acSourceGenset: 2 + property int acSourceShore: 3 // same as grid + + property alias pvInvertersProductIds: _pvInvertersProductIds + property alias batteryProductId: _batteryProductId + + VBusItem { + id: _pvInvertersProductIds + bind: Utils.path(systemPrefix, "/PvInvertersProductIds") + } + + VBusItem { + id: _batteryProductId + bind: Utils.path(systemPrefix, "/Dc/Battery/ProductId") + } + + VBusItem { + id: _vebusService + bind: Utils.path(systemPrefix, "/VebusService") + } + + QtObject { + id: _pvCharger + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Pv/Power"); unit: "W"} + } + + ObjectAcConnection { + id: _pvOnAcOut + bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnOutput") + } + + ObjectAcConnection { + id: _pvOnAcIn1 + bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGenset") + } + + ObjectAcConnection { + id: _pvOnAcIn2 + bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGrid") + } + + ObjectAcConnection { + id: _genset + splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0 + bindPrefix: Utils.path(systemPrefix, "/Ac/Genset") + } + + VBusItem { + id: _acSource + bind: Utils.path(systemPrefix, "/Ac/ActiveIn/Source") + } + + VBusItem { + id: _hasGridMeter + bind: Utils.path(systemPrefix, "/Ac/Grid/DeviceType") + } + + /* + * Single Multis that can be split-phase reports NrOfPhases of 2 + * When L2 is disconnected from the input the output L1 and L2 + * are shorted. This item indicates if L2 is passed through + * from AC-in to AC-out. + * 1: L2 is being passed through from AC-in to AC-out. + * 0: L1 and L2 are shorted together. + * invalid: The unit is configured in such way that its L2 output is not used. + */ + + VBusItem { + id: _splitPhaseL2Passthru + bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2Passthru") + } + + VBusItem { + id: _l2L1OutSummed + bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2L1OutSummed") + } + + + ObjectAcConnection { + id: _grid + splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0 + bindPrefix: Utils.path(systemPrefix, "/Ac/Grid") + } + + ObjectAcConnection { + id: _activein + bindPrefix: Utils.path(systemPrefix, "/Ac/ActiveIn") + } + + ObjectAcConnection { + id: _acLoad + l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0) + isAcOutput: true + bindPrefix: Utils.path(systemPrefix, "/Ac/Consumption") + } + + ObjectAcConnection { + id: _acOutLoad + l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0) + isAcOutput: true + bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnOutput") + } + + ObjectAcConnection { + id: _acInLoad + splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0 + bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnInput") + } + + ObjectAcConnection { + id: _acUnknown + } + + QtObject { + id: _inverterChargerDc + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/InverterCharger/Power"); unit: "W"} + } + + QtObject { + id: _battery + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Power"); unit: "W"} + property VBusItem voltage: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Voltage"); unit: "V"} + property VBusItem current: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Current"); unit: "A"} + property VBusItem soc: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Soc"); unit: "%"} + + // Get the battery charge state, see batteryState properties + property VBusItem state: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/State")} + } + + QtObject { + id: _dcSystem + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/System/Power"); unit: "W"} + } +} diff --git a/FileSets/v3.60~25/OverviewAcValuesEnhanced.qml b/FileSets/v3.60~25/OverviewAcValuesEnhanced.qml new file mode 100644 index 00000000..96882534 --- /dev/null +++ b/FileSets/v3.60~25/OverviewAcValuesEnhanced.qml @@ -0,0 +1,94 @@ +////// modified to show voltage, current and frequency in flow overview +// only displays values for sys.acInput and sys.acLoad +// because other connections don't have related parameters +////// modified to show power bar graphs + + +import QtQuick 2 +import "enhancedFormat.js" as EnhFmt + +Item { + id: root + width: parent.width + height: parent.height + + // NOTE: data is taken by qml, hence it is called connection + property variant connection + + property int phaseCount: root.connection !== undefined && root.connection.phaseCount.valid ? root.connection.phaseCount.value : 0 + + Column { +////// modified to show power bar graphs + y: 6 + + width: parent.width + spacing: 0 + + // total power + TileText { + text: EnhFmt.formatVBusItem (root.connection.power) +////// modified to show power bar graphs + font.pixelSize: 19 + height: 21 + } + + // voltage for single leg + TileText { + text: EnhFmt.formatVBusItem (root.connection.voltageL1, "V") + visible: phaseCount <= 1 + font.pixelSize: 15 + } + // current for single leg + TileText { + text: EnhFmt.formatVBusItem (root.connection.currentL1, "A") + font.pixelSize: 15 + visible: phaseCount <= 1 + } + + // power, voltage and current for multiple legs + TileText { + text: "L1: " + EnhFmt.formatVBusItem (root.connection.powerL1, "W") + + " " + EnhFmt.formatVBusItem (root.connection.voltageL1, "V") + + " " + EnhFmt.formatVBusItem (root.connection.currentL1, "A") + visible: phaseCount >= 2 + font.pixelSize: 11 + } + TileText { + text: + { + if (root.connection.l2AndL1OutSummed) + return "L2 included in L1" + else + { + return "L2:" + EnhFmt.formatVBusItem (root.connection.powerL2, "W") + + " " + EnhFmt.formatVBusItem (root.connection.voltageL2, "V") + + " " + EnhFmt.formatVBusItem (root.connection.currentL2, "A") + } + } + visible: phaseCount >= 2 + font.pixelSize: 11 + } + TileText { + text: + { + if (phaseCount >= 3) + return "L3: " + EnhFmt.formatVBusItem (root.connection.powerL3, "W") + + " " + EnhFmt.formatVBusItem (root.connection.voltageL3, "V") + + " " + EnhFmt.formatVBusItem (root.connection.currentL3, "A") + else + return " " + } + visible: phaseCount >= 2 + font.pixelSize: 11 + } + TileText { + text: EnhFmt.formatVBusItem (root.connection.frequency, "Hz") + font.pixelSize: phaseCount >= 2 ? 11 : 15 + } + TileText { + text: qsTr("Limit: ") + EnhFmt.formatVBusItem (root.connection.inCurrentLimit) + font.pixelSize: phaseCount >= 2 ? 11 : 15 + visible: root.connection == sys.acInput + } + } +} diff --git a/FileSets/v3.60~25/OverviewAcValuesEnhanced.qml.orig b/FileSets/v3.60~25/OverviewAcValuesEnhanced.qml.orig new file mode 100644 index 00000000..ddabc93a --- /dev/null +++ b/FileSets/v3.60~25/OverviewAcValuesEnhanced.qml.orig @@ -0,0 +1,42 @@ +import QtQuick 2 + +Item { + id: root + width: parent.width + + // NOTE: data is taken by qml, hence it is called connection + property variant connection + + Column { + y: 0 + + width: parent.width + spacing: 0 + + TileText { + text: root.connection ? root.connection.power.format(0) : "" + font.pixelSize: 25 + height: 27 + } + + TileText { + text: root.connection ? "L1: " + root.connection.powerL1.format(0) : "" + visible: root.connection !== undefined && root.connection.phaseCount.valid && root.connection.phaseCount.value > 1 && !root.connection.l2AndL1OutSummed + } + + TileText { + text: "L1 + L2" + visible: root.connection !== undefined && root.connection.phaseCount.valid && root.connection.phaseCount.value > 1 && root.connection.l2AndL1OutSummed + } + + TileText { + text: root.connection ? "L2: " + (!root.connection.splitPhaseL2PassthruDisabled ? root.connection.powerL2.format(0) : "--") : "" + visible: root.connection !== undefined && root.connection.phaseCount.valid && root.connection.phaseCount.value >= 2 && !root.connection.l2AndL1OutSummed + } + + TileText { + text: root.connection ? "L3: " + root.connection.powerL3.format(0) : "" + visible: root.connection !== undefined && root.connection.phaseCount.valid && root.connection.phaseCount.value >= 3 + } + } +} diff --git a/FileSets/v3.60~25/OverviewFlowComplex.qml b/FileSets/v3.60~25/OverviewFlowComplex.qml new file mode 100644 index 00000000..667764ee --- /dev/null +++ b/FileSets/v3.60~25/OverviewFlowComplex.qml @@ -0,0 +1,1549 @@ +///// Enhanced DC Coupled / AC Coupled Overview for GuiMods + +import QtQuick 2 +import "utils.js" as Utils +import com.victron.velib 1.0 +import "timeToGo.js" as TTG +import "enhancedFormat.js" as EnhFmt + +OverviewPage { + id: root + +////// GuiMods — DarkMode + property VBusItem darkModeItem: VBusItem { bind: "com.victronenergy.settings/Settings/GuiMods/DarkMode" } + property bool darkMode: darkModeItem.valid && darkModeItem.value == 1 + + VBusItem { id: flowOverviewItem; bind: Utils.path(settingsPrefix, "/Settings/GuiMods/FlowOverview") } + property bool dcCoupled: flowOverviewItem.valid && flowOverviewItem.value == 2 + + VBusItem { id: showInactiveTilesItem; bind: Utils.path(guiModsPrefix, "/ShowInactiveFlowTiles") } + property real disabledTileOpacity: (showInactiveTiles && showInactiveTilesItem.value === 1) ? 0.3 : 1 + property bool showInactiveTiles: showInactiveTilesItem.valid && showInactiveTilesItem.value >= 1 + property bool showInactiveFlow: showInactiveTilesItem.valid && showInactiveTilesItem.value == 3 + + property variant sys: theSystem + property string systemPrefix: "com.victronenergy.system" + property string settingsPrefix: "com.victronenergy.settings" + property color detailColor: "#b3b3b3" + property real laneWidth: (root.width - inOutTileWidth * 2 - battery.width) / 3 + + property int inOutTileHeight: (root.height - topOffset - bottomOffset - 3 * 5) / 4 + property int inOutTileWidth: 145 + VBusItem { id: timeToGo; bind: Utils.path(systemPrefix, "/Dc/Battery/TimeToGo") } + + VBusItem { id: vebusService; bind: Utils.path(systemPrefix, "/VebusService") } + property bool isMulti: vebusService.valid + property string veDirectInverterService: "" + property string inverterService: vebusService.valid ? vebusService.value : veDirectInverterService + + property bool combineAcLoads: dcCoupled || _combineAcLoads.valid && _combineAcLoads.value === 1 + property variant outputLoad: combineAcLoads ? sys.acLoad : sys.acOutLoad + + // for debug, ignore validity checks so all tiles and their flow lines will show + property bool showAllTiles: showInactiveTilesItem.valid && showInactiveTilesItem.value == 3 + + property bool hasInverter: false + property bool showInverter: hasInverter || inverterService != "" || showAllTiles + + property bool showLoadsOnOutput: showInverter || outputLoad.power.valid + property bool showAcInput: isMulti || sys.acInput.power.valid || showAllTiles + property bool hasLoadsOnInput: showAcInput && ! combineAcLoads && (! loadsOnInputItem.valid || loadsOnInputItem.value === 1) + property bool showLoadsOnInput: !dcCoupled && hasLoadsOnInput + property bool hasPvOnInput: sys.pvOnGrid.power.valid + property bool showPvOnInput: (!dcCoupled || !hasAcCharger) && hasPvOnInput + property bool hasPvOnOutput: sys.pvOnAcOut.power.valid + property bool showPvOnOutput: (!dcCoupled || !hasFuelCell) && hasPvOnOutput + property bool showPvCharger: sys.pvCharger.power.valid + property bool showDcSystem: (hasDcSystemItem.valid && hasDcSystemItem.value > 0) || showAllTiles + property bool showAlternator: (dcCoupled || !hasLoadsOnInput) && sys.alternator.power.valid + property bool hasFuelCell: sys.fuelCell.power.valid + property bool showFuelCell: (dcCoupled || !hasPvOnOutput) && hasFuelCell + property bool showWindGen: sys.windGenerator.power.valid + property bool hasAcCharger: sys.acCharger != undefined && sys.acCharger.power.valid + property bool showAcCharger: (dcCoupled || !hasPvOnInput) && hasAcCharger + + VBusItem { id: motorDrivePowerItem; bind: Utils.path(systemPrefix, "/Dc/MotorDrive/Power") } + property bool showMotorDrive: (dcCoupled || !hasLoadsOnInput) && ! showAlternator && motorDrivePowerItem.valid + + property int bottomOffset: showTanksTemps ? 45 : 5 + property int topOffset: showTanksTemps ? 1 : 5 + property string settingsBindPreffix: "com.victronenergy.settings" + property string pumpBindPreffix: "com.victronenergy.pump.startstop0" + property int numberOfTemps: 0 + + property int tankCount: showTanksEnable ? tankModel.rowCount : 0 + property int tempCount: showTempsEnable ? numberOfTemps : 0 + property int tankTempCount: tankCount + tempCount + property bool showTanks: showTanksEnable ? showStatusBar ? false : tankCount > 0 ? true : false : false + property bool showTemps: showTempsEnable ? showStatusBar ? false : tempCount > 0 ? true : false : false + property bool showTanksTemps: showTanks || showTemps + property int compactThreshold: 45 // height below this will be compacted vertically + property int batteryHeight: 91 + property bool compact: showTanks && showTemps && tankTempCount > 4 + property int tanksHeight: compact ? 22 : 45 + + VBusItem { id: ignoreAcInput1; bind: Utils.path(inverterService, "/Ac/State/IgnoreAcIn1") } + VBusItem { id: ignoreAcInput2; bind: Utils.path(inverterService, "/Ac/State/IgnoreAcIn2") } + VBusItem { id: acActiveInput; bind: Utils.path(inverterService, "/Ac/ActiveIn/ActiveInput") } + + property string guiModsPrefix: "com.victronenergy.settings/Settings/GuiMods" + VBusItem { id: showGaugesItem; bind: Utils.path(guiModsPrefix, "/ShowGauges") } + property bool showGauges: showGaugesItem.valid ? showGaugesItem.value === 1 ? true : false : false + VBusItem { id: showTanksItem; bind: Utils.path(guiModsPrefix, "/ShowEnhancedFlowOverviewTanks") } + property bool showTanksEnable: showTanksItem.valid ? showTanksItem.value === 1 ? true : false : false + VBusItem { id: showTempsItem; bind: Utils.path(guiModsPrefix, "/ShowEnhancedFlowOverviewTemps") } + property bool showTempsEnable: showTempsItem.valid ? showTempsItem.value === 1 ? true : false : false + + VBusItem { id: hasDcSystemItem; bind: "com.victronenergy.settings/Settings/SystemSetup/HasDcSystem" } + + VBusItem { id: timeFormatItem; bind: Utils.path(guiModsPrefix, "/TimeFormat") } + property string timeFormat: getTimeFormat () + + property double acInputFlow: showAcInput ? noNoise (sys.acInput.power) : 0 + property VBusItem vebusAcPower: VBusItem { bind: [sys.vebusPrefix, "/Ac/ActiveIn/P"] } + property double multiAcInputFlow: isMulti ? -noNoise (vebusAcPower) : 0 + property double pvOnInputFlow: showPvOnInput ? noNoise (sys.pvOnGrid.power) : 0 + property double loadsOnInputFlow: sys.acInLoad.power.valid ? -noNoise (sys.acInLoad.power) : 0 + property double pvInverterOnAcOutFlow: showPvOnOutput && sys.pvOnAcOut.power.valid ? noNoise (sys.pvOnAcOut.power) : 0 + property double acOutLoadFlow: sys.acOutLoad.power.valid ? -noNoise (sys.acOutLoad.power) : 0 + + property double pvChargerFlow: showPvCharger ? noNoise (sys.pvCharger.power) : 0 + property double dcSystemFlow: showDcSystem ? -noNoise (sys.dcSystem.power) : 0 + property double alternatorFlow: showAlternator ? noNoise (sys.alternator.power) : 0 + property double motorDriveFlow: showMotorDrive ? noNoise (motorDrivePowerItem) : 0 + property double inverterDcFlow: showInverter ? noNoise (sys.inverterChargerDc.power) : 0 + property double batteryFlow: noNoise (sys.battery.power) + property double windGenFlow: noNoise (sys.windGenerator.power) + property double acChargerFlow: noNoise (sys.acCharger.power) + property double fuelCellFlow: noNoise (sys.fuelCell.power) + + VBusItem { id: showBatteryTempItem; bind: Utils.path(guiModsPrefix, "/ShowBatteryTempOnFlows") } + property bool showBatteryTemp: showBatteryTempItem.valid && showBatteryTempItem.value == 1 + + + function getTimeFormat () + { + if (!timeFormatItem.valid || timeFormatItem.value === 0) + return "" + else if (timeFormatItem.value === 2) + return "h:mm ap" + else + return "hh:mm" + } + + //Component.onCompleted: { discoverServices(); showHelp () } + onActiveChanged: + { + if (root.active) + { + discoverServices() + showHelp () + } + } + + title: dcCoupled ? qsTr("DC Coupled overview") : qsTr("AC Coupled overview") + + VBusItem { id: loadsOnInputItem; bind: "com.victronenergy.settings/Settings/GuiMods/ShowEnhancedFlowLoadsOnInput" } + VBusItem { id: _combineAcLoads; bind: "com.victronenergy.settings/Settings/GuiMods/EnhancedFlowCombineLoads" } + + OverviewBox { + id: acInBox + opacity: showAcInput ? 1 : disabledTileOpacity + visible: showAcInput || showInactiveTiles + width: inOutTileWidth + height: inOutTileHeight + title: + { + // input 1 is active + if (acActiveInput.value == 0) + { + if (ignoreAcInput1.valid && ignoreAcInput1.value == 1) + return qsTr ("AC In 1 Ignored") + else + return getAcSourceName(sys.acSource) + } + // input 2 is active + else if (acActiveInput.value == 1) + { + if (ignoreAcInput2.valid && ignoreAcInput2.value == 1) + return qsTr ("AC In 2 Ignored") + else + return getAcSourceName(sys.acSource) + } + else + return "no input" + } +////// GuiMods — DarkMode + titleColor: !darkMode ? "#E74c3c" : "#73261E" + color: !darkMode ? "#C0392B" : "#601C15" + anchors { + top: root.top; topMargin: topOffset + left: parent.left; leftMargin: 5 + } + values: TileText { + y: 13 + text: EnhFmt.formatVBusItem (sys.acInput.power) + font.pixelSize: 17 + visible: showAcInput + } + + MbIcon { + iconId: getAcSourceIcon(sys.acSource) + anchors { + bottom: parent.bottom + left: parent.left; leftMargin: 2 + } + opacity: 0.5 + } + PowerGauge + { + id: acInGauge + width: parent.width + height: 15 + anchors + { + top: parent.top; topMargin: 18 + horizontalCenter: parent.horizontalCenter + } + connection: sys.acInput + useInputCurrentLimit: true + maxForwardPowerParameter: "" + maxReversePowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/MaxFeedInPower" + visible: showGauges && showAcInput + } + DetailTarget { id: acInputTarget; detailsPage: "DetailAcInput.qml" } + } + + OverviewBox + { + id: pvInverterOnInput +////// GuiMods — DarkMode + titleColor: !darkMode ? "#F4B350" : "#7A5928" + color: !darkMode ? "#F39C12" : "#794E09" + title: qsTr("PV on Input") + width: inOutTileWidth + height: inOutTileHeight + visible: showPvOnInput || (showInactiveTiles && !dcCoupled) + opacity: showPvOnInput ? 1 : disabledTileOpacity + MbIcon + { + source: + { + var ids = sys.pvInvertersProductIds.text + if (ids.indexOf(0xA142) > -1) + return "image://theme/overview-fronius-logo" + return "" + } + visible: showPvOnInput + opacity: 0.3 + anchors { + bottom: parent.bottom + left: parent.left + margins: 2 + } + } + values: TileText { + y: 11 + text: EnhFmt.formatVBusItem (sys.pvOnGrid.power) + font.pixelSize: 17 + visible: showPvOnInput + } + anchors { + top: acInBox.bottom + topMargin: 5 + left: acInBox.left + } + PowerGauge + { + id: pvInverterOnInputGauge + width: parent.width + height: 15 + anchors + { + top: parent.top; topMargin: 18 + horizontalCenter: parent.horizontalCenter + } + connection: sys.pvOnGrid + maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/PvOnGridMaxPower" + visible: showGauges && showPvOnInput + } + DetailTarget { id: pvOnInputTarget; detailsPage: "DetailPvInverter.qml" } + } + + OverviewBox { + id: acLoadOnInputBox + title: qsTr("AC In Loads") +////// GuiMods — DarkMode + color: !darkMode ? "#27AE60" : "#135730" + titleColor: !darkMode ? "#2ECC71" : "#176638" + width: inOutTileWidth + height: inOutTileHeight + opacity: showLoadsOnInput ? 1 : disabledTileOpacity + visible: showLoadsOnInput || (showInactiveTiles && !dcCoupled) + anchors { + top: pvInverterOnInput.bottom + topMargin: 5 + left: acInBox.left + } + values: TileText { + y: 13 + text: EnhFmt.formatVBusItem (sys.acInLoad.power) + font.pixelSize: 17 + visible: showLoadsOnInput + } + PowerGauge + { + id: acInLoadGauge + width: parent.width + height: 15 + anchors + { + top: parent.top; topMargin: 18 + horizontalCenter: parent.horizontalCenter + } + connection: sys.acInLoad + maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/AcOutputNonCriticalMaxPower" + visible: showGauges && showLoadsOnInput + } + DetailTarget { id: acLoadsOnInputTarget; detailsPage: "DetailLoadsOnInput.qml" } + } + + // check inverter to see if AC out 2 exists and hide noncritical loads if so + VBusItem { id: inverterOut2Item; bind: Utils.path(root.inverterService, "/Ac/Out/L2/V") } + + OverviewBox { + id: acOutputBox + title: combineAcLoads ? qsTr ("AC Loads") : qsTr ("AC Out Loads") +////// GuiMods — DarkMode + color: !darkMode ? "#27AE60" : "#135730" + titleColor: !darkMode ? "#2ECC71" : "#176638" + height: inOutTileHeight + width: inOutTileWidth + opacity: showLoadsOnOutput ? 1 : disabledTileOpacity + visible: showLoadsOnOutput || showInactiveTiles + anchors { + right: root.right; rightMargin: 5 + top: root.top; topMargin: topOffset + } + + values: TileText { + y: 13 + text: EnhFmt.formatVBusItem (outputLoad.power) + font.pixelSize: 17 + visible: showLoadsOnOutput + } + PowerGauge + { + id: acOutLoadGauge + width: parent.width + height: 15 + anchors + { + top: parent.top; topMargin: 18 + horizontalCenter: parent.horizontalCenter + } + connection: outputLoad + maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/AcOutputMaxPower" + visible: showGauges && showLoadsOnOutput + } + DetailTarget { id: acLoadsOnOutputTarget; detailsPage: "DetailLoadsOnOutput.qml" } + } + Timer { + id: wallClock + running: timeFormat != "" + repeat: true + + interval: 1000 + triggeredOnStart: true + onTriggered: time = Qt.formatDateTime(new Date(), timeFormat) + + property string time + } + + MultiEnhancedGP { + id: multi + iconId: "overview-inverter-short" + opacity: showInverter ? 1 : disabledTileOpacity + visible: showInverter || showInactiveTiles + anchors { + horizontalCenter: parent.horizontalCenter + top: acInBox.top + } + inverterService: root.inverterService + PowerGaugeMulti + { + id: multiGauge + width: multi.width + height: 13 + anchors + { + top: parent.top; topMargin: 21 + horizontalCenter: multi.horizontalCenter + } + inverterService: root.inverterService + visible: showGauges && showInverter + } + DetailTarget { id: multiTarget; detailsPage: "DetailInverter.qml"; width: 60; height: 60 } + } + TileText + { + text: wallClock.time + color: showInverter || darkMode ? "white" : "black" + width: inOutTileWidth + wrapMode: Text.WordWrap + font.pixelSize: 16 + anchors + { + bottom: multi.bottom; bottomMargin: 1 + horizontalCenter: multi.horizontalCenter; + horizontalCenterOffset: multiDcConnector.active ? -10 : 0 + } + visible: wallClock.running + } + + Battery { + id: battery + width: 145 + height: 96 + anchors { + bottom: parent.bottom; bottomMargin: bottomOffset; + right: acOutputBox.left; rightMargin: laneWidth + } + soc: sys.battery.soc.valid ? sys.battery.soc.value : 0 +////// add battery current bar graph + PowerGaugeBattery + { + id: batteryBar + width: parent.width + height: 10 + anchors + { + top: parent.top; topMargin: 52 + horizontalCenter: parent.horizontalCenter + } + visible: showGauges + } + values: Column { + width: parent.width + + TileText { + text: sys.battery.soc.value === undefined ? "--" : sys.battery.soc.format (0) + font.pixelSize: 25 + } + TileText { + text: EnhFmt.formatVBusItem (sys.battery.power, "W") + } + TileText { + text: " " + font.pixelSize: 6 + } + TileText { + text: EnhFmt.formatVBusItem (sys.battery.voltage, "V ", 2) + + EnhFmt.formatVBusItem (sys.battery.current, "A") + } + TileText { + text: timeToGo.valid ? qsTr ("Remain: ") + TTG.formatTimeToGo (timeToGo) : qsTr (" ") + } + } + DetailTarget { id: batteryTarget; detailsPage: "DetailBattery.qml" } + } + + OverviewBox + { + id: pvInverterOnAcOut +////// GuiMods — DarkMode + titleColor: !darkMode ? "#F4B350" : "#7A5928" + color: !darkMode ? "#F39C12" : "#794E09" + title: qsTr("PV on Output") + width: inOutTileWidth + height: inOutTileHeight + opacity: showPvOnOutput ? 1 : disabledTileOpacity + visible: showPvOnOutput || (showInactiveTiles && !dcCoupled) + MbIcon + { + source: + { + var ids = sys.pvInvertersProductIds.text + if (ids.indexOf(0xA142) > -1) + return "image://theme/overview-fronius-logo" + return "" + } + visible: showPvOnOutput + opacity: 0.3 + anchors { + bottom: parent.bottom + right: parent.right + margins: 2 + } + } + + values: TileText { + y: 11 + text: EnhFmt.formatVBusItem (sys.pvOnAcOut.power) + font.pixelSize: 17 + visible: showPvOnOutput + } + anchors { + top: acOutputBox.bottom + topMargin: 5 + right: acOutputBox.right + } + PowerGauge + { + id: pvInverterOnAcOutGauge + width: parent.width + height: 15 + anchors + { + top: parent.top; topMargin: 18 + horizontalCenter: parent.horizontalCenter + } + connection: sys.pvOnAcOut + maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/PvOnOutputMaxPower" + visible: showGauges && showPvOnOutput + } + DetailTarget { id: pvOnOutputTarget; detailsPage: "DetailPvInverter.qml" } + } + + OverviewBox + { + id: acChargerBox + title: qsTr ("AC Charger") +////// GuiMods — DarkMode + color: !darkMode ? "#157894" : "#0a3c4a" + titleColor: !darkMode ? "#419FB9" : "#204f5c" + height: inOutTileHeight + width: inOutTileWidth + opacity: showAcCharger ? 1 : disabledTileOpacity + visible: showAcCharger || (showInactiveTiles && dcCoupled) + anchors + { + left: root.left; leftMargin: 5 + bottom: alternatorBox.top; bottomMargin: 5 + } + values: TileText { + text: EnhFmt.formatVBusItem (sys.acCharger.power) + font.pixelSize: 17 + visible: showAcCharger + anchors + { + bottom: parent.bottom; bottomMargin: 0 + horizontalCenter: parent.horizontalCenter + } + } + PowerGauge + { + id: acChargerGauge + width: parent.width + height: 10 + anchors + { + top: parent.top; topMargin: 20 + horizontalCenter: parent.horizontalCenter + } + connection: sys.acCharger + reversePower: true + maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/MaxAcChargerPower" + visible: showGauges && showAcCharger + } + DetailTarget { id: acChargerTarget; detailsPage: "DetailAcCharger.qml" } + } + + OverviewBox + { + id: alternatorBox + title: qsTr ("Alternator") +////// GuiMods — DarkMode + color: !darkMode ? "#157894" : "#0a3c4a" + titleColor: !darkMode ? "#419FB9" : "#204f5c" + height: inOutTileHeight + width: inOutTileWidth + opacity: showAlternator ? 1 : disabledTileOpacity + visible: showAlternator || (showInactiveTiles && dcCoupled) + anchors + { + left: root.left; leftMargin: 5 + bottom: pvChargerBox.top; bottomMargin: 5 + } + values: TileText { + text: EnhFmt.formatVBusItem (sys.alternator.power) + font.pixelSize: 17 + visible: showAlternator + anchors + { + bottom: parent.bottom; bottomMargin: 0 + horizontalCenter: parent.horizontalCenter + } + } + PowerGauge + { + id: alternatorGauge + width: parent.width + height: 10 + anchors + { + top: parent.top; topMargin: 20 + horizontalCenter: parent.horizontalCenter + } + connection: sys.alternator + maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/MaxAlternatorPower" + visible: showGauges && showAlternator + } + DetailTarget { id: alternatorTarget; detailsPage: "DetailAlternator.qml" } + } + + OverviewBox + { + id: motorDriveBox + title: qsTr ("Motor Drive") +////// GuiMods — DarkMode + color: !darkMode ? "#157894" : "#0a3c4a" + titleColor: !darkMode ? "#419FB9" : "#204f5c" + height: inOutTileHeight + width: inOutTileWidth + opacity: showMotorDrive ? 1 : disabledTileOpacity + visible: showMotorDrive + anchors + { + left: root.left; leftMargin: 5 + bottom: pvChargerBox.top; bottomMargin: 5 + } + values: TileText { + text: EnhFmt.formatVBusItem (motorDrivePowerItem) + font.pixelSize: 17 + visible: showMotorDrive + anchors + { + bottom: parent.bottom; bottomMargin: 0 + horizontalCenter: parent.horizontalCenter + } + } + PowerGauge + { + id: motorDriveGauge + width: parent.width + height: 10 + anchors + { + top: parent.top; topMargin: 20 + horizontalCenter: parent.horizontalCenter + } + connection: motorDrivePowerItem + maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/MaxMotorDriveLoad" + maxReversePowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/MaxMotorDriveCharge" + visible: showGauges && showMotorDrive + showLabels: true + } + DetailTarget { id: motorDriveTarget; detailsPage: "DetailMotorDrive.qml" } + } + + VBusItem { id: dcSystemNameItem; bind: Utils.path(settingsPrefix, "/Settings/GuiMods/CustomDcSystemName") } + + OverviewBox { + id: dcSystemBox + width: inOutTileWidth + height: inOutTileHeight + opacity: showDcSystem ? 1 : disabledTileOpacity + visible: showDcSystem || showInactiveTiles + title: dcSystemNameItem.valid && dcSystemNameItem.value != "" ? dcSystemNameItem.value : qsTr ("DC System") + anchors + { + right: root.right; rightMargin: 5 + bottom: parent.bottom + bottomMargin: bottomOffset + } + values: TileText { + text: EnhFmt.formatVBusItem (sys.dcSystem.power) + font.pixelSize: 17 + visible: showDcSystem + anchors + { + bottom: parent.bottom; bottomMargin: 0 + horizontalCenter: parent.horizontalCenter + } + } + PowerGauge + { + id: dcSystemGauge + width: parent.width + height: 10 + anchors + { + top: parent.top; topMargin: 20 + horizontalCenter: parent.horizontalCenter + } + connection: sys.dcSystem + maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/DcSystemMaxLoad" + maxReversePowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/DcSystemMaxCharge" + showLabels: true + visible: showGauges && showDcSystem + } + DetailTarget { id: dcSystemTarget; detailsPage: "DetailDcSystem.qml" } + } + + OverviewBox { + id: fuelCellBox +////// GuiMods — DarkMode + color: !darkMode ? "#157894" : "#0a3c4a" + titleColor: !darkMode ? "#419FB9" : "#204f5c" + width: inOutTileWidth + height: inOutTileHeight + opacity: showFuelCell ? 1 : disabledTileOpacity + visible: showFuelCell || (showInactiveTiles && dcCoupled) + title: qsTr ("Fuel Cell") + anchors { + left: windGenBox.left + bottom: windGenBox.top; bottomMargin: 5 + } + values: TileText { + text: EnhFmt.formatVBusItem (sys.fuelCell.power) + font.pixelSize: 17 + visible: fuelCellBox.visible + anchors + { + bottom: parent.bottom; bottomMargin: 0 + horizontalCenter: parent.horizontalCenter + } + } + PowerGauge + { + id: fuelCellGauge + width: parent.width + height: 10 + anchors + { + top: parent.top; topMargin: 20 + horizontalCenter: parent.horizontalCenter + } + connection: sys.fuelCell + maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/MaxFuelCellPower" + visible: showGauges && fuelCellBox.visible + } + DetailTarget { id: fuelCellTarget; detailsPage: "DetailFuelCell.qml" } + } + + OverviewBox { + id: windGenBox +////// GuiMods — DarkMode + color: !darkMode ? "#157894" : "#0a3c4a" + titleColor: !darkMode ? "#419FB9" : "#204f5c" + width: inOutTileWidth + height: inOutTileHeight + opacity: showWindGen ? 1 : disabledTileOpacity + visible: showWindGen || showInactiveTiles + title: qsTr ("Wind Generator") + anchors + { + right: dcSystemBox.right + bottom: dcSystemBox.top; bottomMargin: 5 + } + values: TileText { + text: EnhFmt.formatVBusItem (sys.windGenerator.power) + font.pixelSize: 17 + visible: showWindGen + anchors + { + bottom: parent.bottom; bottomMargin: 0 + horizontalCenter: parent.horizontalCenter + } + } + PowerGauge + { + id: windGenGauge + width: parent.width + height: 10 + anchors + { + top: parent.top; topMargin: 20 + horizontalCenter: parent.horizontalCenter + } + connection: sys.windGenerator + maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/MaxWindGenPower" + visible: showGauges && showWindGen + } + DetailTarget { id: windGenTarget; detailsPage: "DetailWindGen.qml" } + } + + OverviewBox { + id: pvChargerBox + title: qsTr("PV Charger") +////// GuiMods — DarkMode + titleColor: !darkMode ? "#F4B350" : "#7A5928" + color: !darkMode ? "#F39C12" : "#794E09" + width: inOutTileWidth + height: inOutTileHeight + opacity: showPvCharger ? 1 : disabledTileOpacity + visible: showPvCharger || showInactiveTiles + anchors + { + left: root.left; leftMargin: 5 + bottom: parent.bottom; bottomMargin: bottomOffset + } + values: TileText { + y: 12 + text: EnhFmt.formatVBusItem (sys.pvCharger.power) + font.pixelSize: 17 + } + // moved sun icon here from OverviewSolarChager so it can be put below text, etc + MbIcon { + iconId: "overview-sun" + anchors { + bottom: parent.bottom + right: parent.right; rightMargin: 2 + } + opacity: 0.5 + } + + PowerGauge + { + id: pvChargerGauge + width: parent.width + height: 10 + anchors + { + top: parent.top; topMargin: 20 + horizontalCenter: parent.horizontalCenter + } + connection: sys.pvCharger + maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/PvChargerMaxPower" + visible: showGauges && showPvCharger + } + DetailTarget { id: pvChargerTarget; detailsPage: "DetailPvCharger.qml" } + } + + // move ESS reason to Battery details page + + // invisible item to connection all AC input connections to.. + Item { + id: acInBus + width: laneWidth + anchors { + left: acInBox.right; + top: multi.top; topMargin: multi.height / 2 + 10 + bottom: pvInverterOnInput.bottom; bottomMargin: 8 + } + } + Item { + id: dcLaneLeft + width: laneWidth + anchors { + right: battery.left; + top: multi.top; topMargin: multi.height / 2 + 10 + bottom: dcSystemBox.bottom; bottomMargin: 8 + } + } + Item { + id: dcLaneRight + width: laneWidth * 0.8 + anchors { + left: battery.right; + + top: dcLaneLeft.top + bottom: dcLaneLeft.bottom + } + } + Item { + id: dcLaneTop + anchors { + left: battery.left + right: battery.right + top: multi.bottom; + bottom: battery.top + } + } + + OverviewConnection { + id: multiAcInFlow + ballCount: 1 + path: straight + active: root.active && ( showAcInput || showPvOnInput || showLoadsOnInput ) + value: -Utils.sign (multiAcInputFlow) + startPointVisible: false + endPointVisible: true + + anchors { + left: acInBus.horizontalCenter; leftMargin: -0.5 + right: multi.left; rightMargin: -8 + bottom: acInBus.top + } + } + + // AC source power flow + OverviewConnection { + id: acSource + ballCount: 1 + path: corner + active: root.active && showAcInput + value: Utils.sign (acInputFlow) + startPointVisible: true + endPointVisible: false + + anchors { + left: acInBox.right; leftMargin: -8 + right: acInBus.horizontalCenter + top: acInBox.bottom; topMargin: -8 + bottom: acInBus.top + } + } + + // Coupled AC sources + OverviewConnection { + id: coupledAcConnection + ballCount: 1 + path: straight + active: root.active && ((showLoadsOnInput && showPvOnInput) || (!dcCoupled && showInactiveFlow)) + value: -Utils.sign (pvOnInputFlow + loadsOnInputFlow) + startPointVisible: false + endPointVisible: false + + anchors { + right: acInBus.horizontalCenter + rightMargin: 0.5 // makes this line up with others + top: acInBus.top + bottom: acInBus.bottom + } + } + + // Grid inverter power flow + OverviewConnection { + id: pvInverterOnInputConnection + ballCount: showLoadsOnInput ? 1 : 2 + path: showLoadsOnInput || (!dcCoupled && showInactiveFlow) ? straight : corner + active: root.active && (showPvOnInput || (!dcCoupled && showInactiveFlow)) + value: Utils.sign (pvOnInputFlow) + startPointVisible: true + endPointVisible: false + + anchors { + left: pvInverterOnInput.right; leftMargin: -8 + right: acInBus.horizontalCenter + top: pvInverterOnInput.bottom; topMargin: -8 + bottom: showLoadsOnInput || (!dcCoupled && showInactiveFlow) ? pvInverterOnInput.bottom : multiAcInFlow.verticalCenter + bottomMargin: showLoadsOnInput || (!dcCoupled && showInactiveFlow) ? 8 : 0 + } + } + + // power to loads on input + OverviewConnection { + id: loadsOnInput + ballCount: 1 + path: corner + active: root.active && (showLoadsOnInput || (!dcCoupled && showInactiveFlow)) + value: Utils.sign (loadsOnInputFlow) + startPointVisible: true + endPointVisible: false + + anchors { + left: acLoadOnInputBox.right; leftMargin: -8 + right: acInBus.horizontalCenter + rightMargin: 0.5 // makes this line up with others + top: acLoadOnInputBox.top; topMargin: 8 + bottom: showPvOnInput|| (!dcCoupled && showInactiveFlow) ? acInBus.bottom : acInBus.top + } + } + + // invisible item to connection all AC output connections to.. + Item { + id: acOutNode + height: 6 + anchors { + left: multi.right + right: acOutputBox.left + verticalCenter: acInBus.top + } + } + + // AC out connection + OverviewConnection { + id: multiAcOutConnection + + ballCount: 1 + path: straight + active: root.active && ((showLoadsOnOutput || showPvOnOutput) || (!dcCoupled && showInactiveFlow)) + value: -Utils.sign (acOutLoadFlow + pvInverterOnAcOutFlow) + endPointVisible: false + + anchors { + left: multi.right; leftMargin: -8 + right: acOutNode.horizontalCenter + top: acOutNode.verticalCenter + } + } + + // loads on output conenction + OverviewConnection { + id: acOutBoxConnection + + ballCount: 1 + path: corner + active: root.active && (showLoadsOnOutput || (!dcCoupled && showInactiveFlow)) + value: Utils.sign (acOutLoadFlow) + startPointVisible: true + endPointVisible: false + + anchors { + right: acOutNode.horizontalCenter + rightMargin: -0.5 // makes this line up with others + left: acOutputBox.left; leftMargin: 8 + top: acOutputBox.bottom; topMargin: -8 + bottom: acOutNode.verticalCenter + } + } + + // PV Inverter on AC out connection + OverviewConnection { + id: pvOnAcOutConnection + + ballCount: 2 + path: corner + active: root.active && (showPvOnOutput || (!dcCoupled && showInactiveFlow)) + value: Utils.sign (pvInverterOnAcOutFlow) + startPointVisible: true + endPointVisible: false + + anchors { + left: pvInverterOnAcOut.left; leftMargin: 8 + top: pvInverterOnAcOut.bottom; topMargin: -8 + right: acOutNode.horizontalCenter + bottom: acOutNode.verticalCenter + } + } + + // invisible summing point for all DC connections + Item { + id: dcNode + height: 10 + width: 10 + anchors { + horizontalCenter: batteryDcConnector.horizontalCenter + verticalCenter: dcLaneTop.verticalCenter + } + } + + // DC bus segments + OverviewConnection { + id: dcBus1 + ballCount: 1 + path: straight + active: root.active && ((showAlternator || showMotorDrive || showPvCharger) || (dcCoupled && showInactiveFlow)) + value: -Utils.sign (alternatorFlow + motorDriveFlow + pvChargerFlow) + startPointVisible: false + endPointVisible: false + + anchors { + right: dcLaneLeft.horizontalCenter + rightMargin: 0.5 // makes this line up with others + bottom: alternatorConnection.verticalCenter + top: dcLaneTop.verticalCenter + } + } + OverviewConnection { + id: dcBus2 + ballCount: 1 + path: straight + active: root.active && ((showAlternator || showMotorDrive || showAcCharger || showPvCharger) || (dcCoupled && showInactiveFlow)) + value: Utils.sign (alternatorFlow + motorDriveFlow + pvChargerFlow + acChargerFlow) + startPointVisible: false + endPointVisible: false + + anchors { + left: dcLaneLeft.horizontalCenter + right: dcBus3.left + bottom: dcLaneTop.verticalCenter + } + } + OverviewConnection { + id: dcBus3 + ballCount: 2 + path: straight + active: root.active && ((showInverter || showFuelCell || showWindGen || showDcSystem) || showInactiveFlow) + value: -Utils.sign (inverterDcFlow + fuelCellFlow + windGenFlow + dcSystemFlow) + startPointVisible: false + endPointVisible: false + + anchors { + left: batteryDcConnector.horizontalCenter + right: multiDcConnector.horizontalCenter + bottom: dcLaneTop.verticalCenter + } + } + OverviewConnection { + id: dcBus4 + ballCount: 1 + path: straight + active: root.active && ((showFuelCell || showWindGen || showDcSystem) || showInactiveFlow) + value: -Utils.sign (fuelCellFlow + windGenFlow + dcSystemFlow) + startPointVisible: false + endPointVisible: false + + anchors { + left: multiDcConnector.horizontalCenter + right: dcLaneRight.horizontalCenter + bottom: dcLaneTop.verticalCenter + } + } + OverviewConnection { + id: dcBus5 + ballCount: 1 + path: straight + active: root.active && ((showWindGen || showDcSystem) || showInactiveFlow) + value: -Utils.sign (windGenFlow + dcSystemFlow) + startPointVisible: false + endPointVisible: false + + anchors { + left: dcLaneRight.horizontalCenter + top: dcLaneTop.verticalCenter + bottom: windGenConnection.verticalCenter + } + } + + + // DC connection multi to bus + OverviewConnection { + id: multiDcConnector + ballCount: 1 + path: straight + active: root.active && (showInverter || showInactiveFlow) + value: Utils.sign (inverterDcFlow) + startPointVisible: true + endPointVisible: false + + anchors { + right: multi.right; rightMargin: 25 + top: multi.bottom; topMargin: -8 + bottom: dcLaneTop.verticalCenter + } + } + // DC connection battery to bus + OverviewConnection { + id: batteryDcConnector + ballCount: 1 + path: straight + active: root.active && ((sys.battery.soc.valid || showDcSystem) || (dcCoupled && showInactiveFlow)) + value: -Utils.sign (batteryFlow) + startPointVisible: true + endPointVisible: false + + anchors { + left: battery.left; leftMargin: 30 + top: battery.top; topMargin: 15 + bottom: dcLaneTop.verticalCenter + } + } + + // AC charger to DC bus + OverviewConnection + { + id: acChargerConnection + ballCount: 1 + path: corner + active: root.active && (showAcCharger || (dcCoupled && showInactiveFlow)) + value: Utils.sign (acChargerFlow) + startPointVisible: true + endPointVisible: false + anchors + { + left: acChargerBox.right; leftMargin: -8 + top: acChargerBox.bottom; topMargin: -8 + right: dcLaneLeft.horizontalCenter + bottom: dcLaneTop.verticalCenter + } + } + + // Alternator to bus + OverviewConnection + { + id: alternatorConnection + ballCount: 1 + path: straight + active: root.active && (showAlternator || showMotorDrive || (dcCoupled && showInactiveFlow)) + value: Utils.sign (alternatorFlow + motorDriveFlow) + startPointVisible: true + endPointVisible: false + anchors + { + left: alternatorBox.right; leftMargin: -8 + top: alternatorBox.bottom; topMargin: -8 + right: dcLaneLeft.horizontalCenter + } + } + + // DC system to DC bus + OverviewConnection + { + id: dcSystemConnection + ballCount: 2 + path: corner + active: root.active && (showDcSystem || (dcCoupled && showInactiveFlow)) + value: Utils.sign (dcSystemFlow) + endPointVisible: false + anchors + { + left: dcSystemBox.left; leftMargin: 8 + top: dcSystemBox.bottom; topMargin: -8 + right: dcLaneRight.horizontalCenter + rightMargin: -0.5 // makes this line up with others + bottom: windGenConnection.verticalCenter + } + } + + + // other DC connection to DC right bus + OverviewConnection + { + id: fuelCellConnection + ballCount: 2 + path: corner + active: root.active && (showFuelCell || (dcCoupled && showInactiveFlow)) + value: Utils.sign (fuelCellFlow) + startPointVisible: true + endPointVisible: false + anchors + { + left: fuelCellBox.left; leftMargin: 8 + top: fuelCellBox.bottom; topMargin: -8 + right: dcLaneRight.horizontalCenter + rightMargin: -0.5 // makes this line up with others + bottom: dcLaneTop.verticalCenter + } + } + + // Wind Gen DC right bus + OverviewConnection + { + id: windGenConnection + ballCount: 1 + path: straight + active: root.active && (showWindGen || showInactiveFlow) + value: Utils.sign (windGenFlow) + startPointVisible: true + endPointVisible: false + anchors + { + left: windGenBox.left; leftMargin: 8 + top: windGenBox.bottom; topMargin: -8 + right: dcLaneRight.horizontalCenter + } + } + + // Solar charger to DC right bus + OverviewConnection + { + id: pvChargerConnection + ballCount: 2 + path: corner + active: root.active && (showPvCharger || showInactiveFlow) + value: Utils.sign (pvChargerFlow) + startPointVisible: true + endPointVisible: false + anchors + { + left: pvChargerBox.right; leftMargin: -8 + top: pvChargerBox.bottom; topMargin: -8 + right: dcLaneLeft.horizontalCenter + bottom: alternatorConnection.top + } + } + + // Synchronise tank name text scroll start + Timer + { + id: scrollTimer + interval: 15000 + repeat: true + running: root.active + } + + ListView + { + id: tanksColum + + visible: showTanks + width: compact ? root.width : root.width * tankCount / tankTempCount + property int tileWidth: width / Math.min (count, 5.2) + height: root.tanksHeight + anchors + { + bottom: root.bottom + left: root.left + } + + // flickable list if more than will fit across bottom of screen + interactive: count > 4 ? true : false + orientation: ListView.Horizontal + + model: TankModel { id: tankModel } + delegate: TileTankEnhanced { + // Without an intermediate assignment this will trigger a binding loop warning. + property variant theService: DBusServices.get(buddy.id) + service: theService + width: tanksColum.tileWidth + height: root.tanksHeight + pumpBindPrefix: root.pumpBindPreffix + compact: root.compact + Connections { + target: scrollTimer + onTriggered: doScroll() + } + } + Tile { + title: qsTr("tanks") + anchors.fill: parent + values: TileText { + text: qsTr("") + width: parent.width + wrapMode: Text.WordWrap + } + z: -1 + } + } + + ListView + { + id: tempsColumn + + visible: showTemps + width: compact ? root.width : root.width * tempCount / tankTempCount + property int tileWidth: width / Math.min (count, 5.2) + height: root.tanksHeight + anchors + { + bottom: root.bottom + bottomMargin: compact ? root.tanksHeight : 0 + right: root.right + } + + // make list flickable if more tiles than will fit completely + interactive: count > 4 ? true : false + orientation: ListView.Horizontal + + model: tempsModel + delegate: TileTemp + { + width: tempsColumn.tileWidth + height: tempsColumn.height + compact: root.compact + Connections + { + target: scrollTimer + onTriggered: doScroll() + } + } + Tile + { + title: qsTr("TEMPS") + anchors.fill: parent + values: TileText + { + text: qsTr("") + width: parent.width + wrapMode: Text.WordWrap + } + z: -1 + } + } + ListModel { id: tempsModel } + + // When new service is found check if is a tank sensor + Connections + { + target: DBusServices + onDbusServiceFound: addService(service) + } + + // hack to get value(s) from within a loop inside a function when service is changing + property string tempServiceName: "" + property VBusItem temperatureItem: VBusItem { bind: Utils.path(tempServiceName, "/Dc/0/Temperature") } + + function addService(service) + { + switch (service.type) + { + case DBusService.DBUS_SERVICE_TEMPERATURE_SENSOR: + numberOfTemps++ + tempsModel.append({serviceName: service.name}) + break;; + + case DBusService.DBUS_SERVICE_MULTI: + hasInverter = true + root.tempServiceName = service.name + if (temperatureItem.valid && showBatteryTemp) + { + numberOfTemps++ + tempsModel.append({serviceName: service.name}) + } + break;; + case DBusService.DBUS_SERVICE_MULTI_RS: + hasInverter = true + break;; + case DBusService.DBUS_SERVICE_INVERTER: + hasInverter = true + if (veDirectInverterService == "") + veDirectInverterService = service.name; + break;; + case DBusService.DBUS_SERVICE_BATTERY: + root.tempServiceName = service.name + if (temperatureItem.valid && showBatteryTemp) + { + numberOfTemps++ + tempsModel.append({serviceName: service.name}) + } + break;; + } + } + + // Detect available services of interest + function discoverServices() + { + numberOfTemps = 0 + tempsModel.clear() + veDirectInverterService = "" + hasInverter = false + for (var i = 0; i < DBusServices.count; i++) + { + addService(DBusServices.at(i)) + } + } + VBusItem { id: incomingTankName; + bind: Utils.path(settingsBindPreffix, "/Settings/Devices/TankRepeater/IncomingTankService") } + + // help message shown when menu is first drawn + Rectangle + { + id: helpBox + color: "white" + width: multi.width + height: 32 + opacity: 0.7 + anchors + { + verticalCenter: dcLaneTop.verticalCenter + horizontalCenter: root.horizontalCenter + } + visible: false + } + TileText + { + text: qsTr ( "Tap tile center for detail at any time" ) + color: "black" + anchors.fill: helpBox + wrapMode: Text.WordWrap + font.pixelSize: 12 + visible: helpBox.visible + } + + //// hard key handler + // used to press buttons when touch isn't available + // UP and DOWN buttons cycle through the list of touch areas + // "space" button is used to simulate a touch on the area + // target must be highlighted so that other uses of "space" + // will still occur + + // list of all details touchable areas + property variant targetList: + [ + acInputTarget, pvOnInputTarget, acLoadsOnInputTarget, + acChargerTarget, alternatorTarget, motorDriveTarget, pvChargerTarget, + multiTarget, batteryTarget, + acLoadsOnOutputTarget, pvOnOutputTarget, fuelCellTarget, windGenTarget, dcSystemTarget + ] + + property int selectedTarget: 0 + + Timer + { + id: targetTimer + interval: 5000 + repeat: false + running: false + onTriggered: { hideAllTargets () } + } + + Keys.forwardTo: [keyHandler] + Item + { + id: keyHandler + Keys.onUpPressed: + { + nextTarget (-1) + event.accepted = true + } + + Keys.onDownPressed: + { + nextTarget (+1) + event.accepted = true + } + Keys.onSpacePressed: + { + if (targetTimer.running) + { + var foo // hack to make clicked() work + bar.clicked (foo) + event.accepted = true + } + else + event.accepted = false + } + } + // hack to make clicked() work + property variant bar: targetList[selectedTarget] + + function nextTarget (increment) + { + // make one pass through all possible targets to find an enabled one + // if found, that's the new selectedTarget, + // if not selectedTarget does not change + var newIndex = selectedTarget + for (var i = 0; i < targetList.length; i++) + { + if (( ! targetTimer.running || helpBox.visible) && targetList[newIndex].enabled) + { + highlightSelectedTarget () + return + } + newIndex += increment + if (newIndex >= targetList.length) + newIndex = 0 + else if (newIndex < 0) + newIndex = targetList.length - 1 + if (targetList[newIndex].enabled) + { + selectedTarget = newIndex + highlightSelectedTarget () + break + } + } + } + + function showHelp () + { + for (var i = 0; i < targetList.length; i++) + { + targetList[i].targetVisible = true + } + helpBox.visible = true + targetTimer.restart () + } + function hideAllTargets () + { + for (var i = 0; i < targetList.length; i++) + { + targetList[i].targetVisible = false + } + helpBox.visible = false + } + function highlightSelectedTarget () + { + for (var i = 0; i < targetList.length; i++) + { + if (targetList[i] == targetList[selectedTarget]) + targetList[i].targetVisible = true + else + targetList[i].targetVisible = false + } + helpBox.visible = false + targetTimer.restart () + } +} diff --git a/FileSets/v3.60~25/OverviewFlowComplex.qml.orig b/FileSets/v3.60~25/OverviewFlowComplex.qml.orig new file mode 100644 index 00000000..65f9139a --- /dev/null +++ b/FileSets/v3.60~25/OverviewFlowComplex.qml.orig @@ -0,0 +1,484 @@ +import QtQuick 2 +import "utils.js" as Utils + +OverviewPage { + id: root + + property variant sys: theSystem + property bool hasAcOutSystem: _hasAcOutSystem.value === 1 + + title: qsTr("Overview") + + VBusItem { + id: _hasAcOutSystem + bind: "com.victronenergy.settings/Settings/SystemSetup/HasAcOutSystem" + } + + OverviewBox { + id: acInBox + + width: 148 + height: 100 + title: getAcSourceName(sys.acSource) + titleColor: "#E74c3c" + color: "#C0392B" + anchors { + top: root.top; topMargin: 1 + left: parent.left; leftMargin: 5 + } + + values: OverviewAcValues { + connection: sys.acInput + } + + MbIcon { + iconId: getAcSourceIcon(sys.acSource) + anchors { + bottom: parent.bottom + left: parent.left; leftMargin: 2 + } + opacity: 0.5 + } + } + + OverviewBox { + id: acLoadBox + title: qsTr("AC Loads") + color: "#27AE60" + titleColor: "#2ECC71" + width: 148 + height: 100 + + anchors { + left: acInBox.right + leftMargin: hasAcOutSystem ? 10 : 174 + top: root.top; topMargin: 1 + } + + values: OverviewAcValues { + connection: sys.acInLoad + } + } + + OverviewBox { + id: acOutputBox + title: qsTr("Critical Loads") + color: "#157894" + titleColor: "#419FB9" + height: 100 + width: 148 + visible: hasAcOutSystem + anchors { + right: root.right; rightMargin: 5 + top: root.top; topMargin: 17 + } + + values: OverviewAcValues { + connection: sys.acOutLoad + } + } + + Multi { + id: multi + iconId: "overview-inverter-short" + anchors { + horizontalCenter: parent.horizontalCenter + bottom: root.bottom; bottomMargin: 39 + } + } + + // invisible item to connection all AC connections to.. + Item { + id: acBus + height: 10 + anchors { + left: acInBox.left; leftMargin: hasAcOutSystem ? 5 : acInBox.width - 5 + right: acLoadBox.right; rightMargin: 2 + bottom: acInBox.bottom; bottomMargin: -15 + } + } + + Battery { + id: battery + + soc: sys.battery.soc.valid ? sys.battery.soc.value : 0 + preferRenewable: sys.preferRenewableEnergy.valid + preferRenewableOverride: sys.preferRenewableEnergy.value === 0 || sys.preferRenewableEnergy.value === 2 + preferRenewableOverrideGenset: sys.remoteGeneratorSelected.value === 1 || sys.acSource.value === 2 + height: pvInverterOnGrid.visible ? 81 : 101 + width: 145 + + anchors { + bottom: parent.bottom; bottomMargin: 5; + left:parent.left; leftMargin: 5 + } + values: Column { + y: pvInverterOnGrid.visible ? 0 : 8 + width: parent.width + + TileText { + text: sys.battery.soc.valid ? sys.battery.soc.value.toFixed(0) : "--" + font.pixelSize: 30 + + Text { + anchors { + bottom: parent.bottom; bottomMargin: 4 + horizontalCenter: parent.horizontalCenter; horizontalCenterOffset: parent.paintedWidth / 2 + 5 + } + visible: sys.battery.soc.valid + text: "%" + color: "white" + font.bold: true + font.pixelSize: 12 + } + } + TileText { + text: sys.battery.power.format(0) + } + TileText { + text: sys.battery.voltage.format(1) + " " + sys.battery.current.format(1) + } + } + } + + // PV inverter on AC in, AC Output ignored + OverviewSolarInverter { + id: pvInverterOnGridNoAcOut + title: qsTr("PV Inverter") + width: 154 + height: 100 + visible: sys.pvOnGrid.power.valid && !hasAcOutSystem + showInverterIcon: false + values: TileText { + y: 2 + text: sys.pvOnGrid.power.format(0) + font.pixelSize: 25 + } + anchors { + top: root.top; topMargin: 1 + horizontalCenter: root.horizontalCenter + } + } + + OverviewSolarInverter { + id: pvInverterOnGrid + title: qsTr("PV Inverter") + width: 148 + height: 60 + visible: sys.pvOnGrid.power.valid && hasAcOutSystem + showInverterIcon: false + values: TileText { + y: 2 + text: sys.pvOnGrid.power.format(0) + font.pixelSize: 20 + } + anchors { + bottom: battery.top; bottomMargin: 5 + left: root.left; leftMargin: 5 + } + } + + OverviewSolarInverter { + id: pvInverterOnAcOut + title: qsTr("PV Inverter") + width: 148 + height: 60 + visible: sys.pvOnAcOut.power.valid + showInverterIcon: false + + values: TileText { + y: 2 + text: sys.pvOnAcOut.power.format(0) + font.pixelSize: 20 + } + anchors { + bottom: blueSolarCharger.top; bottomMargin: 5 + right: parent.right; rightMargin: 5 + } + } + + OverviewSolarCharger { + id: blueSolarCharger + title: qsTr("PV Charger") + width: 148 + height: 60 + visible: sys.pvCharger.power.valid + showChargerIcon: false + + anchors { + right: root.right; rightMargin: 5 + bottom: root.bottom; bottomMargin: 5; + } + + values: TileText { + y: 2 + text: sys.pvCharger.power.format(0) + font.pixelSize: 20 + } + } + + OverviewEssReason { + anchors { + bottom: parent.bottom; bottomMargin: 5 + horizontalCenter: parent.horizontalCenter + } + } + + // AC source power flow + OverviewConnection { + id: acSource + ballCount: 4 + path: corner + active: root.active && hasAcOutSystem + value: flow(sys.acInput ? sys.acInput.power : undefined) * -1 + startPointVisible: false + + anchors { + right: acInBox.left; rightMargin: -9 + left: pvInverterOnGridConnection.horizontalCenter + bottom: acInBox.bottom; bottomMargin: 8 + top: acBus.verticalCenter + } + } + + // Coupled AC sources + OverviewConnection { + id: coupledAcConnection + + property VBusItem coupled: VBusItem { + property double gridPower: sys.acInput.power.valid ? sys.acInput.power.value : 0 + property double pvPower: sys.pvOnGrid.power.valid ? sys.pvOnGrid.power.value : 0 + value: gridPower + pvPower + } + + ballCount: 1 + path: straight + active: root.active && hasAcOutSystem + value: flow(coupled) + startPointVisible: false + endPointVisible: false + + anchors { + left: pvInverterOnGridConnection.right + right: vebusConnection.left + top: acBus.verticalCenter + bottom: acBus.verticalCenter + } + } + + // AC source power flow, ignored AC output + OverviewConnection { + id: acSourceNoAcOut + ballCount: 5 + path: corner + active: root.active && !hasAcOutSystem + value: acSource.value + startPointVisible: false + + anchors { + right: acInBox.left; rightMargin: -9 + left: pvInverterOnGridConnectionNoAcOut.horizontalCenter + bottom: acInBox.bottom; bottomMargin: 8 + top: acBus.verticalCenter + } + } + + // Coupled AC sources, ignored AC output + OverviewConnection { + id: coupledAcConnectionNoAcOut + + ballCount: 1 + path: straight + active: root.active && !hasAcOutSystem + value: coupledAcConnection.value + startPointVisible: false + endPointVisible: false + + anchors { + left: pvInverterOnGridConnectionNoAcOut.right + right: vebusConnection.left + top: acBus.verticalCenter + bottom: acBus.verticalCenter + } + } + + // Grid inverter power flow, ignored AC output + OverviewConnection { + id: pvInverterOnGridConnectionNoAcOut + ballCount: 1 + path: straight + active: root.active && pvInverterOnGridNoAcOut.visible + value: flow(sys.pvOnGrid ? sys.pvOnGrid.power : undefined) + startPointVisible: true + endPointVisible: false + + anchors { + top: pvInverterOnGridNoAcOut.bottom; topMargin: -8 + bottom: acBus.verticalCenter + left: pvInverterOnGridNoAcOut.left; leftMargin: 8 + right: pvInverterOnGridNoAcOut.left; rightMargin: -8 + } + } + + // Grid inverter power flow + OverviewConnection { + id: pvInverterOnGridConnection + ballCount: 1 + path: straight + active: root.active && pvInverterOnGrid.visible + value: flow(sys.pvOnGrid ? sys.pvOnGrid.power : undefined) * -1 + startPointVisible: false + + anchors { + top: acBus.verticalCenter + bottom: pvInverterOnGrid.top; bottomMargin: -8 + left: pvInverterOnGrid.right; leftMargin: -8 + } + } + + // power to loads + OverviewConnection { + id: loadConnection + ballCount: hasAcOutSystem ? 3 : 5 + path: corner + active: root.active + value: flow(sys.acInLoad.power) + startPointVisible: false + endPointVisible: true + + anchors { + right: acLoadBox.right; rightMargin: hasAcOutSystem ? 10 : acLoadBox.width - 10 + left: vebusConnection.horizontalCenter + top: acBus.verticalCenter + bottom: acLoadBox.bottom; bottomMargin: 8 + } + } + + // Towards vebus system + OverviewConnection { + id: vebusConnection + + property VBusItem vebusAcPower: VBusItem { bind: [sys.vebusPrefix, "/Ac/ActiveIn/P"] } + + ballCount: 1 + path: straight + active: root.active + value: flow(vebusAcPower) + startPointVisible: false + endPointVisible: true + + anchors { + left: multi.left; leftMargin: 8 + top: acBus.verticalCenter + bottom: multi.top; bottomMargin: -7 + } + } + + // AC out connection + OverviewConnection { + id: acOutConnection + + property double pvInverterOnAcOutPower: sys.pvOnAcOut.power.valid ? sys.pvOnAcOut.power.value : 0 + property double acOutLoad: sys.acOutLoad.power.valid ? sys.acOutLoad.power.value : 0 + property VBusItem vebusAcOutPower: VBusItem { value: acOutConnection.acOutLoad - acOutConnection.pvInverterOnAcOutPower } + + ballCount: 1 + path: straight + active: root.active && (hasAcOutSystem || pvInverterOnAcOut.visible) + value: flow(vebusAcOutPower) + endPointVisible: false + + anchors { + left: multi.right; leftMargin: -8 + right: acOutBoxConnection.left + top: multi.top; topMargin: 8 + } + } + + // UPS conenction + OverviewConnection { + id: acOutBoxConnection + + ballCount: 1 + path: straight + active: root.active && hasAcOutSystem + value: flow(sys.acOutLoad.power) + startPointVisible: false + + anchors { + left: acOutputBox.left; leftMargin: 10 + top: acOutConnection.verticalCenter + bottom: acOutputBox.bottom; bottomMargin: 9 + } + } + + // PV Inverter on AC out connection + OverviewConnection { + id: pvOnAcOutConnection + + ballCount: 1 + path: straight + active: root.active && pvInverterOnAcOut.visible + value: flow(sys.pvOnAcOut.power) + endPointVisible: false + + anchors { + left: acOutBoxConnection.left + bottom: acOutConnection.verticalCenter + top: pvInverterOnAcOut.top; topMargin: 8 + } + } + + // DC connection from multi + OverviewConnection { + ballCount: 1 + path: straight + active: root.active + value: flow(sys.inverterChargerDc.power) + endPointVisible: false + + anchors { + right: dcConnection.right; + top: multi.bottom; topMargin: -10 + bottom: dcConnection.top; + } + } + + // Battery to DC connection + OverviewConnection { + ballCount: 3 + path: straight + active: root.active + value: Utils.sign(noNoise(sys.pvCharger.power) + noNoise(sys.inverterChargerDc.power)) + startPointVisible: false + + anchors { + left: dcConnection.left; + top: dcConnection.verticalCenter + right: battery.right; rightMargin: 10 + } + } + + // Solar charger to DC connection + OverviewConnection { + ballCount: 3 + path: straight + active: root.active && blueSolarCharger.visible + value: flow(sys.pvCharger.power) + endPointVisible: false + + anchors { + right: dcConnection.right; + top: dcConnection.top + left: blueSolarCharger.left; leftMargin: 10 + } + } + + Item { + id: dcConnection + anchors { + horizontalCenter: multi.horizontalCenter + top: multi.bottom; topMargin: 10 + } + } +} diff --git a/FileSets/v3.60~25/OverviewGeneratorEnhanced.qml b/FileSets/v3.60~25/OverviewGeneratorEnhanced.qml new file mode 100644 index 00000000..37895168 --- /dev/null +++ b/FileSets/v3.60~25/OverviewGeneratorEnhanced.qml @@ -0,0 +1,549 @@ +// GuiMods enhanced generator overview +// This file has been modified to: +// add Auto Start display and control +// show voltage, current, frequency, and power gauge in AC input tile +// show the generator running state inside the icon top left +// show a warning when the generator digital input and expected generator state disagree +// move current run time to separate tile + +import QtQuick 2 +import "utils.js" as Utils +import "enhancedFormat.js" as EnhFmt + +OverviewPage { + id: root + + property string settingsBindPrefix + property string bindPrefix + property variant sys: theSystem +//////// added to show alternator in place of inactive genset + property string guiModsPrefix: "com.victronenergy.settings/Settings/GuiMods" + VBusItem { id: replaceAcInItem; bind: Utils.path(guiModsPrefix, "/ReplaceInactiveAcIn") } + property bool hasAlternator: sys.alternator.power.valid + property bool showAlternator: replaceAcInItem.valid && replaceAcInItem.value == 1 && hasAlternator && ! sys.genset.power.valid + property bool showAcIn: ! showAlternator + + property string icon: "overview-generator" + property VBusItem state: VBusItem { bind: Utils.path(bindPrefix, "/State") } + property VBusItem error: VBusItem { bind: Utils.path(bindPrefix, "/Error") } + property VBusItem runningTime: VBusItem { bind: Utils.path(bindPrefix, "/Runtime") } + property VBusItem runningBy: VBusItem { bind: Utils.path(bindPrefix, "/RunningByConditionCode") } + VBusItem { id: totalAcummulatedTime; bind: Utils.path(settingsBindPrefix, "/AccumulatedTotal") } + VBusItem { id: totalAccumulatedTimeOffset; bind: Utils.path(settingsBindPrefix, "/AccumulatedTotalOffset") } + property VBusItem quietHours: VBusItem { bind: Utils.path(bindPrefix, "/QuietHours") } + property VBusItem testRunDuration: VBusItem { bind: Utils.path(settingsBindPrefix, "/TestRun/Duration") } + property VBusItem nextTestRun: VBusItem { bind: Utils.path(bindPrefix, "/NextTestRun") } + property VBusItem skipTestRun: VBusItem { bind: Utils.path(bindPrefix, "/SkipTestRun") } + + property VBusItem todayRuntime: VBusItem { bind: Utils.path(bindPrefix, "/TodayRuntime") } + property VBusItem manualTimer: VBusItem { bind: Utils.path(bindPrefix, "/ManualStartTimer") } + property VBusItem autoStart: VBusItem { bind: Utils.path(settingsBindPrefix, "/AutoStartEnabled") } + + property bool errors: ! state.valid || state.value == 10 + + property VBusItem externalOverrideItem: VBusItem { bind: Utils.path(bindPrefix, "/ExternalOverride") } + property bool externalOverride: externalOverrideItem.valid && externalOverrideItem.value == 1 && ! errors + + VBusItem { id: showGaugesItem; bind: Utils.path(guiModsPrefix, "/ShowGauges") } + property bool showGauges: showGaugesItem.valid ? showGaugesItem.value === 1 ? true : false : false + property bool editMode: autoRunTile.editMode || manualTile.editMode + + VBusItem { id: serviceInterval; bind: Utils.path(settingsBindPrefix, "/ServiceInterval") } + VBusItem { id: serviceCounterItem; bind: Utils.path(bindPrefix, "/ServiceCounter") } + property bool showServiceInfo: serviceCounterItem.valid && serviceInterval.valid && serviceInterval.value > 0 + property bool serviceOverdue: showServiceInfo && serviceCounterItem.value < 0 + +//////// add to display AC input ignored + VBusItem { id: ignoreAcInput1; bind: Utils.path(sys.vebusPrefix, "/Ac/State/IgnoreAcIn1") } + VBusItem { id: ignoreAcInput2; bind: Utils.path(sys.vebusPrefix, "/Ac/State/IgnoreAcIn2") } + VBusItem { id: acActiveInput; bind: Utils.path(sys.vebusPrefix, "/Ac/ActiveIn/ActiveInput") } + VBusItem { id: ac1source; bind: Utils.path("com.victronenergy.settings", "/Settings/SystemSetup/AcInput1") } + VBusItem { id: ac2source; bind: Utils.path("com.victronenergy.settings", "/Settings/SystemSetup/AcInput2") } + + title: qsTr("Generator") + + property bool autoStartSelected: false + + Component.onCompleted: + { + setFocusManual () + } + + Keys.forwardTo: [keyHandler] + Item + { + id: keyHandler + Keys.onUpPressed: + { + setFocusAuto () + event.accepted = true + } + Keys.onDownPressed: + { + setFocusManual () + event.accepted = true + } + // prevents page changes while timers are running + //// Keys.onReturnPressed: event.accepted = manualTile.startCountdown || autoRunTile.startCountdown + //// Keys.onEscapePressed: event.accepted = manualTile.startCountdown || autoRunTile.startCountdown + } + + function setFocusManual () + { + autoStartSelected = false + } + + function setFocusAuto () + { + autoStartSelected = true + } + + function formatTime (time) + { + if (time >= 3600) + return (time / 3600).toFixed(0) + " h" + else + return (time / 60).toFixed(0) + " m" + } + + function stateDescription() + { + if (!state.valid) + return qsTr ("") + else if (state.value === 10) + { + switch(error.value) + { + case 1: + return qsTr("Error: Remote switch control disabled") + case 2: + return qsTr("Error: Generator in fault condition") + case 3: + return qsTr("Error: Generator not detected at AC input") + default: + return qsTr("Error") + } + } + else + { + var condition = "" + var running = true + var manual = false + switch (runningBy.value) + { + case 0: // stopped + condition = "" + running = false + break;; + case 1: + manual = true + condition = "" + break;; + case 2: + condition = qsTr("Test run") + break;; + case 3: + condition = qsTr("Loss of communication") + break;; + case 4: + condition = qsTr("SOC") + break;; + case 5: + condition = qsTr("AC load") + break;; + case 6: + condition = qsTr("Battery current") + break;; + case 7: + condition = qsTr("Battery voltage") + break;; + case 8: + condition = qsTr("Inverter temperature") + break;; + case 9: + condition = qsTr("Inverter overload") + break;; + default: + condition = qsTr("???") + break;; + } + + if (externalOverride) + { + if (running && ! manual) + return qsTr ("auto pending: ") + condition + else + return " " + } + else if (manual) + { + if (manualTimer.valid && manualTimer.value > 0) + return qsTr("Timed run") + else + return qsTr("Manual run") + } + else if (running) + return qsTr ("auto run: ") + condition + else + return " " + } + } + + function getNextTestRun() + { + if ( ! root.state.valid) + return "" + if (!nextTestRun.value) + return qsTr("No test run programmed") + + var todayDate = new Date() + var nextDate = new Date(nextTestRun.value * 1000) + var nextDateEnd = new Date(nextDate.getTime()) + var message = "" + // blank "next run" if test run is active + if (runningBy.value == 2) + return " " + else if (todayDate.getDate() == nextDate.getDate() && todayDate.getMonth() == nextDate.getMonth()) + { + message = qsTr("Next test run today %1").arg( + Qt.formatDateTime(nextDate, "hh:mm").toString()) + } + else + { + message = qsTr("Next test run on %1").arg( + Qt.formatDateTime(nextDate, "dd/MM/yyyy").toString()) + nextDateEnd.setSeconds(testRunDuration.value) } + + if (skipTestRun.value === 1) + message += qsTr(" \(skipped\)") + + return message + } + + Tile { + id: imageTile + width: 180 + height: 136 + MbIcon { + id: generator + iconId: icon + anchors.centerIn: parent + } + anchors { top: parent.top; left: parent.left } + values: [ + // spacer + TileText { + width: imageTile.width - 5 + text: " " + font.pixelSize: 62 + }, + TileText { + width: imageTile.width - 5 + text: runningTime.valid ? runningTime.value > 0 ? qsTr ("Running ") : qsTr ("Stopped ") : " " + } + ] + } + + Tile { + id: statusTile + height: imageTile.height + color: "#4789d0" + anchors { top: parent.top; left: imageTile.right; right: root.right } + title: qsTr("STATUS") + values: [ + TileText + { + width: statusTile.width - 5 + color: externalOverride ? "yellow" : "white" + text: + { + var runPrefix + var message + if ( ! root.state.valid) + return qsTr ("Generator not connected") + else if (root.state.value === 2) + runPrefix = qsTr("Warming up for ") + else + runPrefix = qsTr ("Running for ") + if (!root.state.valid) + message = "" + else if (externalOverride) + if (root.state.value === 0) + message = qsTr("External Override - running") + else + message = qsTr("External Override - stopped") + else if (root.state.value === 3) + message = qsTr("Cool-down") + else if (root.state.value === 4) + message = qsTr("Stopping") + else if (runningBy.value == 0) + message = qsTr ("Stopped") + else if ( ! runningTime.valid) + message = runPrefix + "??" + else + { + message = runPrefix + formatTime (runningTime.value) + if (manualTimer.valid && manualTimer.value > 0) + message += qsTr (" ends in ") + formatTime (manualTimer.value) + } + return message + } + }, + Rectangle + { + width: parent.width + height: 3 + color: "transparent" + }, + TileTextMultiLine + { + text: stateDescription() + width: statusTile.width - 5 + }, + Rectangle + { + width: parent.width + height: 3 + color: "transparent" + }, + TileText + { + text: qsTr("\nQuiet hours"); + width: statusTile.width - 5 + font.bold: runningBy.valid && runningBy.value != 0 + color: font.bold ? "yellow" : "white" + visible: quietHours.value === 1 + }, + Rectangle + { + width: parent.width + height: 3 + color: "transparent" + }, + TileTextMultiLine + { + width: statusTile.width - 5 + text: getNextTestRun() + } + ] + } + + Tile { + id: acInTile + title: qsTr("GENERATOR POWER") + width: 150 + height: 136 + color: "#82acde" + anchors { top: imageTile.bottom; left: parent.left } + visible: showAcIn + values: + [ + OverviewAcValuesEnhanced + { + connection: sys.genset + visible: sys.genset.power.valid + }, + TileText + { + width: acInTile.width - 5 + text: + { + if (ac1source.valid && ac1source.value == 2) + { + if (ignoreAcInput1.valid && ignoreAcInput1.value == 1) + return qsTr ("\nAC In Ignored\nduring\ngenerator\nstart / stop") + else + return "" + } + else if (ac2source.valid && ac2source.value == 2) + { + if (ignoreAcInput2.valid && ignoreAcInput2.value == 1) + return qsTr ("\nAC In Ignored\nduring\ngenerator\nstart / stop") + else + return "" + } + else + return qsTr ("\nAC In\nis not\ngenerator") + } + visible: !sys.genset.power.valid + } + ] +////// add power bar graph + PowerGauge + { + id: acInBar + width: parent.width + height: 12 + anchors + { + top: parent.top; topMargin: 20 + horizontalCenter: parent.horizontalCenter + } + connection: sys.genset + useInputCurrentLimit: true + maxForwardPowerParameter: "" + maxReversePowerParameter: "" + visible: showGauges && sys.genset.power.valid + } + } + +//////// added to show alternator in place of AC generator + Tile { + id: alternatorTile + title: qsTr("ALTERNATOR POWER") + color: "#157894" + anchors.fill: acInTile + visible: showAlternator + values: + [ + TileText + { + text: EnhFmt.formatVBusItem (sys.alternator.power, "W") + font.pixelSize: 22 + } + ] +////// add power bar graph + PowerGauge + { + id: alternatorGauge + width: parent.width + height: 12 + anchors + { + top: parent.top; topMargin: 20 + horizontalCenter: parent.horizontalCenter + } + connection: sys.alternator + maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/MaxAlternatorPower" + visible: showGauges && showAlternator + } + } + + Tile { + id: runTimeTile + title: qsTr("RUN TIMES") + width: 140 + anchors { top: acInTile.top; bottom: parent.bottom; left: acInTile.right } + values: [ + TileText + { + width: runTimeTile.width - 5 + text: qsTr ("Today") + }, + TileText { + width: runTimeTile.width - 5 + text: todayRuntime.valid ? formatTime (todayRuntime.value) : "--" + }, + Rectangle + { + width: parent.width + height: 3 + color: "transparent" + }, + TileText + { + width: runTimeTile.width - 5 + text: qsTr ("Accumulated") + }, + TileText + { + width: runTimeTile.width - 5 + text: + { + if ( ! totalAcummulatedTime.valid) + return "--" + else if (totalAccumulatedTimeOffset.valid ) + return formatTime (totalAcummulatedTime.value - totalAccumulatedTimeOffset.value) + else + return formatTime (totalAcummulatedTime.value) + } + }, + Rectangle + { + width: parent.width + height: 3 + color: "transparent" + }, + TileText + { + width: runTimeTile.width - 5 + visible: showServiceInfo + color: serviceOverdue ? "red" : "white" + text: serviceOverdue ? qsTr ("Service OVERDUE") : qsTr ("Service in") + }, + TileText + { + width: runTimeTile.width - 5 + visible: showServiceInfo + color: serviceOverdue ? "red" : "white" + text: formatTime (Math.abs (serviceCounterItem.value)) + } + ] + } + + TileAutoRunEnhanced + { + id: autoRunTile + bindPrefix: root.bindPrefix + focus: root.active && autoStartSelected + connected: state.valid + tileHeight: acInTile.height / 2 + anchors { + bottom: parent.bottom; bottomMargin: tileHeight + left: runTimeTile.right + right: parent.right + } + } + + TileManualStartEnhanced + { + id: manualTile + bindPrefix: root.bindPrefix + focus: root.active && ! autoStartSelected + connected: state.valid + tileHeight: acInTile.height / 2 + anchors { + bottom: parent.bottom + left: runTimeTile.right + right: parent.right + } + } + + // mouse areas must be AFTER their associated objects so those objects can catch mouse events + // rejected by these areas + // mouse targets need to be disabled while changes are pending + MouseArea { + id: autoRunTarget + anchors.fill: autoRunTile + enabled: root.active && ! editMode + onPressed: + { + if ( ! root.autoStartSelected ) + { + setFocusAuto () + mouse.accepted = true + } + else + { + mouse.accepted = false + } + } + } + MouseArea { + id: manualStartTarget + anchors.fill: manualTile + enabled: root.active && ! editMode + onPressed: + { + if ( root.autoStartSelected ) + { + setFocusManual () + mouse.accepted = true + } + else + { + mouse.accepted = false + } + } + } +} diff --git a/FileSets/v3.60~25/OverviewGeneratorEnhanced.qml.orig b/FileSets/v3.60~25/OverviewGeneratorEnhanced.qml.orig new file mode 100644 index 00000000..3dd70448 --- /dev/null +++ b/FileSets/v3.60~25/OverviewGeneratorEnhanced.qml.orig @@ -0,0 +1,96 @@ +import QtQuick 2 +import "utils.js" as Utils + +MbPage { + id: root + title: qsTr("Generator start/stop") + property string settingsBindPrefix + property string startStopBindPrefix + property bool showRunTime: true + property alias startStopModel: _startStopModel + property VBusItem activeCondition: VBusItem { bind: Utils.path(startStopBindPrefix, "/RunningByCondition") } + property VBusItem generatorState: VBusItem { bind: Utils.path(startStopBindPrefix, "/State") } + property VBusItem runningTime: VBusItem { bind: Utils.path(startStopBindPrefix, "/Runtime") } + + FnGeneratorStates { + id: genState + } + + model: startStopModel + + function formatError(text, value) + { + return "#" + value.toString() + " " + text + } + + VisibleItemModel { + id: _startStopModel + + MbSwitch { + name: qsTr("Auto start functionality") + bind: Utils.path(startStopBindPrefix, "/AutoStartEnabled") + show: startStopBindPrefix === "com.victronenergy.generator.startstop0" + } + + MbSubMenu { + description: qsTr("Manual start") + show: startStopBindPrefix === "com.victronenergy.generator.startstop0" + subpage: + Component { + PageGeneratorManualStart { + startStopBindPrefix: root.startStopBindPrefix + } + } + } + + MbItemValue { + description: qsTr("Current run time") + item.text: runningTime.valid ? Utils.secondsToNoSecsString(runningTime.value) : "0" + show: generatorState.value >= 1 && generatorState.value <= 3 // Running, Warm-up, Cool-down + } + + MbItemValue { + description: qsTr("State") + show: startStopBindPrefix === "com.victronenergy.generator.startstop0" + item.text: activeCondition.valid ? genState.getState(generatorState.value, activeCondition.value) : '---' + } + + MbItemOptions { + id: _gensetStatus + description: qsTr("Error") + bind: Utils.path(startStopBindPrefix, "/Error") + readonly: true + show: valid && startStopBindPrefix === "com.victronenergy.generator.startstop0" + possibleValues: [ + MbOption { description: qsTr("No error"); value: 0 }, + MbOption { description: formatError(qsTr("Remote switch control disabled"), 1); value: 1 }, + MbOption { description: formatError(qsTr("Generator in fault condition"), 2); value: 2 }, + MbOption { description: formatError(qsTr("Generator not detected at AC input"), 3); value: 3 } + ] + } + + MbSubMenu { + id: conditions + description: qsTr("Settings") + subpage: Component { + PageSettingsGenerator { + settingsBindPrefix: root.settingsBindPrefix + startStopBindPrefix: root.startStopBindPrefix + } + } + } + + MbSubMenu { + id: runtimePage + description: qsTr("Run time and service") + subpage: + Component { + PageGeneratorRuntimeService { + title: qsTr("Run time and service") + settingsBindPrefix: root.settingsBindPrefix + startStopBindPrefix: root.startStopBindPrefix + } + } + } + } +} diff --git a/FileSets/v3.60~25/OverviewGeneratorRelayEnhanced.qml b/FileSets/v3.60~25/OverviewGeneratorRelayEnhanced.qml new file mode 100644 index 00000000..48133381 --- /dev/null +++ b/FileSets/v3.60~25/OverviewGeneratorRelayEnhanced.qml @@ -0,0 +1,8 @@ +import QtQuick 2 +import "utils.js" as Utils + +OverviewGeneratorEnhanced { + icon: "overview-generator" + settingsBindPrefix: "com.victronenergy.settings/Settings/Generator0" + bindPrefix: "com.victronenergy.generator.startstop0" +} diff --git a/FileSets/v3.60~25/OverviewGeneratorRelayEnhanced.qml.orig b/FileSets/v3.60~25/OverviewGeneratorRelayEnhanced.qml.orig new file mode 100644 index 00000000..3b1a162b --- /dev/null +++ b/FileSets/v3.60~25/OverviewGeneratorRelayEnhanced.qml.orig @@ -0,0 +1,8 @@ +import QtQuick 2 +import "utils.js" as Utils + +OverviewGenerator { + icon: "overview-generator" + settingsBindPrefix: "com.victronenergy.settings/Settings/Generator0" + bindPrefix: "com.victronenergy.generator.startstop0" +} diff --git a/FileSets/v3.60~25/OverviewHubEnhanced.qml b/FileSets/v3.60~25/OverviewHubEnhanced.qml new file mode 100644 index 00000000..c4c492d8 --- /dev/null +++ b/FileSets/v3.60~25/OverviewHubEnhanced.qml @@ -0,0 +1,1504 @@ +////// MODIFIED to show: +////// tanks in a row along bottom +////// PV voltage and current and DC power current (up to 2 MPPTs with tanks and temps or 3 without) +////// PV inverter power (up to 2 with tanks and temps or 3 without) +////// voltage, current, frequency in AC tiles (plus current limit for AC input) +////// time of day +////// current in DC Loads +////// remaining time in Battery tile +////// bar graphs on AC in/out and Multi +////// detail pages for all tiles +////// bar gauge on PV Charger tile +////// add support for VE.Direct inverters + +import QtQuick 2 +import "utils.js" as Utils +////// ADDED to show tanks +import com.victron.velib 1.0 +import "timeToGo.js" as TTG +import "enhancedFormat.js" as EnhFmt + +OverviewPage { + id: root + + property variant sys: theSystem + + property string systemPrefix: "com.victronenergy.system" + property string guiModsPrefix: "com.victronenergy.settings/Settings/GuiMods" + +////// GuiMods — DarkMode + property VBusItem darkModeItem: VBusItem { bind: "com.victronenergy.settings/Settings/GuiMods/DarkMode" } + property bool darkMode: darkModeItem.valid && darkModeItem.value == 1 + + VBusItem { id: vebusService; bind: Utils.path(systemPrefix, "/VebusService") } + property bool isMulti: vebusService.valid + property string veDirectInverterService: "" + property string inverterService: vebusService.valid ? vebusService.value : veDirectInverterService + + VBusItem { id: replaceAcInItem; bind: Utils.path(guiModsPrefix, "/ReplaceInactiveAcIn") } + property bool hasAlternator: sys.alternator.power.valid + property bool replaceAcIn: replaceAcInItem.valid && replaceAcInItem.value == 1 && hasAlternator && (sys.acSource == 0 || sys.acSource == 240) + property bool showAcInput: ((isMulti || sys.acInput.power.valid) && ! replaceAcIn) || showAllTiles + property bool showAlternator: !showAcInput && hasAlternator + property double alternatorFlow: showAlternator ? noNoise (sys.alternator.power) : 0 + property bool showAcLoads: isMulti || sys.acLoad.power.valid || veDirectInverterService != "" + property bool showDcSystem: (hasDcSystemItem.valid && hasDcSystemItem.value > 0) || showAllTiles + property bool hasInverter: false + property bool showInverter: hasInverter || inverterService != "" || showAllTiles + + property bool hasAcSolarOnAcIn1: sys.pvOnAcIn1.power.valid + property bool hasAcSolarOnAcIn2: sys.pvOnAcIn2.power.valid + property bool hasAcSolarOnIn: hasAcSolarOnAcIn1 || hasAcSolarOnAcIn2 + property bool hasAcSolarOnOut: sys.pvOnAcOut.power.valid + property bool hasAcSolar: hasAcSolarOnIn || hasAcSolarOnOut + property bool hasDcSolar: sys.pvCharger.power.valid + property bool hasDcAndAcSolar: hasAcSolar && hasDcSolar + property bool showDcAndAcSolar: hasDcAndAcSolar || showAllTiles + property bool showDcSolar: hasDcSolar || showAllTiles + property bool showAcSolar: hasAcSolar || showAllTiles +////// ADDED to show tanks + property int bottomOffset: 45 + property string settingsBindPreffix: "com.victronenergy.settings" + property string pumpBindPreffix: "com.victronenergy.pump.startstop0" + property int numberOfTemps: 0 +//////// added/modified for control show/hide gauges, tanks and temps from menus + property int tankCount: showTanksEnable ? tankModel.rowCount : 0 + property int tempCount: showTempsEnable ? numberOfTemps : 0 + property int tankTempCount: tankCount + tempCount + property bool showTanks: showTanksEnable ? showStatusBar ? false : tankCount > 0 ? true : false : false + property bool showTemps: showTempsEnable ? showStatusBar ? false : tempCount > 0 ? true : false : false + property bool showTanksTemps: showTanks || showTemps + property int compactThreshold: 45 // height below this will be compacted vertically + property bool compact: showTanks && showTemps && tankTempCount > 4 + property int tanksHeight: compact ? 22 : 45 + + property int leftTileCount: (showAcInput ? 1 : 0) + (showAlternator ? 1 : 0) + (showAlternator ? 1 : 0) + +//////// add for PV CHARGER voltage and current + property string pvChargerPrefix1: "" + property string pvChargerPrefix2: "" + property string pvChargerPrefix3: "" + property string pvChargerPrefix4: "" + property string pvChargerPrefix5: "" + property string pvChargerPrefix6: "" + property string pvChargerPrefix7: "" + property int numberOfPvChargers: 0 + property int pvChargerRows: showTanksTemps ? 4 : 7 + property int pvRowsPerCharger: Math.max ( 1, Math.min (pvChargerRows / numberOfPvChargers, 3)) + property bool pvChargerCompact: pvRowsPerCharger < 3 ? true : false + property bool pvShowDetails: pvRowsPerCharger >= 2 ? true : false + +//////// add for PV INVERTER power + property string pvInverterPrefix1: "" + property string pvInverterPrefix2: "" + property string pvInverterPrefix3: "" + property int numberOfPvInverters: 0 + +//////// add for alternator - alternator replaces AC in if AC in is not present + property string alternatorPrefix1: "" + property string alternatorPrefix2: "" + property int numberOfAlternators: 0 + VBusItem { id: alternatorName1; bind: Utils.path(alternatorPrefix1, "/CustomName") } + VBusItem { id: alternatorPower1; bind: Utils.path(alternatorPrefix1, "/Dc/0/Power") } + VBusItem { id: alternatorVoltage1; bind: Utils.path(alternatorPrefix1, "/Dc/0/Voltage") } + VBusItem { id: alternatorCurrent1; bind: Utils.path(alternatorPrefix1, "/Dc/0/Current") } + VBusItem { id: alternatorName2; bind: Utils.path(alternatorPrefix2, "/CustomName") } + VBusItem { id: alternatorPower2; bind: Utils.path(alternatorPrefix2, "/Dc/0/Power") } + +//////// added for control show/hide gauges, tanks and temps from menus + VBusItem { id: showGaugesItem; bind: Utils.path(guiModsPrefix, "/ShowGauges") } + property bool showGauges: showGaugesItem.valid ? showGaugesItem.value === 1 ? true : false : false + VBusItem { id: showTanksItem; bind: Utils.path(guiModsPrefix, "/ShowEnhancedFlowOverviewTanks") } + property bool showTanksEnable: showTanksItem.valid ? showTanksItem.value === 1 ? true : false : false + VBusItem { id: showTempsItem; bind: Utils.path(guiModsPrefix, "/ShowEnhancedFlowOverviewTemps") } + property bool showTempsEnable: showTempsItem.valid ? showTempsItem.value === 1 ? true : false : false + +//////// added to show/dim tiles + VBusItem { id: showInactiveTilesItem; bind: Utils.path(guiModsPrefix, "/ShowInactiveFlowTiles") } + property real disabledTileOpacity: (showInactiveTiles && showInactiveTilesItem.value === 1) ? 0.3 : 1 + property bool showInactiveTiles: showInactiveTilesItem.valid && showInactiveTilesItem.value >= 1 + + VBusItem { id: showBatteryTempItem; bind: Utils.path(guiModsPrefix, "/ShowBatteryTempOnFlows") } + property bool showBatteryTemp: showBatteryTempItem.valid && showBatteryTempItem.value == 1 + + // for debug, ignore validity checks so all tiles and their flow lines will show + property bool showAllTiles: showInactiveTilesItem.valid && showInactiveTilesItem.value == 3 + +//////// added to control time display + VBusItem { id: timeFormatItem; bind: Utils.path(guiModsPrefix, "/TimeFormat") } + property string timeFormat: getTimeFormat () + + function getTimeFormat () + { + if (!timeFormatItem.valid || timeFormatItem.value === 0) + return "" + else if (timeFormatItem.value === 2) + return "h:mm ap" + else + return "hh:mm" + } + +//////// add to display individual PV charger power + VBusItem { id: pvName1; bind: Utils.path(pvChargerPrefix1, "/CustomName") } + VBusItem { id: pvPower1; bind: Utils.path(pvChargerPrefix1, "/Yield/Power") } + VBusItem { id: pvVoltage1; bind: Utils.path(pvChargerPrefix1, "/Pv/V") } + VBusItem { id: pvCurrent1; bind: Utils.path(pvChargerPrefix1, "/Pv/I") } + VBusItem { id: pv1NrTrackers; bind: Utils.path(pvChargerPrefix1, "/NrOfTrackers") } + VBusItem { id: pvName2; bind: Utils.path(pvChargerPrefix2, "/CustomName") } + VBusItem { id: pvPower2; bind: Utils.path(pvChargerPrefix2, "/Yield/Power") } + VBusItem { id: pvVoltage2; bind: Utils.path(pvChargerPrefix2, "/Pv/V") } + VBusItem { id: pvCurrent2; bind: Utils.path(pvChargerPrefix2, "/Pv/I") } + VBusItem { id: pv2NrTrackers; bind: Utils.path(pvChargerPrefix2, "/NrOfTrackers") } + VBusItem { id: pvName3; bind: Utils.path(pvChargerPrefix3, "/CustomName") } + VBusItem { id: pvPower3; bind: Utils.path(pvChargerPrefix3, "/Yield/Power") } + VBusItem { id: pvVoltage3; bind: Utils.path(pvChargerPrefix3, "/Pv/V") } + VBusItem { id: pvCurrent3; bind: Utils.path(pvChargerPrefix3, "/Pv/I") } + VBusItem { id: pv3NrTrackers; bind: Utils.path(pvChargerPrefix3, "/NrOfTrackers") } + VBusItem { id: pvName4; bind: Utils.path(pvChargerPrefix4, "/CustomName") } + VBusItem { id: pvPower4; bind: Utils.path(pvChargerPrefix4, "/Yield/Power") } + VBusItem { id: pvName5; bind: Utils.path(pvChargerPrefix5, "/CustomName") } + VBusItem { id: pvPower5; bind: Utils.path(pvChargerPrefix5, "/Yield/Power") } + VBusItem { id: pvName6; bind: Utils.path(pvChargerPrefix6, "/CustomName") } + VBusItem { id: pvPower6; bind: Utils.path(pvChargerPrefix6, "/Yield/Power") } + VBusItem { id: pvName7; bind: Utils.path(pvChargerPrefix7, "/CustomName") } + VBusItem { id: pvPower7; bind: Utils.path(pvChargerPrefix7, "/Yield/Power") } + + VBusItem { id: timeToGo; bind: Utils.path("com.victronenergy.system","/Dc/Battery/TimeToGo") } + +//////// add to display PV Inverter power + VBusItem { id: pvInverterPower1; bind: Utils.path(pvInverterPrefix1, "/Ac/Power") } + VBusItem { id: pvInverterL1Power1; bind: Utils.path(pvInverterPrefix1, "/Ac/L1/Power") } + VBusItem { id: pvInverterL2Power1; bind: Utils.path(pvInverterPrefix1, "/Ac/L2/Power") } + VBusItem { id: pvInverterL3Power1; bind: Utils.path(pvInverterPrefix1, "/Ac/L3/Power") } + VBusItem { id: pvInverterName1; bind: Utils.path(pvInverterPrefix1, "/CustomName") } + VBusItem { id: pvInverterPower2; bind: Utils.path(pvInverterPrefix2, "/Ac/Power") } + VBusItem { id: pvInverterName2; bind: Utils.path(pvInverterPrefix2, "/CustomName") } + VBusItem { id: pvInverterPower3; bind: Utils.path(pvInverterPrefix3, "/Ac/Power") } + VBusItem { id: pvInverterName3; bind: Utils.path(pvInverterPrefix3, "/CustomName") } + +//////// add to display AC input ignored + VBusItem { id: ignoreAcInput1; bind: Utils.path(inverterService, "/Ac/State/IgnoreAcIn1") } + VBusItem { id: ignoreAcInput2; bind: Utils.path(inverterService, "/Ac/State/IgnoreAcIn2") } + VBusItem { id: acActiveInput; bind: Utils.path(inverterService, "/Ac/ActiveIn/ActiveInput") } + + VBusItem { id: hasDcSystemItem; bind: "com.victronenergy.settings/Settings/SystemSetup/HasDcSystem" } + + //Component.onCompleted: { discoverServices(); showHelp () } + onActiveChanged: + { + if (root.active) + { + discoverServices() + showHelp () + } + } + + title: qsTr("Simple Overview") + + OverviewBox { + id: acInBox +////// GuiMods — DarkMode + titleColor: !darkMode ? "#E74c3c" : "#73261E" + color: !darkMode ? "#C0392B" : "#601C15" + opacity: showAcInput ? 1 : disabledTileOpacity + visible: showAcInput || showInactiveTiles + width: 148 + height: showStatusBar ? 100 : 120 + title: + { + // input 1 is active + if (acActiveInput.value == 0) + { + if (ignoreAcInput1.valid && ignoreAcInput1.value == 1) + return qsTr ("AC In 1 Ignored") + else + return getAcSourceName(sys.acSource) + } + // input 2 is active + else if (acActiveInput.value == 1) + { + if (ignoreAcInput2.valid && ignoreAcInput2.value == 1) + return qsTr ("AC In 2 Ignored") + else + return getAcSourceName(sys.acSource) + } + else + return "no input" + } + anchors { + top: multi.top + left: parent.left; leftMargin: 10 + } + + values: OverviewAcValuesEnhanced { + connection: sys.acInput + } + + MbIcon { + iconId: getAcSourceIcon(sys.acSource) + anchors { + bottom: parent.bottom + left: parent.left; leftMargin: 2 + } + opacity: 0.5 + } +////// add power bar graph + PowerGauge + { + id: acInBar + width: parent.width + height: 12 + anchors + { + top: parent.top; topMargin: 16 + horizontalCenter: parent.horizontalCenter + } + connection: sys.acInput + useInputCurrentLimit: true + maxForwardPowerParameter: "" + maxReversePowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/MaxFeedInPower" + visible: showGauges && showAcInput + } + DetailTarget { id: acInputTarget; detailsPage: "DetailAcInput.qml" } + } + + + //// add alternator if AC input not present + OverviewBox { + id: alternatorBox + title: qsTr ("Alternator") + color: !darkMode ? "#157894" : "#0a3c4a" + titleColor: !darkMode ? "#419FB9" : "#204f5c" + opacity: showAlternator ? 1 : disabledTileOpacity + visible: showAlternator || showInactiveTiles && ! acInBox.visible + width: 148 + height: showStatusBar ? 100 : 120 + anchors.fill: acInBox + values: Column + { + width: parent.width + TileText + { + text: " " + font.pixelSize: 6 + } + TileText + { + text: EnhFmt.formatVBusItem (sys.alternator.power, "W") + font.pixelSize: 19 + } + TileText + { + text: alternatorName1.valid ? alternatorName1.value : "-" + visible: showAlternator && numberOfAlternators >= 1 + } + TileText + { + text: EnhFmt.formatVBusItem (alternatorPower1, "W") + font.pixelSize: 15 + visible: showAlternator && numberOfAlternators > 1 + } + TileText { + text: EnhFmt.formatVBusItem (alternatorVoltage1, "V") + font.pixelSize: 15 + visible: showAlternator && numberOfAlternators == 1 + } + TileText { + text: EnhFmt.formatVBusItem (alternatorCurrent1, "A") + font.pixelSize: 15 + visible: showAlternator && numberOfAlternators == 1 + } + TileText + { + text: alternatorName2.valid ? alternatorName2.value : "-" + visible: showAlternator && numberOfAlternators >= 2 + } + TileText + { + text: EnhFmt.formatVBusItem (alternatorPower1, "W") + font.pixelSize: 15 + visible: showAlternator && numberOfAlternators >= 2 + } + } + + PowerGauge + { + id: alternatorBar + width: parent.width + height: 12 + anchors + { + top: parent.top; topMargin: 16 + horizontalCenter: parent.horizontalCenter + } + connection: sys.alternator + maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/MaxAlternatorPower" + visible: showGauges && showAlternator + } + DetailTarget { id: alternatorTarget; detailsPage: "DetailAlternator.qml" } + } + + MultiEnhanced { + id: multi + anchors { + horizontalCenter: parent.horizontalCenter + top: parent.top; topMargin: 3 + } + inverterService: root.inverterService + opacity: showInverter ? 1 : disabledTileOpacity + visible: showInverter || showInactiveTiles +////// add power bar graph + PowerGaugeMulti + { + id: multiBar + width: multi.width + height: 12 + anchors + { + top: parent.top; topMargin: 23 + horizontalCenter: parent.horizontalCenter + } + inverterService: root.inverterService + visible: showGauges && showInverter + } + DetailTarget { id: multiTarget; detailsPage: "DetailInverter.qml"; width: 60; height: 60 } + } + +////// ADDED to show time inside inverter icon + Timer { + id: wallClock + running: timeFormat != "" + repeat: true + interval: 1000 + triggeredOnStart: true + onTriggered: time = Qt.formatDateTime(new Date(), timeFormat) + property string time + } + TileText + { + text: wallClock.time + font.pixelSize: 18 + color: showInverter || darkMode ? "white" : "black" + anchors + { + top: multi.top; topMargin: 96 + horizontalCenter: multi.horizontalCenter + } + visible: wallClock.running + } + + OverviewBox { + id: acLoadBox + visible: showAcLoads || showInactiveTiles + opacity: showAcLoads ? 1 : disabledTileOpacity + title: qsTr("AC Loads") +////// GuiMods — DarkMode + color: !darkMode ? "#27AE60" : "#135730" + titleColor: !darkMode ? "#2ECC71" : "#176638" + width: 148 + height: showStatusBar ? 80 : 102 + + anchors { + right: parent.right; rightMargin: 10 + top: multi.top + } + + values: OverviewAcValuesEnhanced { + connection: sys.acLoad + } +////// add power bar graph + PowerGauge + { + id: acLoadBar + width: parent.width + height: 12 + anchors + { + top: parent.top; topMargin: 16 + horizontalCenter: parent.horizontalCenter + } + connection: sys.acLoad + maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/AcOutputMaxPower" + visible: showGauges && showAcLoads + } + DetailTarget { id: loadsOnOutputTarget; detailsPage: "DetailLoadsCombined.qml" } + } + + Battery { + id: battery + width: acInBox.width + height: 96 + anchors { + bottom: parent.bottom; bottomMargin: showTanksTemps ? bottomOffset + 3 : 5; + left: parent.left; leftMargin: 10 + } + soc: sys.battery.soc.valid ? sys.battery.soc.value : 0 +////// add battery current bar graph + PowerGaugeBattery + { + id: batteryBar + width: parent.width + height: 10 + anchors + { + top: parent.top; topMargin: 52 + horizontalCenter: parent.horizontalCenter + } + visible: showGauges + } + + values: Column { + width: parent.width + + TileText { + text: sys.battery.soc.value === undefined ? "--" : sys.battery.soc.format (0) + font.pixelSize: 25 + } + TileText { + text: EnhFmt.formatVBusItem (sys.battery.power, "W") + } + TileText { + text: " " + font.pixelSize: 6 + } + TileText { + text: EnhFmt.formatVBusItem (sys.battery.voltage, "V ", 2) + + EnhFmt.formatVBusItem (sys.battery.current, "A") + } + TileText { + text: timeToGo.valid ? qsTr ("Remain: ") + TTG.formatTimeToGo (timeToGo) : qsTr (" ") + } + } + DetailTarget { id: batteryTarget; detailsPage: "DetailBattery.qml" } + } + + VBusItem { id: dcSystemNameItem; bind: Utils.path(settingsBindPreffix, "/Settings/GuiMods/CustomDcSystemName") } + + OverviewBox { + id: dcSystemBox +////// wider to make room for current + width: multi.width + 20 + height: 45 + opacity: showDcSystem ? 1 : disabledTileOpacity + visible: showDcSystem || showInactiveTiles + title: dcSystemNameItem.valid && dcSystemNameItem.value != "" ? dcSystemNameItem.value : qsTr ("DC System") + + anchors { + horizontalCenter: multi.horizontalCenter + horizontalCenterOffset: 2 +////// MODIFIED to show tanks + bottom: parent.bottom; bottomMargin: showTanksTemps ? bottomOffset + 3 : 5 + } + + values: + [ + TileText + { + width: parent.width + anchors + { + horizontalCenter: parent.horizontalCenter + bottom: parent.bottom; bottomMargin: 0 + } + ////// modified to show current + text: + { + if (showDcSystem) + { + var current = "" + if (sys.dcSystem.power.valid && sys.battery.voltage.valid) + current = " " + EnhFmt.formatValue (sys.dcSystem.power.value / sys.battery.voltage.value, "A") + return EnhFmt.formatVBusItem (sys.dcSystem.power) + current + } + else + return "--" + } + } + ] + PowerGauge + { + id: dcSystemGauge + width: parent.width + height: 8 + anchors + { + top: parent.top; topMargin: 19 + left: parent.left; leftMargin: 18 + right: parent.right + } + connection: sys.dcSystem + maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/DcSystemMaxLoad" + maxReversePowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/DcSystemMaxCharge" + showLabels: true + visible: showGauges && showDcSystem + + } + DetailTarget { id: dcSystemTarget; detailsPage: "DetailDcSystem.qml" } + } + + property int pvOffset1: 27 + property int pvRowSpacing: 16 + property int pvOffset2: pvOffset1 + pvRowSpacing * pvRowsPerCharger + property int pvOffset3: pvOffset2 + pvRowSpacing * pvRowsPerCharger + property int pvOffset4: pvOffset3 + pvRowSpacing * pvRowsPerCharger + property int pvOffset5: pvOffset4 + pvRowSpacing * pvRowsPerCharger + property int pvOffset6: pvOffset5 + pvRowSpacing * pvRowsPerCharger + property int pvOffset7: pvOffset6 + pvRowSpacing * pvRowsPerCharger + +////// replaced OverviewSolarCharger with OverviewBox + OverviewBox { + id: pvChargerBox + title: qsTr("PV Charger") +////// GuiMods — DarkMode + titleColor: !darkMode ? "#F4B350" : "#7A5928" + color: !darkMode ? "#F39C12" : "#794E09" + visible: hasDcSolar || showInactiveTiles + opacity: hasDcSolar ? 1 : disabledTileOpacity + +////// MODIFIED to show tanks & provide extra space if not + height: + { + var availableHeight = root.height - 3 - acLoadBox.height - 5 - (showTanksTemps ? bottomOffset + 3 : 5) + if (showDcAndAcSolar) + return ((availableHeight - 5) / 2) + 4 + else if (showDcSolar) + return availableHeight + else + return 0 + } + width: 148 + + anchors { + right: root.right; rightMargin: 10 + bottom: parent.bottom; bottomMargin: showTanksTemps ? bottomOffset + 3 : 5 + } + +////// moved sun icon here from OverviewSolarChager so it can be put below text, etc + MbIcon { + iconId: "overview-sun" + anchors { + bottom: parent.bottom + right: parent.right; rightMargin: 2 + } + opacity: 0.5 + visible: ! showDcAndAcSolar + } + +//////// modified to add power for individual PV charger info + values: + [ + TileText { + y: 8 + text: EnhFmt.formatVBusItem (sys.pvCharger.power) + font.pixelSize: 19 + }, + MarqueeEnhanced + { + y: pvOffset1 + id: pv1Name + // ofset left margin for this row if showing tanks/temps + width: + { + if (pvChargerCompact) + { + if (showTanksTemps) + return ((parent.width / 2) - 15) + else + return ((parent.width / 2) - 5) + } + else + return (parent.width - 10) + } + anchors.left: parent.left; anchors.leftMargin: (showTanksTemps && pvChargerCompact) ? 15 : 5 + height: 15 + text: pvName1.valid ? pvName1.value : "pv 1" + textHorizontalAlignment: pvChargerCompact ? Text.AlignLeft : Text.AlignHCenter + fontSize: 15 + Connections { target: scrollTimer; onTriggered: pv1Name.doScroll() } + scroll: false + visible: numberOfPvChargers >= 1 && ! showDcAndAcSolar + }, + TileText { + y: pvOffset1 + (pvChargerCompact ? 0 : pvRowSpacing) + text: EnhFmt.formatVBusItem (pvPower1, "W") + horizontalAlignment: pvChargerCompact ? Text.AlignRight : Text.AlignHCenter + anchors.right: parent.right; anchors.rightMargin: 5 + font.pixelSize: 15 + visible: numberOfPvChargers >= 1 && ! showDcAndAcSolar + }, + TileText { + y: pvOffset1 + pvRowSpacing * (pvChargerCompact ? 1 : 2) + text: + { + var voltageText, currentText + if (root.numberOfPvChargers < 1) + return " " + else + { + if (pv1NrTrackers.valid && pv1NrTrackers.value > 1) + return qsTr ("multiple trackers") + else if (pvVoltage1.valid) + voltageText = EnhFmt.formatVBusItem (pvVoltage1, "V") + else + voltageText = "??V" + if (pvCurrent1.valid) + currentText = EnhFmt.formatVBusItem (pvCurrent1, "A") + else if (pvPower1.valid) + currentText = EnhFmt.formatValue ((pvPower1.value / pvVoltage1.value), "A") + else + currentText = "??A" + return voltageText + " " + currentText + } + } + font.pixelSize: 15 + visible: pvShowDetails && numberOfPvChargers >= 1 + }, + MarqueeEnhanced + { + y: pvOffset2 + id: pv2Name + width: pvChargerCompact ? ((parent.width / 2) - 5) : parent.width - 10 + anchors.left: parent.left; anchors.leftMargin: 5 + height: 15 + text: pvName2.valid ? pvName2.value : "pv 2" + textHorizontalAlignment: pvChargerCompact ? Text.AlignLeft : Text.AlignHCenter + fontSize: 15 + Connections { target: scrollTimer; onTriggered: pv2Name.doScroll() } + scroll: false + visible: numberOfPvChargers >= 2 && ! showDcAndAcSolar + }, + TileText { + y: pvOffset2 + (pvChargerCompact ? 0 : pvRowSpacing) + text: EnhFmt.formatVBusItem (pvPower2, "W") + horizontalAlignment: pvChargerCompact ? Text.AlignRight : Text.AlignHCenter + anchors.right: parent.right; anchors.rightMargin: 5 + font.pixelSize: 15 + visible: numberOfPvChargers >= 2 && ! showDcAndAcSolar + }, + TileText { + y: pvOffset2 + pvRowSpacing * (pvChargerCompact ? 1 : 2) + text: + { + var voltageText, currentText + if (root.numberOfPvChargers < 2) + return " " + else + { + if (pv2NrTrackers.valid && pv2NrTrackers.value > 1) + return qsTr ("multiple trackers") + else if (pvVoltage2.valid) + voltageText = EnhFmt.formatVBusItem (pvVoltage2, "V") + else + voltageText = "??V" + if (pvCurrent2.valid) + currentText = EnhFmt.formatVBusItem (pvCurrent2, "A") + else if (pvPower2.valid) + currentText = EnhFmt.formatValue ((pvPower2.value / pvVoltage2.value), "A") + else + currentText = "??A" + return voltageText + " " + currentText + } + } + font.pixelSize: 15 + visible: pvShowDetails && numberOfPvChargers >= 2 + }, + MarqueeEnhanced + { + y: pvOffset3 + id: pv3Name + width: pvChargerCompact ? ((parent.width / 2) - 5) : parent.width - 10 + anchors.left: parent.left; anchors.leftMargin: 5 + height: 15 + text: pvName3.valid ? pvName3.value : "pv 3" + textHorizontalAlignment: pvChargerCompact ? Text.AlignLeft : Text.AlignHCenter + fontSize: 15 + Connections { target: scrollTimer; onTriggered: pv3Name.doScroll() } + scroll: false + visible: numberOfPvChargers >= 3 && ! showDcAndAcSolar + }, + TileText { + y: pvOffset3 + (pvChargerCompact ? 0 : pvRowSpacing) + text: EnhFmt.formatVBusItem (pvPower3, "W") + anchors.right: parent.right; anchors.rightMargin: 5 + horizontalAlignment: pvChargerCompact ? Text.AlignRight : Text.AlignHCenter + font.pixelSize: 15 + visible: numberOfPvChargers >= 3 && ! showDcAndAcSolar + }, + TileText { + y: pvOffset3 + pvRowSpacing * (pvChargerCompact ? 1 : 2) + text: + { + var voltageText, currentText + if (root.numberOfPvChargers < 3) + return " " + else + { + if (pv3NrTrackers.valid && pv3NrTrackers.value > 1) + return qsTr ("multiple trackers") + else if (pvVoltage3.valid) + voltageText = EnhFmt.formatVBusItem (pvVoltage3, "V") + else + voltageText = "??V" + if (pvCurrent3.valid) + currentText = EnhFmt.formatVBusItem (pvCurrent3, "A") + else if (pvPower3.valid) + currentText = EnhFmt.formatValue ((pvPower3.value / pvVoltage3.value), "A") + else + currentText = "??A" + return voltageText + " " + currentText + } + } + font.pixelSize: 15 + visible: pvShowDetails && numberOfPvChargers >= 2 + }, + MarqueeEnhanced + { + y: pvOffset4 + id: pv4Name + // ofset left margin for this row if NOT showing tanks/temps + width: + { + if (pvChargerCompact) + { + if (! showTanksTemps) + return ((parent.width / 2) - 15) + else + return ((parent.width / 2) - 5) + } + else + return (parent.width - 10) + } + anchors.left: parent.left; anchors.leftMargin: ( ! showTanksTemps && pvChargerCompact) ? 15 : 5 + height: 15 + text: pvName4.valid ? pvName4.value : "pv 4" + textHorizontalAlignment: pvChargerCompact ? Text.AlignLeft : Text.AlignHCenter + fontSize: 15 + Connections { target: scrollTimer; onTriggered: pv4Name.doScroll() } + scroll: false + visible: numberOfPvChargers >= 4 && ! showDcAndAcSolar + }, + TileText { + y: pvOffset4 + (pvChargerCompact ? 0 : pvRowSpacing) + text: EnhFmt.formatVBusItem (pvPower4, "W") + anchors.right: parent.right; anchors.rightMargin: 5 + horizontalAlignment: pvChargerCompact ? Text.AlignRight : Text.AlignHCenter + font.pixelSize: 15 + visible: numberOfPvChargers >= 4 && ! showDcAndAcSolar + }, + MarqueeEnhanced + { + y: pvOffset5 + id: pv5Name + width: pvChargerCompact ? ((parent.width / 2) - 5) : parent.width - 10 + anchors.left: parent.left; anchors.leftMargin: 5 + height: 15 + text: pvName5.valid ? pvName5.value : "pv 5" + textHorizontalAlignment: pvChargerCompact ? Text.AlignLeft : Text.AlignHCenter + fontSize: 15 + Connections { target: scrollTimer; onTriggered: pv5Name.doScroll() } + scroll: false + visible: numberOfPvChargers >= 5 && pvChargerRows >= 5 && ! showDcAndAcSolar + }, + TileText { + y: pvOffset5 + (pvChargerCompact ? 0 : pvRowSpacing) + text: EnhFmt.formatVBusItem (pvPower5, "W") + anchors.right: parent.right; anchors.rightMargin: 5 + horizontalAlignment: pvChargerCompact ? Text.AlignRight : Text.AlignHCenter + font.pixelSize: 15 + visible: numberOfPvChargers >= 5 && pvChargerRows >= 5 && ! showDcAndAcSolar + }, + MarqueeEnhanced + { + y: pvOffset6 + id: pv6Name + width: pvChargerCompact ? ((parent.width / 2) - 5) : parent.width - 10 + anchors.left: parent.left; anchors.leftMargin: 5 + height: 15 + text: pvName6.valid ? pvName6.value : "pv 6" + textHorizontalAlignment: pvChargerCompact ? Text.AlignLeft : Text.AlignHCenter + fontSize: 15 + Connections { target: scrollTimer; onTriggered: pv6Name.doScroll() } + scroll: false + visible: numberOfPvChargers >= 6 && pvChargerRows >= 6 && ! showDcAndAcSolar + }, + TileText { + y: pvOffset6 + (pvChargerCompact ? 0 : pvRowSpacing) + text: EnhFmt.formatVBusItem (pvPower6, "W") + anchors.right: parent.right; anchors.rightMargin: 5 + horizontalAlignment: pvChargerCompact ? Text.AlignRight : Text.AlignHCenter + font.pixelSize: 15 + visible: numberOfPvChargers >= 6 && pvChargerRows >= 6 && ! showDcAndAcSolar + }, + MarqueeEnhanced + { + y: pvOffset7 + id: pv7Name + width: pvChargerCompact ? ((parent.width / 2) - 5) : parent.width - 10 + anchors.left: parent.left; anchors.leftMargin: 5 + height: 15 + text: pvName7.valid ? pvName7.value : "pv 7" + textHorizontalAlignment: pvChargerCompact ? Text.AlignLeft : Text.AlignHCenter + fontSize: 15 + Connections { target: scrollTimer; onTriggered: pv6Name.doScroll() } + scroll: false + visible: numberOfPvChargers >= 7 && pvChargerRows >= 7 && ! showDcAndAcSolar + }, + TileText { + y: pvOffset7 + (pvChargerCompact ? 0 : pvRowSpacing) + text: EnhFmt.formatVBusItem (pvPower7, "W") + anchors.right: parent.right; anchors.rightMargin: 5 + horizontalAlignment: pvChargerCompact ? Text.AlignRight : Text.AlignHCenter + font.pixelSize: 15 + visible: numberOfPvChargers >= 7 && pvChargerRows >= 7 && ! showDcAndAcSolar + } + ] +////// add power bar graph + PowerGauge + { + id: pvChargerBar + width: parent.width - (showDcAndAcSolar && ! showTanksTemps ? 20 : 0) + height: 10 + anchors + { + top: parent.top; topMargin: 19 + right: parent.right; rightMargin: 0.5 + } + connection: sys.pvCharger + maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/PvChargerMaxPower" + visible: showGauges && showDcSolar + } + DetailTarget { id: pvChargerTarget; detailsPage: "DetailPvCharger.qml" } + } + +////// replaced OverviewSolarInverter with OverviewBox + OverviewBox { + id: pvInverter + title: qsTr("PV Inverter") +////// GuiMods — DarkMode + titleColor: !darkMode ? "#F4B350" : "#7A5928" + color: !darkMode ? "#F39C12" : "#794E09" + visible: hasAcSolar || showInactiveTiles + opacity: hasAcSolar ? 1 : disabledTileOpacity + +////// MODIFIED to show tanks & provide extra space if not + height: + { + var availableHeight = root.height - 3 - acLoadBox.height -5 + availableHeight -= (showTanksTemps ? bottomOffset + 3 : 5) + if (showDcAndAcSolar) + availableHeight -= pvChargerBox.height + 5 + if (showAcSolar) + return availableHeight + else + return 0 + } + width: 148 + + anchors { + right: root.right; rightMargin: 10; + bottom: showDcAndAcSolar ? pvChargerBox.top : root.bottom + bottomMargin: showDcAndAcSolar ? 5 : showTanksTemps ? bottomOffset + 3 : 5 + } + + values: + [ + TileText { + id: coupledPvAc + + property double pvInverterOnAcOut: sys.pvOnAcOut.power.valid ? sys.pvOnAcOut.power.value : 0 + property double pvInverterOnAcIn1: sys.pvOnAcIn1.power.valid ? sys.pvOnAcIn1.power.value : 0 + property double pvInverterOnAcIn2: sys.pvOnAcIn2.power.valid ? sys.pvOnAcIn2.power.value : 0 + property bool powerValid: sys.pvOnAcOut.power.valid || sys.pvOnAcIn1.power.valid || sys.pvOnAcIn2.power.valid + + y: 10 + text: powerValid ? EnhFmt.formatValue (pvInverterOnAcOut + pvInverterOnAcIn1 + pvInverterOnAcIn2, "W") : "" + font.pixelSize: 19 + visible: showAcSolar + }, +//////// add individual PV inverter powers + TileText { + y: 31 + text: pvInverterName1.valid ? pvInverterName1.value : "-" + visible: !showDcAndAcSolar && numberOfPvInverters >= 2 + }, + TileText { + y: 47 + text: EnhFmt.formatVBusItem (pvInverterPower1, "W") + font.pixelSize: 15 + visible: !showDcAndAcSolar && numberOfPvInverters >= 2 + }, + TileText { + y: 63 + text: pvInverterName2.valid ? pvInverterName2.value : "-" + visible: !showDcAndAcSolar && numberOfPvInverters >= 2 + }, + TileText { + y: 77 + text: EnhFmt.formatVBusItem (pvInverterPower2, "W") + font.pixelSize: 15 + visible: !showDcAndAcSolar && numberOfPvInverters >= 2 + }, + TileText { + y: 93 + text: pvInverterName3.valid ? pvInverterName3.value : "-" + visible: !showDcAndAcSolar && numberOfPvInverters >=3 && ! showTanksTemps + }, + TileText { + y: 107 + text: EnhFmt.formatVBusItem (pvInverterPower3, "W") + font.pixelSize: 15 + visible: !showDcAndAcSolar && numberOfPvInverters >=3 && ! showTanksTemps + }, + TileText { + y: 31 + text: qsTr ("L1: ") + EnhFmt.formatVBusItem (pvInverterL1Power1, "W") + visible: !showDcAndAcSolar && numberOfPvInverters == 1 && pvInverterL1Power1.valid && (pvInverterL2Power1.valid || pvInverterL3Power1.valid) + }, + TileText { + y: 47 + text: qsTr ("L2: ") + EnhFmt.formatVBusItem (pvInverterL2Power1, "W") + visible: !showDcAndAcSolar && numberOfPvInverters == 1 && pvInverterL2Power1.valid + }, + TileText { + y: 63 + text: qsTr ("L3: ") + EnhFmt.formatVBusItem (pvInverterL3Power1, "W") + visible: !showDcAndAcSolar && numberOfPvInverters == 1 && pvInverterL3Power1.valid + } + ] +////// add power bar graph + PowerGauge + { + id: pvInverterBar + width: parent.width + height: 12 + anchors + { + top: parent.top; topMargin: 19 + horizontalCenter: parent.horizontalCenter + } + maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/PvOnOutputMaxPower" + maxForwardPowerParameter2: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/PvOnGridMaxPower" + connection: sys.pvOnAcOut + connection2: sys.pvOnGrid + visible: showGauges && showAcSolar + } + DetailTarget { id: pvInverterTarget; detailsPage: "DetailPvInverter.qml" } + } + + OverviewConnection { + id: acInToMulti + ballCount: 2 + path: straight + active: root.active && showAcInput && showInverter + value: flow(sys.acInput ? sys.acInput.power : 0) + + anchors { + left: acInBox.right; leftMargin: -10 + right: multi.left; rightMargin: -10; bottom: acInBox.bottom; bottomMargin: 25 + } + } + + OverviewConnection { + id: multiToAcLoads + ballCount: 2 + path: straight + active: root.active && ( showAcLoads && showInverter ) + value: flow(sys.acLoad.power) + + anchors { + left: multi.right; leftMargin: -10; + right: acLoadBox.left; rightMargin: -10 + bottom: acLoadBox.bottom; bottomMargin: 8 + } + } + + OverviewConnection { + id: pvInverterToMulti + ballCount: 3 + path: corner + active: root.active && showAcSolar && showInverter + value: Utils.sign(noNoise(sys.pvOnAcOut.power) + noNoise(sys.pvOnAcIn1.power) + noNoise(sys.pvOnAcIn2.power)) + + anchors { + left: pvInverter.left; leftMargin: 8 + top: pvInverter.verticalCenter; topMargin: showDcAndAcSolar ? 10 : 0 + right: multi.horizontalCenter; rightMargin: -20 + bottom: multi.bottom; bottomMargin: 10 + } + } + + // invisible anchor point to connect the chargers to the battery + Item { + id: dcConnect + anchors { + left: multi.horizontalCenter; leftMargin: showAcSolar ? -20 : 0 + bottom: dcSystemBox.top; bottomMargin: showDcAndAcSolar ? 7 : 10 + } + } + + OverviewConnection + { + id: dcBus2 + ballCount: 2 + path: straight + active: root.active && ( showInverter || showDcSolar ) + value: -Utils.sign (noNoise (sys.pvCharger.power) + noNoise (sys.inverterChargerDc.power)) + startPointVisible: false + endPointVisible: false + + anchors { + right: dcConnect.left + top: dcConnect.top + + left: multi.left; leftMargin: -10 + bottom: dcConnect.top + } + } + + OverviewConnection + { + id: alternatorToDcBus2 + ballCount: 3 + path: corner + active: root.active && showAlternator + value: Utils.sign (alternatorFlow) + endPointVisible: false + anchors + { + left: alternatorBox.right; leftMargin: -10 + top: alternatorBox.bottom; topMargin: -15 + + right: dcBus2.left + bottom: dcBus2.bottom + } + } + + OverviewConnection { + id: multiToDcConnect + ballCount: showTanksTemps ? 2 : 4 + path: straight + active: root.active && showInverter + value: -flow(sys.inverterChargerDc.power); + startPointVisible: false + + anchors { + left: dcConnect.left + top: dcConnect.top + + right: dcConnect.left + bottom: multi.bottom; bottomMargin: 10 + } + } + + OverviewConnection { + id: pvChargerBoxDcConnect + ballCount: 3 + path: straight + active: root.active && showDcSolar + value: -flow(sys.pvCharger.power) + startPointVisible: false + + anchors { + left: dcConnect.left + top: dcConnect.top + + right: pvChargerBox.left; rightMargin: -8 + bottom: dcConnect.top; + } + } + + OverviewConnection { + id: batteryToDcBus2 + ballCount: 1 + path: straight + active: root.active && ( showInverter || showDcSolar ) + value: Utils.sign(noNoise(sys.pvCharger.power) + noNoise(sys.inverterChargerDc.power) + alternatorFlow) + startPointVisible: false + + anchors { + left: dcBus2.left + top: dcBus2.top + + right: battery.right; rightMargin: 10 + bottom: dcBus2.top + } + } + + OverviewConnection { + id: batteryToDcSystem + ballCount: 2 + path: straight + active: root.active && showDcSystem + value: flow(sys.dcSystem.power) + + anchors { + left: battery.right; leftMargin: -10 + top: dcSystemBox.verticalCenter; + right: dcSystemBox.left; rightMargin: -10 + bottom: dcSystemBox.verticalCenter + } + } +////// moved order so it covers connections +////// moved to under Multi + OverviewEssReason { + anchors { + top: multi.bottom; topMargin: 7 + horizontalCenter: parent.horizontalCenter + } + } + +////// ADDED to show tanks & temps + // Synchronise tank name text scroll start and PV Charger name scroll + Timer + { + id: scrollTimer + interval: 15000 + repeat: true + running: root.active + } + ListView + { + id: tanksColum + + visible: showTanks + width: compact ? root.width : root.width * tankCount / tankTempCount + property int tileWidth: width / Math.min (count, 5.2) + height: root.tanksHeight + anchors + { + bottom: root.bottom + left: root.left + } + + // flickable list if more than will fit across bottom of screen + interactive: count > 4 ? true : false + orientation: ListView.Horizontal + + model: TankModel { id: tankModel } + delegate: TileTankEnhanced { + // Without an intermediate assignment this will trigger a binding loop warning. + property variant theService: DBusServices.get(buddy.id) + service: theService + width: tanksColum.tileWidth + height: root.tanksHeight + pumpBindPrefix: root.pumpBindPreffix + compact: root.compact + Connections { + target: scrollTimer + onTriggered: doScroll() + } + } + Tile { + title: qsTr("TANKS") + anchors.fill: parent + values: TileText { + text: qsTr("") + width: parent.width + wrapMode: Text.WordWrap + } + z: -1 + } + } + + ListView + { + id: tempsColumn + + visible: showTemps + width: compact ? root.width : root.width * tempCount / tankTempCount + property int tileWidth: width / Math.min (count, 5.2) + height: root.tanksHeight + anchors + { + bottom: root.bottom + bottomMargin: compact ? root.tanksHeight : 0 + right: root.right + } + + // make list flickable if more tiles than will fit completely + interactive: count > 4 ? true : false + orientation: ListView.Horizontal + + model: tempsModel + delegate: TileTemp + { + width: tempsColumn.tileWidth + height: tempsColumn.height + compact: root.compact + Connections + { + target: scrollTimer + onTriggered: doScroll() + } + } + Tile + { + title: qsTr("TEMPS") + anchors.fill: parent + values: TileText + { + text: qsTr("") + width: parent.width + wrapMode: Text.WordWrap + } + z: -1 + } + } + ListModel { id: tempsModel } + + // When new service is found add resources as appropriate + Connections + { + target: DBusServices + onDbusServiceFound: addService(service) + } + + // hack to get value(s) from within a loop inside a function when service is changing + property string tempServiceName: "" + property VBusItem temperatureItem: VBusItem { bind: Utils.path(tempServiceName, "/Dc/0/Temperature") } + + function addService(service) + { + switch (service.type) + { +//////// add for temp sensors + case DBusService.DBUS_SERVICE_TEMPERATURE_SENSOR: + numberOfTemps++ + tempsModel.append({serviceName: service.name}) + break;; + case DBusService.DBUS_SERVICE_MULTI: + hasInverter = true + root.tempServiceName = service.name + if (temperatureItem.valid && showBatteryTemp) + { + numberOfTemps++ + tempsModel.append({serviceName: service.name}) + } + break;; +//////// add for VE.Direct inverters + case DBusService.DBUS_SERVICE_INVERTER: + hasInverter = true + if (veDirectInverterService == "") + veDirectInverterService = service.name; + break;; + +//////// add for PV CHARGER voltage and current display + case DBusService.DBUS_SERVICE_SOLAR_CHARGER: + case DBusService.DBUS_SERVICE_MULTI_RS: + if ( service.type == DBusService.DBUS_SERVICE_MULTI_RS ) + hasInverter = true + numberOfPvChargers++ + if (numberOfPvChargers === 1) + pvChargerPrefix1 = service.name; + else if (numberOfPvChargers === 2) + pvChargerPrefix2 = service.name; + else if (numberOfPvChargers === 3) + pvChargerPrefix3 = service.name; + else if (numberOfPvChargers === 4) + pvChargerPrefix4 = service.name; + else if (numberOfPvChargers === 5) + pvChargerPrefix5 = service.name; + else if (numberOfPvChargers === 6) + pvChargerPrefix6 = service.name; + else if (numberOfPvChargers === 7) + pvChargerPrefix7 = service.name; + break;; + +//////// add for PV INVERTER power display + case DBusService.DBUS_SERVICE_PV_INVERTER: + numberOfPvInverters++ + if (numberOfPvInverters === 1) + pvInverterPrefix1 = service.name; + else if (numberOfPvInverters === 2) + pvInverterPrefix2 = service.name; + else if (numberOfPvInverters === 3) + pvInverterPrefix3 = service.name; + break;; + case DBusService.DBUS_SERVICE_BATTERY: + root.tempServiceName = service.name + if (temperatureItem.valid && showBatteryTemp) + { + numberOfTemps++ + tempsModel.append({serviceName: service.name}) + } + break;; +//////// add for alternator + case DBusService.DBUS_SERVICE_ALTERNATOR: + numberOfAlternators++ + if (numberOfAlternators === 1) + alternatorPrefix1 = service.name; + else if (numberOfAlternators === 2) + alternatorPrefix2 = service.name; + break;; + } + } + + // Detect available services of interest + function discoverServices() + { + numberOfTemps = 0 + numberOfPvChargers = 0 + numberOfPvInverters = 0 + numberOfAlternators = 0 + veDirectInverterService = "" + hasInverter = false + pvChargerPrefix1 = "" + pvChargerPrefix2 = "" + pvChargerPrefix3 = "" + pvChargerPrefix4 = "" + pvChargerPrefix5 = "" + pvChargerPrefix6 = "" + pvChargerPrefix7 = "" + pvInverterPrefix1 = "" + pvInverterPrefix2 = "" + pvInverterPrefix3 = "" + alternatorPrefix1 = "" + alternatorPrefix2 = "" + tempsModel.clear() + for (var i = 0; i < DBusServices.count; i++) + { + addService(DBusServices.at(i)) + } + } + +// Details targets + + // help message shown when menu is first drawn + Rectangle + { + id: helpBox + color: "white" + width: multi.width + height: 32 +////// GuiMods — DarkMode + opacity: !darkMode ? 0.7 : 0.85 + anchors + { + top: multi.bottom; topMargin: 1 + horizontalCenter: root.horizontalCenter + } + visible: false + TileText + { + text: qsTr ( "Tap tile center for detail at any time" ) + color: "black" + anchors.fill: helpBox + wrapMode: Text.WordWrap + font.pixelSize: 12 + visible: parent.visible + } + } + + //// hard key handler + // used to press buttons when touch isn't available + // UP and DOWN buttons cycle through the list of touch areas + // "space" button is used to simulate a touch on the area + // target must be highlighted so that other uses of "space" + // will still occur + + // list of all details touchable areas + property variant targetList: + [ + acInputTarget, alternatorTarget, batteryTarget, + multiTarget, dcSystemTarget, + loadsOnOutputTarget, pvInverterTarget, pvChargerTarget + ] + + property int selectedTarget: 0 + + Timer + { + id: targetTimer + interval: 5000 + repeat: false + running: false + onTriggered: { hideAllTargets () } + } + + Keys.forwardTo: [keyHandler] + Item + { + id: keyHandler + Keys.onUpPressed: + { + nextTarget (-1) + event.accepted = true + } + + Keys.onDownPressed: + { + nextTarget (+1) + event.accepted = true + } + Keys.onSpacePressed: + { + if (targetTimer.running) + { + var foo // hack to make clicked() work + bar.clicked (foo) + event.accepted = true + } + else + event.accepted = false + } + } + // hack to make clicked() work + property variant bar: targetList[selectedTarget] + + function nextTarget (increment) + { + // make one pass through all possible targets to find an enabled one + // if found, that's the new selectedTarget, + // if not selectedTarget does not change + var newIndex = selectedTarget + for (var i = 0; i < targetList.length; i++) + { + if (( ! targetTimer.running || helpBox.visible) && targetList[newIndex].enabled) + { + highlightSelectedTarget () + return + } + newIndex += increment + if (newIndex >= targetList.length) + newIndex = 0 + else if (newIndex < 0) + newIndex = targetList.length - 1 + if (targetList[newIndex].enabled) + { + selectedTarget = newIndex + highlightSelectedTarget () + break + } + } + } + + function showHelp () + { + for (var i = 0; i < targetList.length; i++) + { + targetList[i].targetVisible = true + } + helpBox.visible = true + targetTimer.restart () + } + function hideAllTargets () + { + for (var i = 0; i < targetList.length; i++) + { + targetList[i].targetVisible = false + } + helpBox.visible = false + } + function highlightSelectedTarget () + { + for (var i = 0; i < targetList.length; i++) + { + if (targetList[i] == targetList[selectedTarget]) + targetList[i].targetVisible = true + else + targetList[i].targetVisible = false + } + helpBox.visible = false + targetTimer.restart () + } +} diff --git a/FileSets/v3.60~25/OverviewHubEnhanced.qml.orig b/FileSets/v3.60~25/OverviewHubEnhanced.qml.orig new file mode 100644 index 00000000..d6b85835 --- /dev/null +++ b/FileSets/v3.60~25/OverviewHubEnhanced.qml.orig @@ -0,0 +1,311 @@ +import QtQuick 2 +import "utils.js" as Utils + +OverviewPage { + id: root + + property variant sys: theSystem + property bool hasAcSolarOnAcIn1: sys.pvOnAcIn1.power.valid + property bool hasAcSolarOnAcIn2: sys.pvOnAcIn2.power.valid + property bool hasAcSolarOnIn: hasAcSolarOnAcIn1 || hasAcSolarOnAcIn2 + property bool hasAcSolarOnOut: sys.pvOnAcOut.power.valid + property bool hasAcSolar: hasAcSolarOnIn || hasAcSolarOnOut + property bool hasDcSolar: sys.pvCharger.power.valid + property bool hasDcAndAcSolar: hasAcSolar && hasDcSolar + + title: qsTr("Overview") + + OverviewBox { + id: acInBox + + width: 148 + height: showStatusBar ? 100 : 120 + title: getAcSourceName(sys.acSource) + titleColor: "#E74c3c" + color: "#C0392B" + + anchors { + top: multi.top + left: parent.left; leftMargin: 10 + } + + values: OverviewAcValues { + connection: sys.acInput + } + + MbIcon { + iconId: getAcSourceIcon(sys.acSource) + anchors { + bottom: parent.bottom + left: parent.left; leftMargin: 2 + } + opacity: 0.5 + } + } + + Multi { + id: multi + anchors { + horizontalCenter: parent.horizontalCenter + top: parent.top; topMargin: 5 + } + } + + OverviewBox { + id: acLoadBox + title: qsTr("AC Loads") + color: "#27AE60" + titleColor: "#2ECC71" + width: 148 + height: showStatusBar ? 100 : 120 + + anchors { + right: parent.right; rightMargin: 10 + top: multi.top + } + + values: OverviewAcValues { + connection: sys.acLoad + } + } + + Battery { + id: battery + + soc: sys.battery.soc.valid ? sys.battery.soc.value : 0 + preferRenewable: sys.preferRenewableEnergy.valid + preferRenewableOverride: sys.preferRenewableEnergy.value === 0 || sys.preferRenewableEnergy.value === 2 + preferRenewableOverrideGenset: sys.remoteGeneratorSelected.value === 1 || sys.acSource.value === 2 + anchors { + bottom: parent.bottom; bottomMargin: 5; + left: parent.left; leftMargin: 10 + } + values: Column { + width: parent.width + + TileText { + // Use value here instead of format() because format adds the unit to the number and we + // show the percentage symbol in a separated smaller text. + text: sys.battery.soc.value === undefined ? "--" : sys.battery.soc.value.toFixed(0) + font.pixelSize: 40 + + Text { + anchors { + bottom: parent.bottom; bottomMargin: 9 + horizontalCenter: parent.horizontalCenter; horizontalCenterOffset: parent.paintedWidth / 2 + 5 + } + visible: sys.battery.soc.valid + text: "%" + color: "white" + font.bold: true + font.pixelSize: 12 + } + } + TileText { + text: sys.battery.power.format(0) + } + TileText { + text: sys.battery.voltage.format(1) + " " + sys.battery.current.format(1) + } + } + } + + OverviewBox { + id: dcSystemBox + width: 105 + height: 45 + visible: sys.dcSystem.power.valid + title: qsTr("DC Power") + + anchors { + horizontalCenter: multi.horizontalCenter + bottom: parent.bottom; bottomMargin: 5 + } + + values: TileText { + anchors.centerIn: parent + text: sys.dcSystem.power.format(0) + } + } + + OverviewSolarCharger { + id: blueSolarCharger + + height: hasDcAndAcSolar ? 65 : 114 + width: 148 + title: qsTr("PV Charger") + showChargerIcon: !hasDcAndAcSolar + visible: hasDcSolar || hasDcAndAcSolar + + anchors { + right: root.right; rightMargin: 10 + bottom: root.bottom; bottomMargin: 5; + } + + values: TileText { + y: 5 + text: sys.pvCharger.power.format(0) + font.pixelSize: 20 + } + } + + OverviewSolarInverter { + id: pvInverter + height: hasDcAndAcSolar ? 65 : 115 + width: 148 + title: qsTr("PV Inverter") + showInverterIcon: !hasDcAndAcSolar + visible: hasAcSolar + + anchors { + right: root.right; rightMargin: 10; + bottom: root.bottom; bottomMargin: hasDcAndAcSolar ? 75 : 5 + } + + OverviewAcValues { + connection: hasAcSolarOnOut ? sys.pvOnAcOut : hasAcSolarOnAcIn1 ? sys.pvOnAcIn1 : sys.pvOnAcIn2 + visible: !coupledPvAc.visible + } + + TileText { + id: coupledPvAc + + property double pvInverterOnAcOut: sys.pvOnAcOut.power.valid ? sys.pvOnAcOut.power.value : 0 + property double pvInverterOnAcIn1: sys.pvOnAcIn1.power.valid ? sys.pvOnAcIn1.power.value : 0 + property double pvInverterOnAcIn2: sys.pvOnAcIn2.power.valid ? sys.pvOnAcIn2.power.value : 0 + + y: 5 + text: (pvInverterOnAcOut + pvInverterOnAcIn1 + pvInverterOnAcIn2).toFixed(0) + "W" + font.pixelSize: hasDcAndAcSolar ? 20 : 25 + visible: hasDcAndAcSolar || (hasAcSolarOnIn && hasAcSolarOnOut) || (hasAcSolarOnAcIn1 && hasAcSolarOnAcIn2) + } + } + + OverviewEssReason { + anchors { + bottom: parent.bottom; bottomMargin: dcSystemBox.visible ? battery.height + 15 : 5 + horizontalCenter: parent.horizontalCenter; horizontalCenterOffset: dcSystemBox.visible ? -(root.width / 2 - battery.width / 2 - 10) : 0 + } + } + + OverviewConnection { + id: acInToMulti + ballCount: 2 + path: straight + active: root.active + value: flow(sys.acInput ? sys.acInput.power : 0) + + anchors { + left: acInBox.right; leftMargin: -10; top: multi.verticalCenter; + right: multi.left; rightMargin: -10; bottom: multi.verticalCenter + } + } + + OverviewConnection { + id: multiToAcLoads + ballCount: 2 + path: straight + active: root.active + value: flow(sys.acLoad.power) + + anchors { + left: multi.right; leftMargin: -10; + top: multi.verticalCenter + right: acLoadBox.left; rightMargin: -10 + bottom: multi.verticalCenter + } + } + + OverviewConnection { + id: pvInverterToMulti + + property int hasDcAndAcFlow: Utils.sign(noNoise(sys.pvOnAcOut.power) + noNoise(sys.pvOnAcIn1.power) + noNoise(sys.pvOnAcIn2.power)) + + ballCount: 4 + path: corner + active: root.active && hasAcSolar + value: hasDcAndAcSolar ? hasDcAndAcFlow : flow(sys.pvOnAcOut.power) + + anchors { + left: pvInverter.left; leftMargin: 8 + top: pvInverter.verticalCenter; topMargin: hasDcAndAcSolar ? 1 : 0 + right: multi.horizontalCenter; rightMargin: -20 + bottom: multi.bottom; bottomMargin: 10 + } + } + + // invisible anchor point to connect the chargers to the battery + Item { + id: dcConnect + anchors { + left: multi.horizontalCenter; leftMargin: hasAcSolar ? -20 : 0 + bottom: dcSystemBox.top; bottomMargin: 10 + } + } + + OverviewConnection { + id: multiToDcConnect + ballCount: 3 + path: straight + active: root.active + value: -flow(sys.inverterChargerDc.power); + startPointVisible: false + + anchors { + left: dcConnect.left + top: dcConnect.top + + right: dcConnect.left + bottom: multi.bottom; bottomMargin: 10 + } + } + + OverviewConnection { + id: blueSolarChargerDcConnect + ballCount: 3 + path: straight + active: root.active && hasDcSolar + value: -flow(sys.pvCharger.power) + startPointVisible: false + + anchors { + left: dcConnect.left + top: dcConnect.top + + right: blueSolarCharger.left; rightMargin: -8 + bottom: dcConnect.top; + } + } + + OverviewConnection { + id: chargersToBattery + ballCount: 3 + path: straight + active: root.active + value: Utils.sign(noNoise(sys.pvCharger.power) + noNoise(sys.inverterChargerDc.power)) + startPointVisible: false + + anchors { + left: dcConnect.left + top: dcConnect.top + + right: battery.right; rightMargin: 10 + bottom: dcConnect.top + } + } + + OverviewConnection { + id: batteryToDcSystem + ballCount: 2 + path: straight + active: root.active && sys.dcSystem.power.valid + value: flow(sys.dcSystem.power) + + anchors { + left: battery.right; leftMargin: -10 + top: dcSystemBox.verticalCenter; + right: dcSystemBox.left; rightMargin: -10 + bottom: dcSystemBox.verticalCenter + } + } +} diff --git a/FileSets/v3.60~25/OverviewMobileEnhanced.qml b/FileSets/v3.60~25/OverviewMobileEnhanced.qml new file mode 100644 index 00000000..ceeeffc5 --- /dev/null +++ b/FileSets/v3.60~25/OverviewMobileEnhanced.qml @@ -0,0 +1,989 @@ +// GuiMods Enhancements to OverviewMobile screen + +// Removed logo and added AC INPUT and SYSTEM tiles originally displayed on other overviews +// Added voltage, current and frequency to AC INPUT and AC LOADS tiles +// Added source (Grid, Generator, Shore Power) to AC INPUT tile +// Replaced to/from battery with current in DC SYSTEM tile +// DC SYSTEM tile title now reflects direction: "DC LOADS, DC CHARGER" +// Rearranged tiles to match a left to right signal flow : sources on left, loads on right +// Standardized "info" tile sizes to 1 or 1.5 wide x 1 or 2 high +// infoArea defines usable space for info tiles and all tiles are a child of infoArea +// (makes repositioning easier than when they were in separate column objects) +// Large text for main paremeter in each tile has been reduced in size to allow more parameters without +// expanding tile height (30 to 22) +// merged SYSTEM and STATUS tiles +// removed speed from STATUS to reduce tile height +// hide "reason" text if it's blank to save space +// changed clock to 12-hour format +// Capitialized battery state: "Idle", "Charging", "Discharging" +// errors and notificaitons in SYSTEM/STATUS tile may push clock off bottom of tile +// Tile content for items that are not present are made invisible - tile remains in place +// that is no height adjustments when a tile provides no information +// Adjust button widths so that pump button fits within tank column +// Hide pump button when not enabled giving more room for tanks +// Add temperature sensors to tanks column +// add control of VE.Direct inverters + +// Includes changes to handle SeeLevel NMEA2000 tank sensor: +// Ignore the real incoming tank dBus service because it's information changes +// Changes in TileText.qml are also part of the TankRepeater package + +// Search for //////// to find changes + +import QtQuick 2 +import com.victron.velib 1.0 +import "utils.js" as Utils +import "timeToGo.js" as TTG +import "enhancedFormat.js" as EnhFmt + +OverviewPage { + title: qsTr("Mobile") + id: root + + property color detailColor: "#b3b3b3" + property real touchTargetOpacity: 0.3 + property int touchArea: 40 + + property variant sys: theSystem + property string settingsBindPreffix: "com.victronenergy.settings" + property string pumpBindPreffix: "com.victronenergy.pump.startstop0" + property variant activeNotifications: NotificationCenter.notifications.filter( + function isActive(obj) { return obj.active} ) + property string noAdjustableByDmc: qsTr("This setting is disabled when a Digital Multi Control " + + "is connected. If it was recently disconnected execute " + + "\"Redetect system\" that is available on the inverter menu page.") + property string noAdjustableByBms: qsTr("This setting is disabled when a VE.Bus BMS " + + "is connected. If it was recently disconnected execute " + + "\"Redetect system\" that is available on the inverter menu page.") + property string noAdjustableTextByConfig: qsTr("This setting is disabled. " + + "Possible reasons are \"Overruled by remote\" is not enabled or " + + "an assistant is preventing the adjustment. Please, check " + + "the inverter configuration with VEConfigure.") + +//////// added to keep track of tanks and temps + property int numberOfTemps: 0 + property int tankTempCount: tankModel.rowCount + numberOfTemps + property real tanksTempsHeight: root.height - (pumpButton.pumpEnabled ? pumpButton.height : 0) + property real tanksHeight: tankModel.rowCount > 0 ? tanksTempsHeight * tankModel.rowCount / tankTempCount : 0 + property real tempsHeight: tanksTempsHeight - tanksHeight + property real minimumTankHeight: 21 + property real maxTankHeight: 80 + property real tankTileHeight: Math.min (Math.max (tanksTempsHeight / tankTempCount, minimumTankHeight), maxTankHeight) + + property bool compact: tankTempCount > (pumpButton.pumpEnabled ? 5 : 6) + + property string systemPrefix: "com.victronenergy.system" + VBusItem { id: vebusService; bind: Utils.path(systemPrefix, "/VebusService") } + property bool isMulti: vebusService.valid + property string veDirectInverterService: "" + property string inverterService: vebusService.valid ? vebusService.value : veDirectInverterService + + property bool isInverter: ! isMulti && veDirectInverterService != "" + property bool hasAcInput: isMulti + VBusItem { id: _hasAcOutSystem; bind: "com.victronenergy.settings/Settings/SystemSetup/HasAcOutSystem" } + property bool hasAcOutSystem: _hasAcOutSystem.value === 1 + +//////// add for system state + property bool hasSystemState: _systemState.valid + +//////// add for SYSTEM tile and voltage, power and frequency values + property VBusItem _systemState: VBusItem { bind: Utils.path(systemPrefix, "/SystemState/State") } +//////// add for PV CHARGER voltage and current + property string pvChargerPrefix: "" + property int numberOfPvChargers: 0 + + + //////// standard tile sizes + //////// positions are left, center, right and top, center, bottom of infoArea + property int tankWidth: 130 + + property int upperTileHeight: 185 + property int acTileHeight: height - upperTileHeight + + property int infoWidth: width - tankWidth + property int infoWidth3Column: infoWidth / 3 + property int infoWidth2Column: infoWidth / 2 + +//////// add for PV Charger voltage and current + VBusItem { id: pvNrTrackers; bind: Utils.path(pvChargerPrefix, "/NrOfTrackers") } + property bool singleTracker: ! pvNrTrackers.valid || pvNrTrackers.value == 1 + property bool showPvVI: numberOfPvChargers == 1 && singleTracker + VBusItem { id: pvPower; bind: Utils.path(pvChargerPrefix, "/Yield/Power") } + VBusItem { id: pvVoltage; bind: Utils.path(pvChargerPrefix, singleTracker ? "/Pv/V" : "/Pv/0/V") } + +//////// add for inverter mode in STATUS + VBusItem { id: inverterMode; bind: Utils.path(inverterService, "/Mode") } + +//////// add for gauges + VBusItem { id: showGaugesItem; bind: Utils.path(guiModsPrefix, "/ShowGauges") } + property bool showGauges: showGaugesItem.valid ? showGaugesItem.value === 1 ? true : false : false + +//////// added to control time display + property string guiModsPrefix: "com.victronenergy.settings/Settings/GuiMods" + VBusItem { id: timeFormatItem; bind: Utils.path(guiModsPrefix, "/TimeFormat") } + property string timeFormat: getTimeFormat () + + function getTimeFormat () + { + if (!timeFormatItem.valid || timeFormatItem.value === 0) + return "" + else if (timeFormatItem.value === 2) + return "h:mm ap" + else + return "hh:mm" + } + + Component.onCompleted: { discoverServices(); showHelp () } + + // define usable space for tiles but don't show anything + Rectangle { + id: infoArea + visible: false + anchors { + left: parent.left + right: tanksColum.left + top: parent.top; + bottom: parent.bottom; + } + } + +//////// change time to selectable 12/24 hour format + Timer { + id: wallClock + running: timeFormat != "" + repeat: true + interval: 1000 + triggeredOnStart: true + onTriggered: time = Qt.formatDateTime(new Date(), timeFormat) + property string time + } + + VBusItem { id: systemName; bind: Utils.path(settingsBindPreffix, "/Settings/SystemSetup/SystemName") } + +//////// copied SYSTEM from OverviewTiles.qml & combined SYSTEM and STATUS tiles + Tile { + title: qsTr("STATUS") + id: statusTile + anchors { left: parent.left; top: parent.top } + width: root.infoWidth3Column + height: root.upperTileHeight - inverterTile.height + color: "#4789d0" + +//////// relorder to give priority to errors + values: [ + TileText { + text: systemName.valid && systemName.value !== "" ? systemName.value : sys.systemType.valid ? sys.systemType.value.toUpperCase() : "" + font.pixelSize: 16 + wrapMode: Text.WordWrap + width: statusTile.width - 5 + }, + TileText { + text: wallClock.running ? wallClock.time : "" + font.pixelSize: 15 + }, +//////// combine SystemReason with notifications + MarqueeEnhanced { + text: + { + if (activeNotifications.length === 0) + return systemReasonMessage.text + else + return notificationText() + " || " + systemReasonMessage.text + } + width: statusTile.width + textHorizontalAlignment: Text.AlignHCenter + interval: 100 + SystemReasonMessage { + id: systemReasonMessage + } + }, + TileText { + property VeQuickItem gpsService: VeQuickItem { uid: "dbus/com.victronenergy.system/GpsService" } + property VeQuickItem speed: VeQuickItem { uid: Utils.path("dbus/", gpsService.value, "/Speed") } + property VeQuickItem speedUnit: VeQuickItem { uid: "dbus/com.victronenergy.settings/Settings/Gps/SpeedUnit" } + + text: speed.value === undefined ? "" : getValue() + visible: speed.value !== undefined && speedUnit.value !== undefined + + function getValue() + { + if (speed.value < 0.5) // blank speed if less than about 1 MPH + return " " + if (speedUnit.value === "km/h") + return (speed.value * 3.6).toFixed(1) + speedUnit.value + if (speedUnit.value === "mph") + return (speed.value * 2.236936).toFixed(1) + speedUnit.value + if (speedUnit.value === "kt") + return (speed.value * (3600/1852)).toFixed(1) + speedUnit.value + return speed.value.toFixed(2) + "m/s" + } + } + ] + } // end Tile STATUS + Tile + { + title: qsTr("INVERTER") + id: inverterTile + anchors { left: parent.left; top: statusTile.bottom } + width: root.infoWidth3Column + height: 62 + color: "#4789d0" + + values: [ + TileText + { + text: inverterMode.valid ? inverterMode.text : "--" + }, + TileText { + text: qsTr(systemState.text) + + SystemState { + id: systemState + bind: hasSystemState?Utils.path(systemPrefix, "/SystemState/State"):Utils.path(inverterService, "/State") + } + } + ] +////// add power bar graph + PowerGaugeMulti + { + id: multiBar + width: parent.width - 10 + height: 8 + anchors + { + top: parent.top; topMargin: 20 + horizontalCenter: parent.horizontalCenter + } + inverterService: root.inverterService + visible: showGauges + } + DetailTarget { id: multiTarget; detailsPage: "DetailInverter.qml" } + } // end Tile INVERTER + + Tile { + title: qsTr("BATTERY") + id: batteryTile + anchors { horizontalCenter: infoArea.horizontalCenter; top: infoArea.top } + width: root.infoWidth3Column + height: root.upperTileHeight + + values: [ + TileText // spacer + { + text: "" + font.pixelSize: 6 + }, + TileText { + text: sys.battery.soc.absFormat(0) + font.pixelSize: 22 + //////// remove height (for consistency with other tiles) + }, + TileText { + text: { + if (!sys.battery.state.valid) + return "---" + switch(sys.battery.state.value) { + //////// change - capitalized words look better + case sys.batteryStateIdle: return qsTr("Idle") + case sys.batteryStateCharging : return qsTr("Charging") + case sys.batteryStateDischarging : return qsTr("Discharging") + } + } + }, + TileText { +//////// change to show negative for battery drain + text: sys.battery.power.text + font.pixelSize: 18 + }, + TileText { + text: sys.battery.voltage.format(2) + }, + TileText { + text: sys.battery.current.format(1) + }, + TileText { + text: qsTr("Remaining:") + visible: timeToGo.valid + }, + TileText { + id: timeToGoText + text: timeToGo.valid ? TTG.formatTimeToGo (timeToGo) : " " + visible: timeToGo.valid + + VBusItem { + id: timeToGo + bind: Utils.path("com.victronenergy.system","/Dc/Battery/TimeToGo") + } + } + ] +////// add battery current bar graph + PowerGaugeBattery + { + id: batteryBar + width: parent.width - 10 + height: 8 + endLabelFontSize: 14 + endLabelBackgroundColor: batteryTile.color + anchors + { + top: parent.top; topMargin: 22 + horizontalCenter: parent.horizontalCenter + } + visible: showGauges + } + DetailTarget { id: batteryTarget; detailsPage: "DetailBattery.qml" } + } // end Tile BATTERY + + VBusItem { id: dcSystemNameItem; bind: Utils.path(settingsBindPreffix, "/Settings/GuiMods/CustomDcSystemName") } + + Tile { + title: dcSystemNameItem.valid && dcSystemNameItem.value != "" ? dcSystemNameItem.value : qsTr ("DC SYSTEM") + id: dcSystem + anchors { right: infoArea.right; bottom: infoArea.bottom; bottomMargin: root.acTileHeight } + width: root.infoWidth3Column + height: (root.upperTileHeight / 2) - 5 + color: "#16a085" + values: [ + TileText { // spacer + font.pixelSize: 6 + text: "" + }, + TileText { + font.pixelSize: 22 + text: EnhFmt.formatVBusItem (sys.dcSystem.power) + visible: sys.dcSystem.power.valid + }, + ////// replace to/from battery with current + TileText { + text: !sys.dcSystem.power.valid ? "---" : + EnhFmt.formatValue (sys.dcSystem.power.value / sys.battery.voltage.value, "A") + visible: sys.dcSystem.power.valid + } + ] + PowerGauge + { + id: dcSystemGauge + width: parent.width - 10 + height: 8 + anchors + { + top: parent.top; topMargin: 22 + horizontalCenter: parent.horizontalCenter + } + connection: sys.dcSystem + endLabelFontSize: 12 + endLabelBackgroundColor: dcSystem.color + maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/DcSystemMaxLoad" + maxReversePowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/DcSystemMaxCharge" + showLabels: true + visible: showGauges && sys.dcSystem.power.valid + } + DetailTarget { id: dcSystemTarget; detailsPage: "DetailDcSystem.qml" } + } // end Tile DC SYSTEM + + Tile { + id: solarTile + title: qsTr("PV CHARGER") + anchors { right: infoArea.right; top: infoArea.top } + width: root.infoWidth3Column + height: root.upperTileHeight - dcSystem.height + color: "#2cc36b" + values: [ + TileText { + font.pixelSize: 22 + text: EnhFmt.formatVBusItem (sys.pvCharger.power) + }, + //////// add voltage + TileText { + text: + { + if (showPvVI) + return EnhFmt.formatVBusItem (pvVoltage, "V") + else + return "" + } + visible: showPvVI + }, + //////// add current + TileText { + text: + { + if (showPvVI && pvPower.valid && pvVoltage.valid) + { + var voltage = pvVoltage.value + return EnhFmt.formatValue ((pvPower.value / voltage), "A") + } + else + return "" + } + visible: showPvVI + } + ] +////// add power bar graph + PowerGauge + { + id: pvChargerBar + width: parent.width - 10 + height: 8 + anchors + { + top: parent.top; topMargin: 20 + horizontalCenter: parent.horizontalCenter + } + connection: sys.pvCharger + maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/PvChargerMaxPower" + visible: showGauges && sys.pvCharger.power.valid + } + DetailTarget { id: pvChargerTarget; detailsPage: "DetailPvCharger.qml" } + } // end Tile PV CHARGER + +//////// add to display AC input ignored + VBusItem { id: ignoreAcInput; bind: Utils.path(inverterService, "/Ac/State/IgnoreAcIn1") } + +//////// add AC INPUT tile + Tile { + id: acInputTile + title: { + if (isInverter) + return qsTr ("No AC Input") + else if (ignoreAcInput.valid && ignoreAcInput.value == 1) + return qsTr ("AC In Ignored") + else + { + switch(sys.acSource) { + case 1: return qsTr("GRID") + case 2: return qsTr("GENERATOR") + case 3: return qsTr("SHORE POWER") + default: return qsTr("AC INPUT") + } + } + } + anchors { left: infoArea.left; bottom: infoArea.bottom } + width: root.infoWidth2Column + height: root.acTileHeight + color: "#82acde" +//////// add voltage and current + VBusItem { id: currentLimit; bind: Utils.path(inverterService, "/Ac/ActiveIn/CurrentLimit") } + values: [ + TileText { + visible: isMulti + text: EnhFmt.formatVBusItem (sys.acInput.power) + font.pixelSize: 20 + + }, +//////// add voltage and current + TileText { + visible: isMulti + text: EnhFmt.formatVBusItem (sys.acInput.voltageL1, "V") + " " + EnhFmt.formatVBusItem (sys.acInput.currentL1, "A") + " " + EnhFmt.formatVBusItem (sys.acInput.frequency, "Hz") + }, + TileText + { + text: qsTr ("Limit: ") + EnhFmt.formatVBusItem (currentLimit, "A") + visible: currentLimit.valid + } + ] +////// add power bar graph + PowerGauge + { + id: acInBar + width: parent.width - 10 + height: 8 + anchors + { + top: parent.top; topMargin: 20 + horizontalCenter: parent.horizontalCenter + } + connection: sys.acInput + useInputCurrentLimit: true + maxForwardPowerParameter: "" + maxReversePowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/MaxFeedInPower" + visible: showGauges && hasAcInput + } + DetailTarget { id: acInputTarget; detailsPage: "DetailAcInput.qml" } + } + + Tile { + title: qsTr("AC LOADS") + id: acLoadsTile + anchors { right: infoArea.right; bottom: infoArea.bottom} + width: root.infoWidth2Column + height: root.acTileHeight + color: "#e68e8a" +//////// add voltage and current + VBusItem { id: outVoltage; bind: Utils.path(inverterService, "/Ac/Out/L1/V") } + VBusItem { id: outCurrent; bind: Utils.path(inverterService, "/Ac/Out/L1/I") } + VBusItem { id: outFrequency; bind: Utils.path(inverterService, "/Ac/Out/L1/F") } + + values: [ + TileText { + text: EnhFmt.formatVBusItem (sys.acLoad.power) + font.pixelSize: 22 + }, +//////// add voltage and current - no frequency for VE.Direct inverter + TileText { + text: + { + var lineText = "" + if (isMulti || isInverter) + { + lineText = EnhFmt.formatVBusItem (outVoltage, "V") + " " + EnhFmt.formatVBusItem (outCurrent, "A") + if (isMulti) + lineText += " " + EnhFmt.formatVBusItem (outFrequency, "Hz") + } + return lineText + } + } + ] +////// add power bar graph + PowerGauge + { + id: acLoadBar + width: parent.width - 10 + height: 8 + anchors + { + top: parent.top; topMargin: 20 + horizontalCenter: parent.horizontalCenter + } + connection: sys.acLoad + maxForwardPowerParameter: "com.victronenergy.settings/Settings/GuiMods/GaugeLimits/AcOutputMaxPower" + visible: showGauges && hasAcOutSystem + } + DetailTarget { id: acLoadsOnOutputTarget; detailsPage: "DetailLoadsOnOutput.qml" } + } + + // Synchronise tank name text scroll start + Timer { + id: scrollTimer + interval: 15000 + repeat: true + running: root.active + } + + ListView { + id: tanksColum + + anchors { + top: root.top + right: root.right + } + height: root.tanksHeight + width: root.tankWidth +//////// make list flickable if more tiles than will fit completely + interactive: root.tankTileHeight * count > (tanksColum.height + 1) ? true : false + + model: TankModel { id: tankModel } + delegate: TileTankEnhanced { + // Without an intermediate assignment this will trigger a binding loop warning. + property variant theService: DBusServices.get(buddy.id) + service: theService + width: tanksColum.width + height: root.tankTileHeight + pumpBindPrefix: root.pumpBindPreffix +//////// modified to control compact differently + compact: root.compact + Connections { + target: scrollTimer + onTriggered: doScroll() + } + } + Tile { + title: qsTr("TANKS") + anchors.fill: parent + values: TileText { + text: qsTr("") + width: parent.width + wrapMode: Text.WordWrap + } + z: -1 + } + } + +//////// added temperature ListView and Model + ListView { + id: tempsColumn + + anchors { + top: tanksColum.bottom + right: root.right + } + height: root.tempsHeight + width: root.tankWidth +//////// make list flickable if more tiles than will fit completely + interactive: root.tankTileHeight * count > (tempsColumn.height + 1) ? true : false + + model: tempsModel + delegate: TileTemp + { + width: tempsColumn.width + height: root.tankTileHeight +//////// modified to control compact differently + compact: root.compact + Connections + { + target: scrollTimer + onTriggered: doScroll() + } + } + Tile + { + title: qsTr("TEMPS") + anchors.fill: parent + values: TileText + { + text: qsTr("") + width: parent.width + wrapMode: Text.WordWrap + } + z: -1 + } + } + ListModel { id: tempsModel } + + Tile { + id: pumpButton + + anchors.right: parent.right + anchors.bottom: parent.bottom + + property variant texts: [ qsTr("AUTO"), qsTr("ON"), qsTr("OFF")] + property int value: 0 + property bool reset: false + property bool pumpEnabled: pumpRelay.value === 3 + isCurrentItem: false // not used by GuiMods key handler - focus shown a different way + //focus: root.active && isCurrentItem // don't switch focus -- messes up key handler + + title: qsTr("PUMP") + width: pumpEnabled ? root.tankWidth : 0 + height: 45 + editable: true + readOnly: false + color: pumpButtonMouseArea.containsPressed ? "#d3d3d3" : "#A8A8A8" + + VBusItem { id: pump; bind: Utils.path(settingsBindPreffix, "/Settings/Pump0/Mode") } + VBusItem { id: pumpRelay; bind: Utils.path(settingsBindPreffix, "/Settings/Relay/Function") } + + values: [ + TileText { + text: pumpButton.pumpEnabled ? qsTr("%1").arg(pumpButton.texts[pumpButton.value]) : qsTr("DISABLED") + } + ] + + function edit() { + if (!pumpEnabled) { + toast.createToast(qsTr("Pump functionality is not enabled. To enable it go to the relay settings page and set function to \"Tank pump\""), 5000) + return + } + + reset = true + applyAnimation.restart() + reset = false + + if (value < 2) + value++ + else + value = 0 + } + + MouseArea { + id: pumpButtonMouseArea + property bool containsPressed: containsMouse && pressed + anchors.fill: parent + onClicked: { + parent.edit() + } + } + + Rectangle { + id: timerRect + height: 2 + width: pumpButton.width * 0.8 + visible: applyAnimation.running + anchors { + bottom: parent.bottom; bottomMargin: 5 + horizontalCenter: parent.horizontalCenter + } + } + + SequentialAnimation { + id: applyAnimation + alwaysRunToEnd: false + NumberAnimation { + target: timerRect + property: "width" + from: 0 + to: pumpButton.width * 0.8 + duration: 3000 + } + + ColorAnimation { + target: pumpButton + property: "color" + from: "#A8A8A8" + to: "#4789d0" + duration: 200 + } + + ColorAnimation { + target: pumpButton + property: "color" + from: "#4789d0" + to: "#A8A8A8" + duration: 200 + } + PropertyAction { + target: timerRect + property: "width" + value: 0 + } + // Do not set value if the animation is restarted by user pressing the button + // to move between options + onRunningChanged: if (!running && !pumpButton.reset) pump.setValue(pumpButton.value) + } + DetailTarget { id: pumpButtonTarget; detailsPage: "" } + } + + // When new service is found add resources as appropriate + Connections { + target: DBusServices + onDbusServiceFound: addService(service) + } + + // hack to get value(s) from within a loop inside a function when service is changing + property string tempServiceName: "" + property VBusItem temperatureItem: VBusItem { bind: Utils.path(tempServiceName, "/Dc/0/Temperature") } + +//////// rewrite to use switch in place of if statements + function addService(service) + { + switch (service.type) + { +//////// add for temp sensors + case DBusService.DBUS_SERVICE_TEMPERATURE_SENSOR: + numberOfTemps++ + tempsModel.append({serviceName: service.name}) + break;; + case DBusService.DBUS_SERVICE_MULTI: + root.tempServiceName = service.name + if (temperatureItem.valid) + { + numberOfTemps++ + tempsModel.append({serviceName: service.name}) + } + break;; +//////// add for VE.Direct inverters + case DBusService.DBUS_SERVICE_INVERTER: + if (veDirectInverterService == "") + veDirectInverterService = service.name; + break;; + +//////// add for PV CHARGER voltage and current display + case DBusService.DBUS_SERVICE_SOLAR_CHARGER: + numberOfPvChargers++ + if (pvChargerPrefix === "") + pvChargerPrefix = service.name; + break;; + case DBusService.DBUS_SERVICE_BATTERY: + root.tempServiceName = service.name + if (temperatureItem.valid) + { + numberOfTemps++ + tempsModel.append({serviceName: service.name}) + } + break;; + } + } + + // Check available services to find tank sesnsors +//////// rewrite to always call addService, removing redundant service type checks + function discoverServices() + { + numberOfTemps = 0 + numberOfPvChargers = 0 + veDirectInverterService = "" + tempsModel.clear() + for (var i = 0; i < DBusServices.count; i++) + addService(DBusServices.at(i)) + } + + function notificationText() + { + if (activeNotifications.length === 0) + return qsTr("") + + var descr = [] + for (var n = 0; n < activeNotifications.length; n++) { + var notification = activeNotifications[n]; + + var text = notification.serviceName + " - " + notification.description; + if (notification.value !== "" ) + text += ": " + notification.value + + descr.push(text) + } + + return descr.join(" | ") + } + + VBusItem { id: dmc; bind: Utils.path(inverterService, "/Devices/Dmc/Version") } + VBusItem { id: bms; bind: Utils.path(inverterService, "/Devices/Bms/Version") } + + + +// Details targets +////// display detail targets and help message when first displayed. + Timer { + id: helpTimer + running: false + repeat: false + interval: 5000 + triggeredOnStart: true + } + + // help message shown when menu is first drawn + Rectangle + { + id: helpBox + color: "white" + width: 150 + height: 32 + opacity: 0.7 + anchors + { + horizontalCenter: infoArea.horizontalCenter + verticalCenter: infoArea.verticalCenter + } + visible: false + } + TileText + { + text: qsTr ( "Tap tile center for detail at any time" ) + color: "black" + anchors.fill: helpBox + wrapMode: Text.WordWrap + font.pixelSize: 12 + visible: helpBox.visible + } + + + //// hard key handler + // used to press buttons when touch isn't available + // UP and DOWN buttons cycle through the list of touch areas + // "space" button is used to simulate a touch on the area + // target must be highlighted so that other uses of "space" + // will still occur + + // list of all details touchable areas + // pump button sets value locally, no details page + // so is hanelded differently + // it must be LAST in the list because target list index is used for special processing + property variant targetList: + [ + multiTarget, batteryTarget, pvChargerTarget, dcSystemTarget, + acInputTarget, acLoadsOnOutputTarget, pumpButtonTarget // pump MUST BE LAST + ] + + property int selectedTarget: 0 + + Timer + { + id: targetTimer + interval: 5000 + repeat: false + running: false + onTriggered: { hideAllTargets () } + } + + Keys.forwardTo: [keyHandler] + Item + { + id: keyHandler + Keys.onUpPressed: + { + nextTarget (-1) + event.accepted = true + } + + Keys.onDownPressed: + { + nextTarget (+1) + event.accepted = true + } + Keys.onSpacePressed: + { + if (targetTimer.running) + { + var foo // hack to make clicked() work + if (selectedTarget == targetList.length - 1) + pumpButton.edit () + else + bar.clicked (foo) + event.accepted = true + } + else + event.accepted = false + } + } + // hack to make clicked() work + property variant bar: targetList[selectedTarget] + + function nextTarget (increment) + { + // make one pass through all possible targets to find an enabled one + // if found, that's the new selectedTarget, + // if not selectedTarget does not change + var newIndex = selectedTarget + for (var i = 0; i < targetList.length; i++) + { + if (( ! targetTimer.running || helpBox.visible) && targetList[newIndex].enabled) + { + highlightSelectedTarget () + return + } + newIndex += increment + if (newIndex >= targetList.length) + newIndex = 0 + else if (newIndex < 0) + newIndex = targetList.length - 1 + var includeTarget + if (newIndex == targetList.length - 1) + includeTarget = pumpButton.pumpEnabled + else + includeTarget = targetList[newIndex].enabled + if (includeTarget) + { + selectedTarget = newIndex + highlightSelectedTarget () + break + } + } + } + + function showHelp () + { + for (var i = 0; i < targetList.length; i++) + { + targetList[i].targetVisible = true + } + helpBox.visible = true + targetTimer.restart () + } + function hideAllTargets () + { + for (var i = 0; i < targetList.length; i++) + targetList[i].targetVisible = false + helpBox.visible = false + } + function highlightSelectedTarget () + { + for (var i = 0; i < targetList.length; i++) + { + if (targetList[i] == targetList[selectedTarget]) + targetList[i].targetVisible = true + else + targetList[i].targetVisible = false + } + targetTimer.restart () + helpBox.visible = false + } +} diff --git a/FileSets/v3.60~25/OverviewMobileEnhanced.qml.orig b/FileSets/v3.60~25/OverviewMobileEnhanced.qml.orig new file mode 100644 index 00000000..a1bccfb0 --- /dev/null +++ b/FileSets/v3.60~25/OverviewMobileEnhanced.qml.orig @@ -0,0 +1,642 @@ +import QtQuick 2 +import com.victron.velib 1.0 +import "utils.js" as Utils + +OverviewPage { + id: root + + property variant sys: theSystem + property string settingsBindPreffix: "com.victronenergy.settings" + property string pumpBindPreffix: "com.victronenergy.pump.startstop0" + property variant activeNotifications: NotificationCenter.notifications.filter( + function isActive(obj) { return obj.active} ) + property string noAdjustableByDmc: qsTr("This setting is disabled when a Digital Multi Control " + + "is connected. If it was recently disconnected execute " + + "\"Redetect system\" that is available on the inverter menu page.") + property string noAdjustableByBms: qsTr("This setting is disabled when a VE.Bus BMS " + + "is connected. If it was recently disconnected execute " + + "\"Redetect system\" that is available on the inverter menu page.") + property string noAdjustableTextByConfig: qsTr("This setting is disabled. " + + "Possible reasons are \"Overruled by remote\" is not enabled or " + + "an assistant is preventing the adjustment. Please, check " + + "the inverter configuration with VEConfigure.") + property int numberOfMultis: 0 + property string vebusPrefix: "" + + // Keeps track of which button on the bottom row is active + property int buttonIndex: 0 + // Keep the focus on the buttons, not on the page + focus: false + + title: qsTr("Mobile") + + Component.onCompleted: discoverMulti() + + ListView { + id: pwColumn + + property int tilesCount: solarTile.visible || dcSystem.visible ? 3 : 2 + property int tileHeight: Math.ceil(height / tilesCount) + interactive: false // static tiles + + width: 136 + anchors { + left: parent.left + top: parent.top; + bottom: acModeButton.top; + } + + model: VisibleItemModel { + Tile { + width: pwColumn.width + height: visible ? pwColumn.tileHeight : 0 + title: qsTr("AC INPUT") + color: "#82acde" + visible: !dcSystem.visible || !solarTile.visible + values: [ + TileText { + text: sys.acInput.power.text + font.pixelSize: 30 + } + + ] + } + + TileAcPower { + width: pwColumn.width + height: visible ? pwColumn.tileHeight : 0 + title: qsTr("AC LOADS") + color: "#e68e8a" + values: [ + TileText { + text: sys.acLoad.power.text + font.pixelSize: 30 + } + ] + } + + Tile { + id: solarTile + width: pwColumn.width + height: visible ? pwColumn.tileHeight : 0 + title: qsTr("PV CHARGER") + color: "#2cc36b" + visible : sys.pvCharger.power.valid + + values: [ + TileText { + font.pixelSize: 30 + text: sys.pvCharger.power.text + } + ] + } + Tile { + id: dcSystem + width: pwColumn.width + height: visible ? pwColumn.tileHeight : 0 + title: qsTr("DC SYSTEM") + color: "#16a085" + visible: sys.dcSystem.power.valid + + values: [ + TileText { + font.pixelSize: 30 + text: sys.dcSystem.power.format(0) + }, + TileText { + text: !sys.dcSystem.power.valid ? "---" : + sys.dcSystem.power.value < 0 ? qsTr("to battery") : qsTr("from battery") + } + ] + } + } + } + + Tile { + id: logoTile + + color: "#575748" + height: 120 + anchors { + left: pwColumn.right + right: tanksColum.left + top: parent.top + } + + MbIcon { + x: 1 + y: 1 + // see below, so the svg instead of a png if there is a 1x1 image + visible: customImage.sourceSize.width === 1 && customImage.sourceSize.height === 1 + iconId: "mobile-builder-logo-svg" + } + + // The uploaded png, the default is a 1x1 transparent pixel now. + Image { + id: customImage + source: "image://theme/mobile-builder-logo" + anchors.centerIn: parent + } + } + + Tile { + id: batteryTile + height: 112 + title: qsTr("BATTERY") + anchors { + left: pwColumn.right + right: stateTile.left + top: logoTile.bottom + bottom: acModeButton.top + } + + values: [ + TileText { + text: sys.battery.soc.absFormat(0) + font.pixelSize: 30 + height: 32 + }, + TileText { + text: { + if (!sys.battery.state.valid) + return "---" + switch(sys.battery.state.value) { + case sys.batteryStateIdle: return qsTr("idle") + case sys.batteryStateCharging : return qsTr("charging") + case sys.batteryStateDischarging : return qsTr("discharging") + } + } + }, + TileText { + text: sys.battery.power.absFormat(0) + }, + TileText { + text: sys.battery.voltage.format(1) + " " + sys.battery.current.format(1) + } + ] + } + + Tile { + id: stateTile + + width: 104 + title: qsTr("STATUS") + color: "#4789d0" + + anchors { + right: tanksColum.left + top: logoTile.bottom + bottom: acModeButton.top + } + + Timer { + id: wallClock + + running: true + repeat: true + interval: 1000 + triggeredOnStart: true + onTriggered: time = Qt.formatDateTime(new Date(), "hh:mm") + + property string time + } + + values: [ + TileText { + id: systemTile + text: wallClock.time + font.pixelSize: 30 + }, + TileText { + property VeQuickItem gpsService: VeQuickItem { uid: "dbus/com.victronenergy.system/GpsService" } + property VeQuickItem speed: VeQuickItem { uid: Utils.path("dbus/", gpsService.value, "/Speed") } + property VeQuickItem speedUnit: VeQuickItem { uid: "dbus/com.victronenergy.settings/Settings/Gps/SpeedUnit" } + + text: speed.value === undefined ? "" : getValue() + visible: speed.value !== undefined && speedUnit.value !== undefined + + function getValue() + { + if (speedUnit.value === "km/h") + return (speed.value * 3.6).toFixed(1) + speedUnit.value + if (speedUnit.value === "mph") + return (speed.value * 2.236936).toFixed(1) + speedUnit.value + if (speedUnit.value === "kt") + return (speed.value * (3600/1852)).toFixed(1) + speedUnit.value + return speed.value.toFixed(2) + "m/s" + } + }, + Marquee { + text: notificationText() + width: stateTile.width + interval: 100 + fontSize: 13 + } + ] + } + + ListView { + id: tanksColum + + property int tileHeight: Math.ceil(height / Math.max(count, 2)) + width: 134 + interactive: false // static tiles + model: TankModel { id: tankModel } + delegate: TileTank { + // Without an intermediate assignment this will trigger a binding loop warning. + property variant theService: DBusServices.get(buddy.id) + service: theService + width: tanksColum.width + height: tanksColum.tileHeight + pumpBindPrefix: root.pumpBindPreffix + compact: tankModel.rowCount > (pumpButton.pumpEnabled ? 4 : 5) + Connections { + target: scrollTimer + onTriggered: doScroll() + } + } + + anchors { + top: root.top + bottom: pumpButton.pumpEnabled ? acModeButton.top : acModeButton.bottom + right: root.right + } + + // Synchronise tank name text scroll start + Timer { + id: scrollTimer + interval: 15000 + repeat: true + running: root.active && tankModel.rowCount > 4 + } + + Tile { + title: qsTr("TANKS") + anchors.fill: parent + values: TileText { + text: qsTr("No tanks found") + width: parent.width + wrapMode: Text.WordWrap + } + z: -1 + } + } + + Keys.forwardTo: [keyHandler] + + Item { + id: keyHandler + Keys.onLeftPressed: { + if (buttonIndex > 0) + buttonIndex-- + + event.accepted = true + } + + Keys.onRightPressed: { + if (buttonIndex < (pumpButton.pumpEnabled ? 2 : 1)) + buttonIndex++ + + event.accepted = true + } + } + + MouseArea { + anchors.fill: parent + enabled: parent.active + onPressed: mouse.accepted = acCurrentButton.expanded + onClicked: acCurrentButton.cancel() + } + + TileSpinBox { + id: acCurrentButton + + anchors.bottom: parent.bottom + anchors.left: parent.left + isCurrentItem: (buttonIndex == 0) + focus: root.active && isCurrentItem + + bind: Utils.path(vebusPrefix, "/Ac/ActiveIn/CurrentLimit") + title: qsTr("AC CURRENT LIMIT") + color: containsMouse && !editMode ? "#d3d3d3" : "#A8A8A8" + width: pumpButton.pumpEnabled ? 160 : 173 + fontPixelSize: 14 + unit: "A" + readOnly: currentLimitIsAdjustable.value !== 1 || numberOfMultis > 1 + buttonColor: "#979797" + + VBusItem { id: currentLimitIsAdjustable; bind: Utils.path(vebusPrefix, "/Ac/ActiveIn/CurrentLimitIsAdjustable") } + + Keys.onSpacePressed: showErrorToast(event) + + function editIsAllowed() { + if (numberOfMultis > 1) { + toast.createToast(qsTr("It is not possible to change this setting when there are more than one inverter connected."), 5000) + return false + } + + if (currentLimitIsAdjustable.value === 0) { + if (dmc.valid) { + toast.createToast(noAdjustableByDmc, 5000) + return false + } + if (bms.valid) { + toast.createToast(noAdjustableByBms, 5000) + return false + } + if (!dmc.valid && !bms.valid) { + toast.createToast(noAdjustableTextByConfig, 5000) + return false + } + } + + return true + } + + function showErrorToast(event) { + editIsAllowed() + event.accepted = true + } + } + + Tile { + id: acModeButton + anchors.left: acCurrentButton.right + anchors.bottom: parent.bottom + property variant texts: { 4: qsTr("OFF"), 3: qsTr("ON"), 1: qsTr("CHARGER ONLY"), 2: qsTr("INVERTER ONLY") } + property int value: mode.valid ? mode.value : 3 + property int shownValue: applyAnimation2.running ? applyAnimation2.pendingValue : value + + isCurrentItem: (buttonIndex == 1) + focus: root.active && isCurrentItem + + editable: true + readOnly: !modeIsAdjustable.valid || modeIsAdjustable.value !== 1 || numberOfMultis > 1 + width: pumpButton.pumpEnabled ? 160 : 173 + height: 45 + color: acModeButtonMouseArea.containsPressed ? "#d3d3d3" : "#A8A8A8" + title: qsTr("AC MODE") + + values: [ + TileText { + text: modeIsAdjustable.valid && numberOfMultis === 1 ? qsTr("%1").arg(acModeButton.texts[acModeButton.shownValue]) : qsTr("NOT AVAILABLE") + } + ] + + VBusItem { id: mode; bind: Utils.path(vebusPrefix, "/Mode") } + VBusItem { id: modeIsAdjustable; bind: Utils.path(vebusPrefix,"/ModeIsAdjustable") } + + Keys.onSpacePressed: edit() + + function edit() { + if (!mode.valid) + return + + if (numberOfMultis > 1) { + toast.createToast(qsTr("It is not possible to change this setting when there are more than one inverter connected."), 5000) + return + } + + if (modeIsAdjustable.value === 0) { + if (dmc.valid) + toast.createToast(noAdjustableByDmc, 5000) + if (bms.valid) + toast.createToast(noAdjustableByBms, 5000) + if (!dmc.valid && !bms.valid) + toast.createToast(noAdjustableTextByConfig, 5000) + return + } + + switch (shownValue) { + case 4: + applyAnimation2.pendingValue = 3 + break; + case 3: + applyAnimation2.pendingValue = 1 + break; + case 2: + applyAnimation2.pendingValue = 4 + break; + case 1: + applyAnimation2.pendingValue = 2 + break; + } + + applyAnimation2.restart() + } + + MouseArea { + id: acModeButtonMouseArea + anchors.fill: parent + property bool containsPressed: containsMouse && pressed + onClicked: { + buttonIndex = 1 + parent.edit() + } + } + + Rectangle { + id: timerRect2 + height: 2 + width: acModeButton.width * 0.8 + visible: applyAnimation2.running + anchors { + bottom: parent.bottom; bottomMargin: 5 + horizontalCenter: parent.horizontalCenter + } + } + + SequentialAnimation { + id: applyAnimation2 + + property int pendingValue + + NumberAnimation { + target: timerRect2 + property: "width" + from: 0 + to: acModeButton.width * 0.8 + duration: 3000 + } + + ColorAnimation { + target: acModeButton + property: "color" + from: "#A8A8A8" + to: "#4789d0" + duration: 200 + } + + ColorAnimation { + target: acModeButton + property: "color" + from: "#4789d0" + to: "#A8A8A8" + duration: 200 + } + PropertyAction { + target: timerRect2 + property: "width" + value: 0 + } + + ScriptAction { script: mode.setValue(applyAnimation2.pendingValue) } + + PauseAnimation { duration: 1000 } + } + } + + Tile { + id: pumpButton + + anchors.left: acModeButton.right + anchors.bottom: parent.bottom + + property variant texts: [ qsTr("AUTO"), qsTr("ON"), qsTr("OFF")] + property int value: 0 + property bool reset: false + property bool pumpEnabled: pumpRelay.value === 3 + + show: pumpEnabled + isCurrentItem: (buttonIndex == 2) + focus: root.active && isCurrentItem + + title: qsTr("PUMP") + width: show ? 160 : 0 + height: 45 + editable: true + readOnly: false + color: pumpButtonMouseArea.containsPressed ? "#d3d3d3" : "#A8A8A8" + + VBusItem { id: pump; bind: Utils.path(settingsBindPreffix, "/Settings/Pump0/Mode") } + VBusItem { id: pumpRelay; bind: Utils.path(settingsBindPreffix, "/Settings/Relay/Function") } + + values: [ + TileText { + text: pumpButton.pumpEnabled ? qsTr("%1").arg(pumpButton.texts[pumpButton.value]) : qsTr("DISABLED") + } + ] + + Keys.onSpacePressed: edit() + + function edit() { + if (!pumpEnabled) { + toast.createToast(qsTr("Pump functionality is not enabled. To enable it go to the relay settings page and set function to \"Tank pump\""), 5000) + return + } + + reset = true + applyAnimation.restart() + reset = false + + if (value < 2) + value++ + else + value = 0 + } + + MouseArea { + id: pumpButtonMouseArea + property bool containsPressed: containsMouse && pressed + anchors.fill: parent + onClicked: { + buttonIndex = 2 + parent.edit() + } + } + + Rectangle { + id: timerRect + height: 2 + width: pumpButton.width * 0.8 + visible: applyAnimation.running + anchors { + bottom: parent.bottom; bottomMargin: 5 + horizontalCenter: parent.horizontalCenter + } + } + + SequentialAnimation { + id: applyAnimation + alwaysRunToEnd: false + NumberAnimation { + target: timerRect + property: "width" + from: 0 + to: pumpButton.width * 0.8 + duration: 3000 + } + + ColorAnimation { + target: pumpButton + property: "color" + from: "#A8A8A8" + to: "#4789d0" + duration: 200 + } + + ColorAnimation { + target: pumpButton + property: "color" + from: "#4789d0" + to: "#A8A8A8" + duration: 200 + } + PropertyAction { + target: timerRect + property: "width" + value: 0 + } + // Do not set value if the animation is restarted by user pressing the button + // to move between options + onRunningChanged: if (!running && !pumpButton.reset) pump.setValue(pumpButton.value) + } + } + + // When new service is found check if is a tank sensor + Connections { + target: DBusServices + onDbusServiceFound: addService(service) + } + + function addService(service) + { + if (service.type === DBusService.DBUS_SERVICE_MULTI) { + numberOfMultis++ + if (vebusPrefix === "") + vebusPrefix = service.name; + } + } + + // Check available services to find tank sesnsors + function discoverMulti() + { + for (var i = 0; i < DBusServices.count; i++) { + if (DBusServices.at(i).type === DBusService.DBUS_SERVICE_MULTI) { + addService(DBusServices.at(i)) + } + } + } + + function notificationText() + { + if (activeNotifications.length === 0) + return qsTr("no alarms") + + var descr = [] + for (var n = 0; n < activeNotifications.length; n++) { + var notification = activeNotifications[n]; + + var text = notification.serviceName + " - " + notification.description; + if (notification.value !== "" ) + text += ": " + notification.value + + descr.push(text) + } + + return descr.join(" | ") + } + + VBusItem { id: dmc; bind: Utils.path(vebusPrefix, "/Devices/Dmc/Version") } + VBusItem { id: bms; bind: Utils.path(vebusPrefix, "/Devices/Bms/Version") } +} diff --git a/FileSets/v3.60~25/OverviewTanksTempsDigInputs.qml b/FileSets/v3.60~25/OverviewTanksTempsDigInputs.qml new file mode 100644 index 00000000..b07a817a --- /dev/null +++ b/FileSets/v3.60~25/OverviewTanksTempsDigInputs.qml @@ -0,0 +1,207 @@ +//// New overview page for tanks, temps and digital inputs +//// part of GuiMods +//// based on tank/temps column in mobile overview + +import QtQuick 2 +import com.victron.velib 1.0 +import "utils.js" as Utils +import "timeToGo.js" as TTG + +OverviewPage { + title: qsTr("Tanks & Temps & Digital Inputs") + id: root + + property variant sys: theSystem + property string systemPrefix: "com.victronenergy.system" + property string settingsBindPreffix: "com.victronenergy.settings" + property string pumpBindPreffix: "com.victronenergy.pump.startstop0" + + property int numberOfTanks: tankModel.rowCount + property real tanksHeight: root.height + property real minTankHeight: 21 // use for temps also + property real maxTankHeight: 80 // use for temps also + property real tankTileHeight: Math.min (Math.max (tanksHeight / numberOfTanks, minTankHeight), maxTankHeight) + property bool tanksCompact: numberOfTanks > 6 + + property int numberOfTemps: 0 + property real tempsHeight: root.height + property real tempsTileHeight: Math.min (Math.max (tempsHeight / numberOfTemps, minTankHeight), maxTankHeight) + property bool tempsCompact: numberOfTemps > 6 + + property int tankWidth: parent.width / 3 + property int tempsWidth: tankWidth + property int digInWidth: tankWidth + + property int numberOfDigIn: 0 + property real digInHeight: root.height + property real digInTileHeight: Math.min (Math.max (digInHeight / numberOfDigIn, minTankHeight), maxTankHeight) + + Component.onCompleted: { discoverServices() } + + // Synchronise name text scroll start + Timer { + id: scrollTimer + interval: 15000 + repeat: true + running: root.active + } + + ListView { + id: tanksColum + + anchors { + top: root.top + left: root.left + } + height: root.tanksHeight + width: root.tankWidth + interactive: root.tankTileHeight * count > (tanksColum.height + 1) ? true : false + + model: TankModel { id: tankModel } + delegate: TileTankEnhanced { + // Without an intermediate assignment this will trigger a binding loop warning. + property variant theService: DBusServices.get(buddy.id) + service: theService + width: tanksColum.width + height: root.tankTileHeight + pumpBindPrefix: root.pumpBindPreffix + compact: root.tanksCompact + Connections { + target: scrollTimer + onTriggered: doScroll() + } + } + Tile { + title: numberOfTanks == 0 ? qsTr ("no tanks") : qsTr("Tanks") + anchors.fill: parent + color: "#b3b3b3" + values: TileText { + text: qsTr("") + width: parent.width + wrapMode: Text.WordWrap + } + z: -1 + } + } + + ListView { + id: tempsColumn + + anchors { + top: root.top + left: tanksColum.right + } + height: root.tempsHeight + width: root.tempsWidth + // make list flickable if more tiles than will fit completely + interactive: root.tankTileHeight * count > (tempsColumn.height + 1) ? true : false + + model: tempsModel + delegate: TileTemp + { + width: tempsColumn.width + height: root.tempsTileHeight + compact: root.tempsCompact + Connections + { + target: scrollTimer + onTriggered: doScroll() + } + } + Tile + { + title: numberOfTemps == 0 ? qsTr ("no temps") : qsTr("Temps") + anchors.fill: parent + color: "#b3b3b3" + values: TileText + { + text: qsTr("") + width: parent.width + wrapMode: Text.WordWrap + } + z: -1 + } + } + ListModel { id: tempsModel } + + ListView { + id: digInputsColumn + + anchors { + top: root.top + right: root.right + } + height: root.digInHeight + width: root.digInWidth + // make list flickable if more tiles than will fit completely + interactive: root.digInTileHeight * count > (digInputsColumn.height + 1) ? true : false + + model: digInModel + delegate: TileDigIn + { + width: digInputsColumn.width + height: root.digInTileHeight + } + Tile + { + title: numberOfDigIn == 0 ? qsTr ("no digital inputs") : qsTr("Digital Inputs") + anchors.fill: parent + color: "#b3b3b3" + values: TileText + { + text: qsTr("") + width: parent.width + wrapMode: Text.WordWrap + } + z: -1 + } + } + ListModel { id: digInModel } + + + // When new service is found add resources as appropriate + Connections { + target: DBusServices + onDbusServiceFound: addService(service) + } + + // hack to get value(s) from within a loop inside a function when service is changing + property string tempServiceName: "" + property VBusItem temperatureItem: VBusItem { bind: Utils.path(tempServiceName, "/Dc/0/Temperature") } + + function addService(service) + { + switch (service.type) + { + case DBusService.DBUS_SERVICE_TEMPERATURE_SENSOR: + numberOfTemps++ + tempsModel.append({serviceName: service.name}) + break;; + case DBusService.DBUS_SERVICE_DIGITAL_INPUT: + case DBusService.DBUS_SERVICE_PULSE_COUNTER: + numberOfDigIn++ + digInModel.append({serviceName: service.name}) + break;; + case DBusService.DBUS_SERVICE_BATTERY: + case DBusService.DBUS_SERVICE_MULTI: + root.tempServiceName = service.name + if (temperatureItem.valid) + { + numberOfTemps++ + tempsModel.append({serviceName: service.name}) + } + break;; + } + } + + // Check available services to find tank sesnsors + function discoverServices() + { + numberOfTemps = 0 + tempsModel.clear() + numberOfDigIn = 0 + digInModel.clear() + for (var i = 0; i < DBusServices.count; i++) + addService(DBusServices.at(i)) + } +} diff --git a/FileSets/v3.60~25/OverviewTanksTempsDigInputs.qml.orig b/FileSets/v3.60~25/OverviewTanksTempsDigInputs.qml.orig new file mode 100644 index 00000000..a1bccfb0 --- /dev/null +++ b/FileSets/v3.60~25/OverviewTanksTempsDigInputs.qml.orig @@ -0,0 +1,642 @@ +import QtQuick 2 +import com.victron.velib 1.0 +import "utils.js" as Utils + +OverviewPage { + id: root + + property variant sys: theSystem + property string settingsBindPreffix: "com.victronenergy.settings" + property string pumpBindPreffix: "com.victronenergy.pump.startstop0" + property variant activeNotifications: NotificationCenter.notifications.filter( + function isActive(obj) { return obj.active} ) + property string noAdjustableByDmc: qsTr("This setting is disabled when a Digital Multi Control " + + "is connected. If it was recently disconnected execute " + + "\"Redetect system\" that is available on the inverter menu page.") + property string noAdjustableByBms: qsTr("This setting is disabled when a VE.Bus BMS " + + "is connected. If it was recently disconnected execute " + + "\"Redetect system\" that is available on the inverter menu page.") + property string noAdjustableTextByConfig: qsTr("This setting is disabled. " + + "Possible reasons are \"Overruled by remote\" is not enabled or " + + "an assistant is preventing the adjustment. Please, check " + + "the inverter configuration with VEConfigure.") + property int numberOfMultis: 0 + property string vebusPrefix: "" + + // Keeps track of which button on the bottom row is active + property int buttonIndex: 0 + // Keep the focus on the buttons, not on the page + focus: false + + title: qsTr("Mobile") + + Component.onCompleted: discoverMulti() + + ListView { + id: pwColumn + + property int tilesCount: solarTile.visible || dcSystem.visible ? 3 : 2 + property int tileHeight: Math.ceil(height / tilesCount) + interactive: false // static tiles + + width: 136 + anchors { + left: parent.left + top: parent.top; + bottom: acModeButton.top; + } + + model: VisibleItemModel { + Tile { + width: pwColumn.width + height: visible ? pwColumn.tileHeight : 0 + title: qsTr("AC INPUT") + color: "#82acde" + visible: !dcSystem.visible || !solarTile.visible + values: [ + TileText { + text: sys.acInput.power.text + font.pixelSize: 30 + } + + ] + } + + TileAcPower { + width: pwColumn.width + height: visible ? pwColumn.tileHeight : 0 + title: qsTr("AC LOADS") + color: "#e68e8a" + values: [ + TileText { + text: sys.acLoad.power.text + font.pixelSize: 30 + } + ] + } + + Tile { + id: solarTile + width: pwColumn.width + height: visible ? pwColumn.tileHeight : 0 + title: qsTr("PV CHARGER") + color: "#2cc36b" + visible : sys.pvCharger.power.valid + + values: [ + TileText { + font.pixelSize: 30 + text: sys.pvCharger.power.text + } + ] + } + Tile { + id: dcSystem + width: pwColumn.width + height: visible ? pwColumn.tileHeight : 0 + title: qsTr("DC SYSTEM") + color: "#16a085" + visible: sys.dcSystem.power.valid + + values: [ + TileText { + font.pixelSize: 30 + text: sys.dcSystem.power.format(0) + }, + TileText { + text: !sys.dcSystem.power.valid ? "---" : + sys.dcSystem.power.value < 0 ? qsTr("to battery") : qsTr("from battery") + } + ] + } + } + } + + Tile { + id: logoTile + + color: "#575748" + height: 120 + anchors { + left: pwColumn.right + right: tanksColum.left + top: parent.top + } + + MbIcon { + x: 1 + y: 1 + // see below, so the svg instead of a png if there is a 1x1 image + visible: customImage.sourceSize.width === 1 && customImage.sourceSize.height === 1 + iconId: "mobile-builder-logo-svg" + } + + // The uploaded png, the default is a 1x1 transparent pixel now. + Image { + id: customImage + source: "image://theme/mobile-builder-logo" + anchors.centerIn: parent + } + } + + Tile { + id: batteryTile + height: 112 + title: qsTr("BATTERY") + anchors { + left: pwColumn.right + right: stateTile.left + top: logoTile.bottom + bottom: acModeButton.top + } + + values: [ + TileText { + text: sys.battery.soc.absFormat(0) + font.pixelSize: 30 + height: 32 + }, + TileText { + text: { + if (!sys.battery.state.valid) + return "---" + switch(sys.battery.state.value) { + case sys.batteryStateIdle: return qsTr("idle") + case sys.batteryStateCharging : return qsTr("charging") + case sys.batteryStateDischarging : return qsTr("discharging") + } + } + }, + TileText { + text: sys.battery.power.absFormat(0) + }, + TileText { + text: sys.battery.voltage.format(1) + " " + sys.battery.current.format(1) + } + ] + } + + Tile { + id: stateTile + + width: 104 + title: qsTr("STATUS") + color: "#4789d0" + + anchors { + right: tanksColum.left + top: logoTile.bottom + bottom: acModeButton.top + } + + Timer { + id: wallClock + + running: true + repeat: true + interval: 1000 + triggeredOnStart: true + onTriggered: time = Qt.formatDateTime(new Date(), "hh:mm") + + property string time + } + + values: [ + TileText { + id: systemTile + text: wallClock.time + font.pixelSize: 30 + }, + TileText { + property VeQuickItem gpsService: VeQuickItem { uid: "dbus/com.victronenergy.system/GpsService" } + property VeQuickItem speed: VeQuickItem { uid: Utils.path("dbus/", gpsService.value, "/Speed") } + property VeQuickItem speedUnit: VeQuickItem { uid: "dbus/com.victronenergy.settings/Settings/Gps/SpeedUnit" } + + text: speed.value === undefined ? "" : getValue() + visible: speed.value !== undefined && speedUnit.value !== undefined + + function getValue() + { + if (speedUnit.value === "km/h") + return (speed.value * 3.6).toFixed(1) + speedUnit.value + if (speedUnit.value === "mph") + return (speed.value * 2.236936).toFixed(1) + speedUnit.value + if (speedUnit.value === "kt") + return (speed.value * (3600/1852)).toFixed(1) + speedUnit.value + return speed.value.toFixed(2) + "m/s" + } + }, + Marquee { + text: notificationText() + width: stateTile.width + interval: 100 + fontSize: 13 + } + ] + } + + ListView { + id: tanksColum + + property int tileHeight: Math.ceil(height / Math.max(count, 2)) + width: 134 + interactive: false // static tiles + model: TankModel { id: tankModel } + delegate: TileTank { + // Without an intermediate assignment this will trigger a binding loop warning. + property variant theService: DBusServices.get(buddy.id) + service: theService + width: tanksColum.width + height: tanksColum.tileHeight + pumpBindPrefix: root.pumpBindPreffix + compact: tankModel.rowCount > (pumpButton.pumpEnabled ? 4 : 5) + Connections { + target: scrollTimer + onTriggered: doScroll() + } + } + + anchors { + top: root.top + bottom: pumpButton.pumpEnabled ? acModeButton.top : acModeButton.bottom + right: root.right + } + + // Synchronise tank name text scroll start + Timer { + id: scrollTimer + interval: 15000 + repeat: true + running: root.active && tankModel.rowCount > 4 + } + + Tile { + title: qsTr("TANKS") + anchors.fill: parent + values: TileText { + text: qsTr("No tanks found") + width: parent.width + wrapMode: Text.WordWrap + } + z: -1 + } + } + + Keys.forwardTo: [keyHandler] + + Item { + id: keyHandler + Keys.onLeftPressed: { + if (buttonIndex > 0) + buttonIndex-- + + event.accepted = true + } + + Keys.onRightPressed: { + if (buttonIndex < (pumpButton.pumpEnabled ? 2 : 1)) + buttonIndex++ + + event.accepted = true + } + } + + MouseArea { + anchors.fill: parent + enabled: parent.active + onPressed: mouse.accepted = acCurrentButton.expanded + onClicked: acCurrentButton.cancel() + } + + TileSpinBox { + id: acCurrentButton + + anchors.bottom: parent.bottom + anchors.left: parent.left + isCurrentItem: (buttonIndex == 0) + focus: root.active && isCurrentItem + + bind: Utils.path(vebusPrefix, "/Ac/ActiveIn/CurrentLimit") + title: qsTr("AC CURRENT LIMIT") + color: containsMouse && !editMode ? "#d3d3d3" : "#A8A8A8" + width: pumpButton.pumpEnabled ? 160 : 173 + fontPixelSize: 14 + unit: "A" + readOnly: currentLimitIsAdjustable.value !== 1 || numberOfMultis > 1 + buttonColor: "#979797" + + VBusItem { id: currentLimitIsAdjustable; bind: Utils.path(vebusPrefix, "/Ac/ActiveIn/CurrentLimitIsAdjustable") } + + Keys.onSpacePressed: showErrorToast(event) + + function editIsAllowed() { + if (numberOfMultis > 1) { + toast.createToast(qsTr("It is not possible to change this setting when there are more than one inverter connected."), 5000) + return false + } + + if (currentLimitIsAdjustable.value === 0) { + if (dmc.valid) { + toast.createToast(noAdjustableByDmc, 5000) + return false + } + if (bms.valid) { + toast.createToast(noAdjustableByBms, 5000) + return false + } + if (!dmc.valid && !bms.valid) { + toast.createToast(noAdjustableTextByConfig, 5000) + return false + } + } + + return true + } + + function showErrorToast(event) { + editIsAllowed() + event.accepted = true + } + } + + Tile { + id: acModeButton + anchors.left: acCurrentButton.right + anchors.bottom: parent.bottom + property variant texts: { 4: qsTr("OFF"), 3: qsTr("ON"), 1: qsTr("CHARGER ONLY"), 2: qsTr("INVERTER ONLY") } + property int value: mode.valid ? mode.value : 3 + property int shownValue: applyAnimation2.running ? applyAnimation2.pendingValue : value + + isCurrentItem: (buttonIndex == 1) + focus: root.active && isCurrentItem + + editable: true + readOnly: !modeIsAdjustable.valid || modeIsAdjustable.value !== 1 || numberOfMultis > 1 + width: pumpButton.pumpEnabled ? 160 : 173 + height: 45 + color: acModeButtonMouseArea.containsPressed ? "#d3d3d3" : "#A8A8A8" + title: qsTr("AC MODE") + + values: [ + TileText { + text: modeIsAdjustable.valid && numberOfMultis === 1 ? qsTr("%1").arg(acModeButton.texts[acModeButton.shownValue]) : qsTr("NOT AVAILABLE") + } + ] + + VBusItem { id: mode; bind: Utils.path(vebusPrefix, "/Mode") } + VBusItem { id: modeIsAdjustable; bind: Utils.path(vebusPrefix,"/ModeIsAdjustable") } + + Keys.onSpacePressed: edit() + + function edit() { + if (!mode.valid) + return + + if (numberOfMultis > 1) { + toast.createToast(qsTr("It is not possible to change this setting when there are more than one inverter connected."), 5000) + return + } + + if (modeIsAdjustable.value === 0) { + if (dmc.valid) + toast.createToast(noAdjustableByDmc, 5000) + if (bms.valid) + toast.createToast(noAdjustableByBms, 5000) + if (!dmc.valid && !bms.valid) + toast.createToast(noAdjustableTextByConfig, 5000) + return + } + + switch (shownValue) { + case 4: + applyAnimation2.pendingValue = 3 + break; + case 3: + applyAnimation2.pendingValue = 1 + break; + case 2: + applyAnimation2.pendingValue = 4 + break; + case 1: + applyAnimation2.pendingValue = 2 + break; + } + + applyAnimation2.restart() + } + + MouseArea { + id: acModeButtonMouseArea + anchors.fill: parent + property bool containsPressed: containsMouse && pressed + onClicked: { + buttonIndex = 1 + parent.edit() + } + } + + Rectangle { + id: timerRect2 + height: 2 + width: acModeButton.width * 0.8 + visible: applyAnimation2.running + anchors { + bottom: parent.bottom; bottomMargin: 5 + horizontalCenter: parent.horizontalCenter + } + } + + SequentialAnimation { + id: applyAnimation2 + + property int pendingValue + + NumberAnimation { + target: timerRect2 + property: "width" + from: 0 + to: acModeButton.width * 0.8 + duration: 3000 + } + + ColorAnimation { + target: acModeButton + property: "color" + from: "#A8A8A8" + to: "#4789d0" + duration: 200 + } + + ColorAnimation { + target: acModeButton + property: "color" + from: "#4789d0" + to: "#A8A8A8" + duration: 200 + } + PropertyAction { + target: timerRect2 + property: "width" + value: 0 + } + + ScriptAction { script: mode.setValue(applyAnimation2.pendingValue) } + + PauseAnimation { duration: 1000 } + } + } + + Tile { + id: pumpButton + + anchors.left: acModeButton.right + anchors.bottom: parent.bottom + + property variant texts: [ qsTr("AUTO"), qsTr("ON"), qsTr("OFF")] + property int value: 0 + property bool reset: false + property bool pumpEnabled: pumpRelay.value === 3 + + show: pumpEnabled + isCurrentItem: (buttonIndex == 2) + focus: root.active && isCurrentItem + + title: qsTr("PUMP") + width: show ? 160 : 0 + height: 45 + editable: true + readOnly: false + color: pumpButtonMouseArea.containsPressed ? "#d3d3d3" : "#A8A8A8" + + VBusItem { id: pump; bind: Utils.path(settingsBindPreffix, "/Settings/Pump0/Mode") } + VBusItem { id: pumpRelay; bind: Utils.path(settingsBindPreffix, "/Settings/Relay/Function") } + + values: [ + TileText { + text: pumpButton.pumpEnabled ? qsTr("%1").arg(pumpButton.texts[pumpButton.value]) : qsTr("DISABLED") + } + ] + + Keys.onSpacePressed: edit() + + function edit() { + if (!pumpEnabled) { + toast.createToast(qsTr("Pump functionality is not enabled. To enable it go to the relay settings page and set function to \"Tank pump\""), 5000) + return + } + + reset = true + applyAnimation.restart() + reset = false + + if (value < 2) + value++ + else + value = 0 + } + + MouseArea { + id: pumpButtonMouseArea + property bool containsPressed: containsMouse && pressed + anchors.fill: parent + onClicked: { + buttonIndex = 2 + parent.edit() + } + } + + Rectangle { + id: timerRect + height: 2 + width: pumpButton.width * 0.8 + visible: applyAnimation.running + anchors { + bottom: parent.bottom; bottomMargin: 5 + horizontalCenter: parent.horizontalCenter + } + } + + SequentialAnimation { + id: applyAnimation + alwaysRunToEnd: false + NumberAnimation { + target: timerRect + property: "width" + from: 0 + to: pumpButton.width * 0.8 + duration: 3000 + } + + ColorAnimation { + target: pumpButton + property: "color" + from: "#A8A8A8" + to: "#4789d0" + duration: 200 + } + + ColorAnimation { + target: pumpButton + property: "color" + from: "#4789d0" + to: "#A8A8A8" + duration: 200 + } + PropertyAction { + target: timerRect + property: "width" + value: 0 + } + // Do not set value if the animation is restarted by user pressing the button + // to move between options + onRunningChanged: if (!running && !pumpButton.reset) pump.setValue(pumpButton.value) + } + } + + // When new service is found check if is a tank sensor + Connections { + target: DBusServices + onDbusServiceFound: addService(service) + } + + function addService(service) + { + if (service.type === DBusService.DBUS_SERVICE_MULTI) { + numberOfMultis++ + if (vebusPrefix === "") + vebusPrefix = service.name; + } + } + + // Check available services to find tank sesnsors + function discoverMulti() + { + for (var i = 0; i < DBusServices.count; i++) { + if (DBusServices.at(i).type === DBusService.DBUS_SERVICE_MULTI) { + addService(DBusServices.at(i)) + } + } + } + + function notificationText() + { + if (activeNotifications.length === 0) + return qsTr("no alarms") + + var descr = [] + for (var n = 0; n < activeNotifications.length; n++) { + var notification = activeNotifications[n]; + + var text = notification.serviceName + " - " + notification.description; + if (notification.value !== "" ) + text += ": " + notification.value + + descr.push(text) + } + + return descr.join(" | ") + } + + VBusItem { id: dmc; bind: Utils.path(vebusPrefix, "/Devices/Dmc/Version") } + VBusItem { id: bms; bind: Utils.path(vebusPrefix, "/Devices/Bms/Version") } +} diff --git a/FileSets/v3.60~25/PageGenerator.qml.USE_ORIGINAL b/FileSets/v3.60~25/PageGenerator.qml.USE_ORIGINAL new file mode 100644 index 00000000..e69de29b diff --git a/FileSets/v3.60~25/PageGenerator.qml.orig b/FileSets/v3.60~25/PageGenerator.qml.orig new file mode 100644 index 00000000..3dd70448 --- /dev/null +++ b/FileSets/v3.60~25/PageGenerator.qml.orig @@ -0,0 +1,96 @@ +import QtQuick 2 +import "utils.js" as Utils + +MbPage { + id: root + title: qsTr("Generator start/stop") + property string settingsBindPrefix + property string startStopBindPrefix + property bool showRunTime: true + property alias startStopModel: _startStopModel + property VBusItem activeCondition: VBusItem { bind: Utils.path(startStopBindPrefix, "/RunningByCondition") } + property VBusItem generatorState: VBusItem { bind: Utils.path(startStopBindPrefix, "/State") } + property VBusItem runningTime: VBusItem { bind: Utils.path(startStopBindPrefix, "/Runtime") } + + FnGeneratorStates { + id: genState + } + + model: startStopModel + + function formatError(text, value) + { + return "#" + value.toString() + " " + text + } + + VisibleItemModel { + id: _startStopModel + + MbSwitch { + name: qsTr("Auto start functionality") + bind: Utils.path(startStopBindPrefix, "/AutoStartEnabled") + show: startStopBindPrefix === "com.victronenergy.generator.startstop0" + } + + MbSubMenu { + description: qsTr("Manual start") + show: startStopBindPrefix === "com.victronenergy.generator.startstop0" + subpage: + Component { + PageGeneratorManualStart { + startStopBindPrefix: root.startStopBindPrefix + } + } + } + + MbItemValue { + description: qsTr("Current run time") + item.text: runningTime.valid ? Utils.secondsToNoSecsString(runningTime.value) : "0" + show: generatorState.value >= 1 && generatorState.value <= 3 // Running, Warm-up, Cool-down + } + + MbItemValue { + description: qsTr("State") + show: startStopBindPrefix === "com.victronenergy.generator.startstop0" + item.text: activeCondition.valid ? genState.getState(generatorState.value, activeCondition.value) : '---' + } + + MbItemOptions { + id: _gensetStatus + description: qsTr("Error") + bind: Utils.path(startStopBindPrefix, "/Error") + readonly: true + show: valid && startStopBindPrefix === "com.victronenergy.generator.startstop0" + possibleValues: [ + MbOption { description: qsTr("No error"); value: 0 }, + MbOption { description: formatError(qsTr("Remote switch control disabled"), 1); value: 1 }, + MbOption { description: formatError(qsTr("Generator in fault condition"), 2); value: 2 }, + MbOption { description: formatError(qsTr("Generator not detected at AC input"), 3); value: 3 } + ] + } + + MbSubMenu { + id: conditions + description: qsTr("Settings") + subpage: Component { + PageSettingsGenerator { + settingsBindPrefix: root.settingsBindPrefix + startStopBindPrefix: root.startStopBindPrefix + } + } + } + + MbSubMenu { + id: runtimePage + description: qsTr("Run time and service") + subpage: + Component { + PageGeneratorRuntimeService { + title: qsTr("Run time and service") + settingsBindPrefix: root.settingsBindPrefix + startStopBindPrefix: root.startStopBindPrefix + } + } + } + } +} diff --git a/FileSets/v3.60~25/PageSettingsGuiMods.qml b/FileSets/v3.60~25/PageSettingsGuiMods.qml new file mode 100644 index 00000000..0ad96985 --- /dev/null +++ b/FileSets/v3.60~25/PageSettingsGuiMods.qml @@ -0,0 +1,290 @@ +/////// new menu for all Gui Mods + +import QtQuick 2 +import "utils.js" as Utils +import com.victron.velib 1.0 + +MbPage { + id: root + title: qsTr("Gui Mods") + property string bindPrefixGuiMods: "com.victronenergy.settings/Settings/GuiMods" + property string bindPrefix: "com.victronenergy.settings/Settings/Gui" + property VBusItem systemScaleItem: VBusItem { bind: "com.victronenergy.settings/Settings/System/Units/Temperature" } + + property bool showFlowParams: flowOverview.item.valid && flowOverview.item.value >= 1 + property bool showComplexParams: flowOverview.item.valid && flowOverview.item.value >= 2 + property bool showAcCoupledParams: flowOverview.item.valid && flowOverview.item.value == 3 + + model: VisibleItemModel + { + MbSwitch + { + id: showTileOverview + bind: Utils.path (bindPrefixGuiMods, "/ShowTileOverview") + name: qsTr ("Show Tile Overview") + writeAccessLevel: User.AccessUser + } + + MbSwitch + { + id: moveSettings + bind: Utils.path (bindPrefixGuiMods, "/MoveSettings") + name: qsTr ("Move Settings to top of Device List") + writeAccessLevel: User.AccessUser + } + + MbSwitch { + id: relayOverview + bind: Utils.path (bindPrefixGuiMods, "/ShowRelayOverview") + name: qsTr ("Show Relay overview") + writeAccessLevel: User.AccessUser + } + MbSwitch { + id: tanksTempsOverview + bind: Utils.path (bindPrefixGuiMods, "/ShowTanksTempsDigIn") + name: qsTr ("Show Tanks, Temps, Digital Input overview") + writeAccessLevel: User.AccessUser + } + + MbSwitch + { + id: useEnhGeneratorOverview + bind: Utils.path (bindPrefixGuiMods, "/UseEnhancedGeneratorOverview") + name: qsTr ("Use Enhanced Generator Overview") + writeAccessLevel: User.AccessUser + } + + // duplicate mobile overview on/off here for convenience + MbSwitch { + id: mobileOverview + bind: Utils.path (bindPrefix, "/MobileOverview") + name: qsTr ("Show boat & motorhome overview") + writeAccessLevel: User.AccessUser + } + MbSwitch + { + id: useEnhMobileOverview + bind: Utils.path (bindPrefixGuiMods, "/UseEnhancedMobileOverview") + name: qsTr ("Use Enhanced Mobile Overview") + // When enabled set Enhanced OverviewMobile as default overview + onClicked: + { + if (!checked) + { + // also enable Mobile Overview when turning on use enhanced Mobile Overview + showMobileOverview.setValue (1) + } + } + VBusItem { id: showMobileOverview; bind: Utils.path (bindPrefix, "/MobileOverview") } + writeAccessLevel: User.AccessUser + } + MbItemOptions + { + id: flowOverview + description: qsTr("Flow overview") + bind: Utils.path (bindPrefixGuiMods, "/FlowOverview") + possibleValues: + [ + MbOption {description: qsTr("Victron stock"); value: 0}, + MbOption {description: qsTr("GuiMods simple"); value: 1}, + MbOption {description: qsTr("GuiMods DC Coupled"); value: 2}, + MbOption {description: qsTr("GuiMods AC Coupled"); value: 3} + ] + } + + MbSwitch + { + id: combineLoads + bind: Utils.path (bindPrefixGuiMods, "/EnhancedFlowCombineLoads") + name: qsTr ("Combine AC input/ouput loads") + show: root.showAcCoupledParams + writeAccessLevel: User.AccessInstaller + } + MbSwitch + { + id: showLoadsOnInput + bind: Utils.path (bindPrefixGuiMods, "/ShowEnhancedFlowLoadsOnInput") + name: qsTr ("Show Loads On Input") + show: root.showAcCoupledParams && ! combineLoads.checked + writeAccessLevel: User.AccessInstaller + } + + MbSwitch + { + id: showTanks + bind: Utils.path (bindPrefixGuiMods, "/ShowEnhancedFlowOverviewTanks") + name: qsTr ("Show tanks on Flow Overview") + show: root.showFlowParams + writeAccessLevel: User.AccessUser + } + MbItemOptions + { + id: tankFormat + description: qsTr("Tank bar format") + bind: Utils.path (bindPrefixGuiMods, "/TankBarFormat") + possibleValues: + [ + MbOption {description: qsTr("%"); value: 1}, + MbOption {description: qsTr("units"); value: 2}, + MbOption {description: qsTr("% + units"); value: 0} + ] + } + MbSwitch + { + id: showTemps + bind: Utils.path (bindPrefixGuiMods, "/ShowEnhancedFlowOverviewTemps") + name: qsTr ("Show temperatures on Flow Overview") + show: root.showFlowParams + writeAccessLevel: User.AccessUser + } + MbSwitch + { + id: showBatteryTemps + bind: Utils.path (bindPrefixGuiMods, "/ShowBatteryTempOnFlows") + name: qsTr ("Show battery temperature on Flow Overview") + show: showTemps.item.value == 1 + writeAccessLevel: User.AccessUser + } + MbSwitch + { + id: shortenTankNames + bind: Utils.path (bindPrefixGuiMods, "/ShortenTankNames") + name: qsTr ("Shorten tank names") + writeAccessLevel: User.AccessUser + } + MbEditBox { + id: dcSystemName + description: qsTr("DC System tile name") + item.bind: Utils.path (bindPrefixGuiMods, "/CustomDcSystemName") + maximumLength: 32 + enableSpaceBar: true + } + + MbSwitch + { + id: replaceInactiveAcIn + bind: Utils.path (bindPrefixGuiMods, "/ReplaceInactiveAcIn") + name: qsTr ("Replace AC in if inactive") + writeAccessLevel: User.AccessUser + } + + MbSpinBox { + description: qsTr ("AC Input Limit Preset 1") + item + { + bind: Utils.path (bindPrefixGuiMods, "/AcCurrentLimit/Preset1") + unit: "A" + decimals: 0 + step: 1 + min: 0 + max: 999 + } + writeAccessLevel: User.AccessUser + } + + MbSpinBox { + description: qsTr ("AC Input Limit Preset 2") + item + { + bind: Utils.path (bindPrefixGuiMods, "/AcCurrentLimit/Preset2") + unit: "A" + decimals: 0 + step: 1 + min: 0 + max: 999 + } + writeAccessLevel: User.AccessUser + } + + MbSpinBox { + description: qsTr ("AC Input Limit Preset 3") + item + { + bind: Utils.path (bindPrefixGuiMods, "/AcCurrentLimit/Preset3") + unit: "A" + decimals: 0 + step: 1 + min: 0 + max: 999 + } + writeAccessLevel: User.AccessUser + } + + MbSpinBox { + description: qsTr ("AC Input Limit Preset 4") + item + { + bind: Utils.path (bindPrefixGuiMods, "/AcCurrentLimit/Preset4") + unit: "A" + decimals: 0 + step: 1 + min: 0 + max: 999 + } + writeAccessLevel: User.AccessUser + } + + MbItemOptions + { + id: tempScale + description: qsTr ("Temperature scale") + bind: Utils.path (bindPrefixGuiMods, "/TemperatureScale") + show: ! systemScaleItem.valid + possibleValues: + [ + MbOption { description: "°C"; value: 1 }, + MbOption { description: "°F"; value: 2 }, + MbOption { description: qsTr("both °C & °F"); value: 0 } + ] + writeAccessLevel: User.AccessUser + } + + MbSpinBox { + description: qsTr ("Watt / Kilowatt threshold") + item + { + bind: Utils.path (bindPrefixGuiMods, "/KilowattThreshold") + unit: "W" + decimals: 0 + step: 100 + min: 1000 + max: 10000 + } + writeAccessLevel: User.AccessUser + } + + MbItemOptions + { + id: timeFormat + description: qsTr ("Time format") + bind: Utils.path (bindPrefixGuiMods, "/TimeFormat") + possibleValues: + [ + MbOption { description: qsTr("24 hour"); value: 1 }, + MbOption { description: qsTr("12 hour AM/PM"); value: 2 }, + MbOption { description: qsTr("don't show time"); value: 0 } + ] + writeAccessLevel: User.AccessUser + } + MbItemOptions + { + id: inactiveFlowTiles + description: qsTr ("Inactive Tiles on Flow Overview") + bind: Utils.path (bindPrefixGuiMods, "/ShowInactiveFlowTiles") + show: root.showFlowParams + possibleValues: + [ + MbOption { description: qsTr("Show Dimmed"); value: 1 }, + MbOption { description: qsTr("Show Full"); value: 2 }, + MbOption { description: qsTr("Hide"); value: 0 } + ] + writeAccessLevel: User.AccessUser + } + MbSubMenu + { + description: qsTr("Power Gauges") + subpage: Component { PageSettingsGuiModsGauges {} } + show: root.showFlowParams + } + } +} diff --git a/FileSets/v3.60~25/PageSettingsGuiMods.qml.orig b/FileSets/v3.60~25/PageSettingsGuiMods.qml.orig new file mode 100644 index 00000000..99d438bc --- /dev/null +++ b/FileSets/v3.60~25/PageSettingsGuiMods.qml.orig @@ -0,0 +1,373 @@ +import QtQuick 2 +import com.victron.velib 1.0 +import "utils.js" as Utils + +MbPage { + property string cgwacsPath: "com.victronenergy.settings/Settings/CGwacs" + property string settingsPrefix: "com.victronenergy.settings" + property string batteryLifePath: cgwacsPath + "/BatteryLife" + // Hub4Mode + property int hub4PhaseCompensation: 1 + property int hub4PhaseSplit: 2 + property int hub4Disabled: 3 + // BatteryLifeState + property int batteryLifeStateDisabled: 0 + property int batteryLifeStateRestart: 1 + property int batteryLifeStateDefault: 2 + property int batteryLifeStateAbsorption: 3 + property int batteryLifeStateFloat: 4 + property int batteryLifeStateDischarged: 5 + property int batteryLifeStateForceCharge: 6 + property int batteryLifeStateSustain: 7 + property int batteryLifeStateLowSocCharge: 8 + property int batteryKeepCharged: 9 + property int batterySocGuardDefault: 10 + property int batterySocGuardDischarged: 11 + property int batterySocGuardLowSocCharge: 12 + + property VBusItem systemType: VBusItem { bind: "com.victronenergy.system/SystemType" } + property VBusItem maxChargePowerItem: VBusItem { bind: Utils.path(cgwacsPath, "/MaxChargePower") } + property VBusItem maxDischargePowerItem: VBusItem { bind: Utils.path(cgwacsPath, "/MaxDischargePower") } + property VBusItem socLimitItem: VBusItem { bind: Utils.path(batteryLifePath, "/SocLimit") } + property VBusItem minSocLimitItem: VBusItem { bind: Utils.path(batteryLifePath, "/MinimumSocLimit") } + property VBusItem stateItem: VBusItem { bind: Utils.path(batteryLifePath, "/State") } + property VBusItem hub4Mode: VBusItem { bind: Utils.path(cgwacsPath, "/Hub4Mode") } + property VBusItem maxChargeCurrentControl: VBusItem { bind: "com.victronenergy.system/Control/MaxChargeCurrent" } + property VBusItem scheduleSoc: VBusItem { bind: "com.victronenergy.system/Control/ScheduledSoc" } + property VBusItem dEssModeItem: VBusItem { bind: "com.victronenergy.settings/Settings/DynamicEss/Mode" } + + title: systemType.value === "Hub-4" ? systemType.value : qsTr("ESS") + model: acSystems.rowCount > 0 ? hasAcSystem : (systemType.value === "ESS" || systemType.value === "Hub-4" ? hub4Settings : noHub4) + + property VeQItemSortTableModel acSystems: VeQItemSortTableModel { + filterFlags: VeQItemSortTableModel.FilterOffline + dynamicSortFilter: true + filterRole: VeQItemTableModel.UniqueIdRole + filterRegExp: "^dbus/com\.victronenergy\.acsystem\." + model: DBusServices + } + + VisibleItemModel { + id: noHub4 + + MbItemText { + text: qsTr("No ESS Assistant found") + } + } + + VisibleItemModel { + id: hasAcSystem + + MbItemText { + text: qsTr("For Multi-RS and HS19 devices, ESS settings\n" + + "can be accessed from the device list") + } + } + + function isBatteryLifeActive(state) { + switch (state) { + case batteryLifeStateRestart: + case batteryLifeStateDefault: + case batteryLifeStateAbsorption: + case batteryLifeStateFloat: + case batteryLifeStateDischarged: + case batteryLifeStateForceCharge: + case batteryLifeStateSustain: + case batteryLifeStateLowSocCharge: + return true + default: + return false + } + } + + function isBatterySocGuardActive(state) { + switch (state) { + case batterySocGuardDefault: + case batterySocGuardDischarged: + case batterySocGuardLowSocCharge: + return true + default: + return false + } + } + + VisibleItemModel { + id: hub4Settings + + MbItemOptions { + function getLocalValue(hub4Mode, state) { + if (hub4Mode === undefined || state === undefined) + return undefined + if (hub4Mode === hub4Disabled) + return 3 + if (isBatteryLifeActive(state)) + return 0 + if (isBatterySocGuardActive(state)) + return 1 + if (state === batteryKeepCharged) + return 2 + return 0 + } + + description: qsTr("Mode") + localValue: getLocalValue(hub4Mode.value, stateItem.value) + possibleValues:[ + MbOption { description: qsTr("Optimized (with BatteryLife)"); value: 0 }, + MbOption { description: qsTr("Optimized (without BatteryLife)"); value: 1 }, + MbOption { description: qsTr("Keep batteries charged"); value: 2 }, + MbOption { description: qsTr("External control"); value: 3 } + ] + onLocalValueChanged: { + if (localValue === undefined) + return + // Hub 4 mode + if (localValue === 3 && hub4Mode.value !== hub4Disabled) { + hub4Mode.setValue(hub4Disabled) + } else if (localValue !== 3 && hub4Mode.value === hub4Disabled) { + hub4Mode.setValue(hub4PhaseCompensation) + } + // BatteryLife state + switch (localValue) { + case 0: + if (!isBatteryLifeActive(stateItem.value)) + stateItem.setValue(batteryLifeStateRestart) + break + case 1: + if (!isBatterySocGuardActive(stateItem.value)) + stateItem.setValue(batterySocGuardDefault) + break + case 2: + stateItem.setValue(batteryKeepCharged) + break + case 3: + stateItem.setValue(batteryLifeStateDisabled) + break + } + } + } + + MbItemOptions { + id: withoutGridMeter + description: qsTr("Grid metering") + bind: Utils.path(cgwacsPath, '/RunWithoutGridMeter') + show: hub4Mode.value !== hub4Disabled + enabled: userHasWriteAccess + possibleValues:[ + MbOption { description: qsTr("External meter"); value: 0 }, + MbOption { description: qsTr("Inverter/Charger"); value: 1 } + ] + } + + MbSwitch { + id: acOutInUse + bind: Utils.path(settingsPrefix, "/Settings/SystemSetup/HasAcOutSystem") + name: qsTr("Inverter AC output in use") + show: withoutGridMeter.value == 0 + } + + MbItemOptions { + description: qsTr("Self-consumption from battery") + bind: Utils.path(cgwacsPath, "/BatteryUse") + show: withoutGridMeter.value == 0 && acOutInUse.item.value == 1 + possibleValues:[ + MbOption { description: qsTr("All system loads"); value: 0 }, + MbOption { description: qsTr("Only critical loads"); value: 1 } + ] + } + + MbItemOptions { + description: qsTr("Multiphase regulation") + bind: hub4Mode.bind + show: hub4Mode.value !== hub4Disabled && stateItem.value !== batteryKeepCharged + enabled: userHasWriteAccess + possibleValues:[ + MbOption { description: qsTr("Total of all phases"); value: hub4PhaseCompensation }, + MbOption { description: qsTr("Individual phase"); value: hub4PhaseSplit } + ] + onOptionSelected: { + if (newValue === hub4PhaseSplit) { + toast.createToast(qsTr("Each phase is regulated to individually achieve the grid setpoint (system efficiency is decreased).\n\n" + + "CAUTION: Use only if required by the utility provider"), 15000); + } else if (newValue === hub4PhaseCompensation) { + toast.createToast(qsTr("The total of all phases is intelligently regulated to achieve the grid setpoint (system efficiency is optimised).\n\n" + + "Use unless prohibited by the utility provider"), 15000); + } + } + } + + MbSpinBox { + id: minSocLimit + description: qsTr("Minimum SOC (unless grid fails)") + enabled: userHasWriteAccess + show: hub4Mode.value !== hub4Disabled && stateItem.value !== batteryKeepCharged + item { + bind: Utils.path(batteryLifePath, "/MinimumSocLimit") + decimals: 0 + unit: "%" + min: 0 + max: 100 + step: 5 + } + } + + MbItemValue { + id: socLimit + description: qsTr("Active SOC limit") + show: hub4Mode.value !== hub4Disabled && isBatteryLifeActive(stateItem.value) + item { + value: Math.max(minSocLimitItem.value, socLimitItem.value) + unit: '%' + } + } + + MbItemOptions { + description: qsTr("BatteryLife state") + value: stateItem.value + readonly: true + show: hub4Mode.value !== hub4Disabled && isBatteryLifeActive(stateItem.value) + possibleValues:[ + // Values below taken from MaintenanceState enum in dbus-cgwacs + MbOption { description: qsTr("Self-consumption"); value: 2 }, + MbOption { description: qsTr("Self-consumption"); value: 3 }, + MbOption { description: qsTr("Self-consumption"); value: 4 }, + MbOption { description: qsTr("Discharge disabled"); value: 5 }, + MbOption { description: qsTr("Slow charge"); value: 6 }, + MbOption { description: qsTr("Sustain"); value: 7 }, + MbOption { description: qsTr("Recharge"); value: 8 } + ] + } + + MbSwitch { + id: maxChargePowerSwitch + name: qsTr("Limit charge power") + checked: maxChargePowerItem.value >= 0 + enabled: userHasWriteAccess + show: hub4Mode.value !== hub4Disabled && !(maxChargeCurrentControl.valid && maxChargeCurrentControl.value) + onCheckedChanged: { + if (checked && maxChargePowerItem.value < 0) + maxChargePowerItem.setValue(1000) + else if (!checked && maxChargePowerItem.value >= 0) + maxChargePowerItem.setValue(-1) + } + } + + MbSpinBox { + id: maxChargePower + description: qsTr("Maximum charge power") + enabled: userHasWriteAccess + show: maxChargePowerSwitch.show && maxChargePowerSwitch.checked + item { + bind: Utils.path(cgwacsPath, "/MaxChargePower") + decimals: 0 + unit: "W" + min: 0 + max: 200000 + step: 50 + } + } + + MbSwitch { + id: maxInverterPowerSwitch + name: qsTr("Limit inverter power") + checked: maxDischargePowerItem.value >= 0 + enabled: userHasWriteAccess + show: hub4Mode.value !== hub4Disabled && stateItem.value !== batteryKeepCharged + onCheckedChanged: { + if (checked && maxDischargePowerItem.value < 0) + maxDischargePowerItem.setValue(1000) + else if (!checked && maxDischargePowerItem.value >= 0) + maxDischargePowerItem.setValue(-1) + } + } + + MbSpinBox { + id: maxDischargePower + description: qsTr("Maximum inverter power") + enabled: userHasWriteAccess + show: maxInverterPowerSwitch.show && maxInverterPowerSwitch.checked + item { + bind: Utils.path(cgwacsPath, "/MaxDischargePower") + decimals: 0 + unit: "W" + min: 0 + max: 300000 + step: 50 + } + } + + MbSpinBox { + description: qsTr("Grid setpoint") + show: hub4Mode.value !== hub4Disabled + enabled: userHasWriteAccess + item { + bind: "com.victronenergy.settings/Settings/CGwacs/AcPowerSetPoint" + decimals: 0 + unit: "W" + step: 10 + } + } + + MbSubMenu { + id: feedinSetupItem + description: qsTr("Grid feed-in") + show: hub4Mode.value !== hub4Disabled + subpage: Component { + PageSettingsHub4Feedin { + title: feedinSetupItem.description + } + } + } + + MbSubMenu { + id: peakShaveSetupMenu + description: qsTr("Peak shaving") + show: hub4Mode.value !== hub4Disabled + subpage: Component { + PageSettingsHub4Peakshaving { + title: peakShaveSetupMenu.description + } + } + } + + MbSubMenu { + id: scheduleSettings + property string bindPrefix: "com.victronenergy.settings/Settings/CGwacs/BatteryLife/Schedule/Charge/" + description: qsTr("Scheduled charge levels") + show: hub4Mode.value !== hub4Disabled && stateItem.value !== batteryKeepCharged + item: VBusItem { value: scheduleSoc.valid ? qsTr("Active (%1)").arg(scheduleSoc.text) : qsTr("Inactive") } + subpage: Component { + MbPage { + title: scheduleSettings.description + model: VisibleItemModel { + ChargeScheduleItem { bindPrefix: scheduleSettings.bindPrefix; scheduleNumber: 0 } + ChargeScheduleItem { bindPrefix: scheduleSettings.bindPrefix; scheduleNumber: 1 } + ChargeScheduleItem { bindPrefix: scheduleSettings.bindPrefix; scheduleNumber: 2 } + ChargeScheduleItem { bindPrefix: scheduleSettings.bindPrefix; scheduleNumber: 3 } + ChargeScheduleItem { bindPrefix: scheduleSettings.bindPrefix; scheduleNumber: 4 } + } + } + } + } + + MbSubMenu { + id: dEssSetupItem + description: qsTr("Dynamic ESS") + show: (dEssModeItem.value > 0 || user.accessLevel >= User.AccessService) && hub4Mode.value !== hub4Disabled && stateItem.value !== batteryKeepCharged + subpage: Component { + PageSettingsDynamicEss { + title: dEssSetupItem.description + } + } + } + + MbSubMenu { + id: deviceItem + description: qsTr("Debug") + show: hub4Mode.value !== hub4Disabled && user.accessLevel >= User.AccessService + backgroundColor: mbStyle.backgroundColorService + subpage: Component { + PageHub4Debug { } + } + } + } +} diff --git a/FileSets/v3.60~25/PowerGauge.qml b/FileSets/v3.60~25/PowerGauge.qml new file mode 100644 index 00000000..9593259b --- /dev/null +++ b/FileSets/v3.60~25/PowerGauge.qml @@ -0,0 +1,320 @@ +// displays value as a bar surrounded by three range regions +// use for I/O, PV inverter & charger + +import QtQuick 2 +import "utils.js" as Utils +import com.victron.velib 1.0 + +Item { + id: root + +////// GuiMods — DarkMode + property VBusItem darkModeItem: VBusItem { bind: "com.victronenergy.settings/Settings/GuiMods/DarkMode" } + property bool darkMode: darkModeItem.valid && darkModeItem.value == 1 + + property variant connection + // connection2 accommodates combined PV inverter AC input and AC output + property variant connection2: undefined + property bool includeConnection2: connection2 != undefined + + property bool reversePower: false + property bool useInputCurrentLimit: false + property variant endLabelFontSize: 16 + property color endLabelBackgroundColor: "transparent" + + property int reportedPhaseCount1: connection == undefined ? 0 : connection.phaseCount == undefined || ! connection.phaseCount.valid ? 1 : connection.phaseCount.value + property int phaseCount1: reportedPhaseCount1 < 2 ? reportedPhaseCount1 : (connection.isAcOutput ? connection.l2AndL1OutSummed : connection.splitPhaseL2PassthruDisabled) ? 1 : connection.phaseCount.value + + property int reportedPhaseCount2: connection2 == undefined ? 0 : connection2.phaseCount == undefined || ! connection2.phaseCount.valid ? 1 : connection2.phaseCount.value + property int phaseCount2: reportedPhaseCount2 < 2 ? reportedPhaseCount2 : (connection2.isAcOutput ? connection2.l2AndL1OutSummed : connection2.splitPhaseL2PassthruDisabled) ? 1 : connection2.phaseCount.value + + property int phaseCount: includeConnection2 ? Math.max (phaseCount1, phaseCount2) : phaseCount1 + + property string maxForwardPowerParameter: "" + VBusItem { id: maxForwardLimitItem; bind: root.maxForwardPowerParameter } + property string maxForwardPowerParameter2: "" + VBusItem { id: maxForwardLimitItem2; bind: root.maxForwardPowerParameter2 } + + property string maxReversePowerParameter: "" + VBusItem { id: maxReverseLimitItem; bind: root.maxReversePowerParameter } + + property real inPowerLimit: sys.acInput.inCurrentLimit.valid ? sys.acInput.inCurrentLimit.value * sys.acInput.voltageL1.value : 0 + + property real maxForwardPower1: maxForwardLimitItem.valid ? maxForwardLimitItem.value : 0 + property real maxForwardPower2: maxForwardLimitItem2.valid ? maxForwardLimitItem2.value : 0 + property real maxForwardLimit: useInputCurrentLimit ? inPowerLimit : maxForwardPower1 + maxForwardPower2 + property real maxReverseLimit: maxReverseLimitItem.valid ? maxReverseLimitItem.value : 0 + // overload range is 10% of forward to reverse limits + property real overload: (maxForwardLimit + maxReverseLimit) * 0.1 + property real maxForwardDisplayed: maxForwardLimit > 0 ? maxForwardLimit + overload : 0 + property real maxReverseDisplayed: maxReverseLimit > 0 ? maxReverseLimit + overload : 0 + property real totalPowerDisplayed: maxForwardDisplayed + maxReverseDisplayed + + property bool showLabels: false + property variant endLabelColor: "white" + property real labelOffset: 15 + property real showLeftLabel: showGauge && showLabels && maxReverseLimit != 0 + property bool showRightLabel: showGauge && showLabels && maxForwardLimit != 0 + property int labelCount: (showLeftLabel ? 1 : 0) + (showRightLabel ? 1 : 0) + + property bool showGauge: root.connection != undefined && totalPowerDisplayed > 0 && phaseCount > 0 + property real scaleFactor: showGauge ? (root.width - (labelCount * labelOffset)) / totalPowerDisplayed : 0 + property real zeroOffset: showGauge ? ( maxReverseDisplayed * scaleFactor + (showLeftLabel ? labelOffset : 0 )) : 0 + + property int barSpacing: phaseCount > 0 ? Math.max (height / (phaseCount + 1), 2) : 0 + property int barHeight: barSpacing < 3 ? barSpacing : barSpacing - 1 + property int firstBarVertPos: (height - barSpacing * phaseCount) / 2 + property real bar1offset + property real bar2offset + property real bar3offset + + property color bar1color: "black" + property color bar2color: "black" + property color bar3color: "black" + + // left end label + Rectangle + { + anchors.fill: leftlabelText + color: endLabelBackgroundColor + visible: showLeftLabel + } + TileText + { + id: leftlabelText + text: "S" + color: endLabelColor + font.pixelSize: endLabelFontSize + width: labelOffset + anchors + { + verticalCenter: root.verticalCenter + verticalCenterOffset: 1 + left: root.left + } + visible: showLeftLabel + } + // right end label + Rectangle + { + anchors.fill: rightLabelText + color: endLabelBackgroundColor + visible: showRightLabel + } + TileText + { + id: rightLabelText + text: "C" + color: endLabelColor + font.pixelSize: endLabelFontSize + width: labelOffset + anchors + { + verticalCenter: leftlabelText.verticalCenter + right: root.right + } + visible: showRightLabel + } + // overload range Left + Rectangle + { + id: overloadLeft + width: showGauge ? scaleFactor * (maxReverseDisplayed - maxReverseLimit) : 0 + height: root.height + clip: true +////// GuiMods — DarkMode + color: !darkMode ? "#ffb3b3" : "#bf8686" + visible: showGauge + anchors + { + top: root.top + left: root.left; leftMargin: showLeftLabel ? labelOffset : 0 + } + } + // OK range (both left and right in a single rectangle) + Rectangle + { + id: okRange + width: showGauge ? scaleFactor * (maxForwardLimit + maxReverseLimit) : 0 + height: root.height + clip: true +////// GuiMods — DarkMode + color: !darkMode ? "#99ff99" : "#73bf73" + visible: showGauge + anchors + { + top: root.top + left: overloadLeft.right + } + } + // overload range right + Rectangle + { + id: overloadRight + width: showGauge ? scaleFactor * (maxForwardDisplayed - maxForwardLimit) : 0 + height: root.height + clip: true +////// GuiMods — DarkMode + color: !darkMode ? "#ffb3b3" : "#bf8686" + visible: showGauge + anchors + { + top: root.top + left: okRange.right + } + } + + // actual bars + Rectangle + { + id: bar1 + width: phaseCount >= 1 ? calculateBar1width () : 0 + height: barHeight + clip: true + color: root.bar1color + anchors + { + top: root.top; topMargin: firstBarVertPos + left: root.left; leftMargin: root.bar1offset + + } + visible: showGauge && phaseCount >= 1 + } + Rectangle + { + id: bar2 + width: phaseCount >= 2 ? calculateBar2width () : 0 + height: barHeight + clip: true + color: root.bar2color + anchors + { + top: root.top; topMargin: firstBarVertPos + barSpacing + left: root.left; leftMargin: root.bar2offset + } + visible: showGauge && phaseCount >= 2 + } + Rectangle + { + id: bar3 + width: phaseCount >= 3 ? calculateBar3width () : 0 + height: barHeight + clip: true + color: root.bar3color + anchors + { + top: root.top; topMargin: firstBarVertPos + barSpacing * 2 + left: root.left; leftMargin: root.bar3offset + } + visible: showGauge && phaseCount >= 3 + } + + // zero line - draw last so it's on top + Rectangle + { + id: zeroLine + width: 1 + height: root.height + clip: true + color: "black" + visible: showGauge && maxReverseLimit > 0 + anchors + { + top: root.top + left: root.left + leftMargin: zeroOffset + } + } + + function calculateBar1width () + { + var currentValue = 0.0, barWidth + if (root.connection.powerL1 != undefined) + currentValue += root.connection.powerL1.valid ? root.connection.powerL1.value : 0 + else if (root.connection.power != undefined) + currentValue += root.connection.power.valid ? root.connection.power.value : 0 + if (includeConnection2) + { + if (root.connection2.powerL1 != undefined) + currentValue += root.connection2.powerL1.valid ? root.connection2.powerL1.value : 0 + else if (root.connection2.power != undefined) + currentValue += root.connection2.power.valid ? root.connection2.power.value : 0 + } + + if (reversePower) + currentValue = -currentValue + + root.bar1color = getBarColor (currentValue) + barWidth = Math.min ( Math.max (currentValue, -maxReverseDisplayed), maxForwardDisplayed) * scaleFactor + // left of bar is at 0 point + if (barWidth >= 0) + { + root.bar1offset = zeroOffset + return barWidth + } + // RIGHT of bar is at 0 point + else + { + root.bar1offset = zeroOffset + barWidth + return -barWidth + } + } + function calculateBar2width () + { + var currentValue, barWidth + currentValue = root.connection.powerL2.valid ? root.connection.powerL2.value : 0 + if (includeConnection2) + currentValue += root.connection2.powerL2.valid ? root.connection2.powerL2.value : 0 + + if (reversePower) + currentValue = -currentValue + root.bar2color = getBarColor (currentValue) + barWidth = Math.min ( Math.max (currentValue, -maxReverseDisplayed), maxForwardDisplayed) * scaleFactor + // left of bar is at 0 point + if (barWidth >= 0) + { + root.bar2offset = zeroOffset + return barWidth + } + // RIGHT of bar is at 0 point + else + { + root.bar2offset = zeroOffset + barWidth + return -barWidth + } + } + function calculateBar3width () + { + var currentValue, barWidth + currentValue = root.connection.powerL3.valid ? root.connection.powerL3.value : 0 + if (includeConnection2) + currentValue += root.connection2.powerL3.valid ? root.connection2.powerL3.value : 0 + + if (reversePower) + currentValue = -currentValue + root.bar3color = getBarColor (currentValue) + barWidth = Math.min ( Math.max (currentValue, -maxReverseDisplayed), maxForwardDisplayed) * scaleFactor + // left of bar is at 0 point + if (barWidth >= 0) + { + root.bar3offset = zeroOffset + return barWidth + } + // RIGHT of bar is at 0 point + else + { + root.bar3offset = zeroOffset + barWidth + return -barWidth + } + } + + function getBarColor (currentValue) + { + if (currentValue > maxForwardLimit || currentValue < -maxReverseLimit) +////// GuiMods — DarkMode + return !darkMode ? "#ff0000" : "#bf0000" + else +////// GuiMods — DarkMode + return !darkMode ? "#008000" : "#006000" + } +} diff --git a/FileSets/v3.60~25/PowerGauge.qml.orig b/FileSets/v3.60~25/PowerGauge.qml.orig new file mode 100644 index 00000000..6efa32bf --- /dev/null +++ b/FileSets/v3.60~25/PowerGauge.qml.orig @@ -0,0 +1,170 @@ +import QtQuick 2 +import "utils.js" as Utils + +Item { + id: root + + property string systemPrefix: "com.victronenergy.system" + property string vebusPrefix: _vebusService.valid ? _vebusService.value : "" + + property variant battery: _battery + property alias dcSystem: _dcSystem + property alias pvCharger: _pvCharger + property alias pvOnAcIn1: _pvOnAcIn1 + property alias pvOnAcIn2: _pvOnAcIn2 + property alias pvOnAcOut: _pvOnAcOut + property alias inverterChargerDc: _inverterChargerDc + property alias acLoad: _acLoad + property alias acInLoad: _acInLoad + property alias acOutLoad: _acOutLoad + property alias grid: _grid + property alias genset: _genset + property alias acInput: _activein + property VBusItem systemType: VBusItem { bind: Utils.path(systemPrefix, "/SystemType") } + property bool hasGridMeter: _hasGridMeter.valid + property variant acSource: _acSource.value + property VBusItem preferRenewableEnergy: VBusItem { bind: Utils.path(vebusPrefix, "/Dc/0/PreferRenewableEnergy") } + property VBusItem remoteGeneratorSelected: VBusItem { bind: Utils.path(vebusPrefix, "/Ac/State/RemoteGeneratorSelected") } + + property alias pvOnGrid: _pvOnAcIn2 + + property int batteryStateIdle: 0 + property int batteryStateCharging: 1 + property int batteryStateDischarging: 2 + + property int acSourceNotAvailable: 0 + property int acSourceGrid: 1 + property int acSourceGenset: 2 + property int acSourceShore: 3 // same as grid + + property alias pvInvertersProductIds: _pvInvertersProductIds + property alias batteryProductId: _batteryProductId + + VBusItem { + id: _pvInvertersProductIds + bind: Utils.path(systemPrefix, "/PvInvertersProductIds") + } + + VBusItem { + id: _batteryProductId + bind: Utils.path(systemPrefix, "/Dc/Battery/ProductId") + } + + VBusItem { + id: _vebusService + bind: Utils.path(systemPrefix, "/VebusService") + } + + QtObject { + id: _pvCharger + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Pv/Power"); unit: "W"} + } + + ObjectAcConnection { + id: _pvOnAcOut + bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnOutput") + } + + ObjectAcConnection { + id: _pvOnAcIn1 + bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGenset") + } + + ObjectAcConnection { + id: _pvOnAcIn2 + bindPrefix: Utils.path(systemPrefix, "/Ac/PvOnGrid") + } + + ObjectAcConnection { + id: _genset + splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0 + bindPrefix: Utils.path(systemPrefix, "/Ac/Genset") + } + + VBusItem { + id: _acSource + bind: Utils.path(systemPrefix, "/Ac/ActiveIn/Source") + } + + VBusItem { + id: _hasGridMeter + bind: Utils.path(systemPrefix, "/Ac/Grid/DeviceType") + } + + /* + * Single Multis that can be split-phase reports NrOfPhases of 2 + * When L2 is disconnected from the input the output L1 and L2 + * are shorted. This item indicates if L2 is passed through + * from AC-in to AC-out. + * 1: L2 is being passed through from AC-in to AC-out. + * 0: L1 and L2 are shorted together. + * invalid: The unit is configured in such way that its L2 output is not used. + */ + + VBusItem { + id: _splitPhaseL2Passthru + bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2Passthru") + } + + VBusItem { + id: _l2L1OutSummed + bind: Utils.path(vebusPrefix, "/Ac/State/SplitPhaseL2L1OutSummed") + } + + + ObjectAcConnection { + id: _grid + splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0 + bindPrefix: Utils.path(systemPrefix, "/Ac/Grid") + } + + ObjectAcConnection { + id: _activein + bindPrefix: Utils.path(systemPrefix, "/Ac/ActiveIn") + } + + ObjectAcConnection { + id: _acLoad + l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0) + isAcOutput: true + bindPrefix: Utils.path(systemPrefix, "/Ac/Consumption") + } + + ObjectAcConnection { + id: _acOutLoad + l2AndL1OutSummed: _l2L1OutSummed.valid && (_l2L1OutSummed.value !== 0) + isAcOutput: true + bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnOutput") + } + + ObjectAcConnection { + id: _acInLoad + splitPhaseL2PassthruDisabled: _splitPhaseL2Passthru.value === 0 + bindPrefix: Utils.path(systemPrefix, "/Ac/ConsumptionOnInput") + } + + ObjectAcConnection { + id: _acUnknown + } + + QtObject { + id: _inverterChargerDc + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/InverterCharger/Power"); unit: "W"} + } + + QtObject { + id: _battery + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Power"); unit: "W"} + property VBusItem voltage: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Voltage"); unit: "V"} + property VBusItem current: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Current"); unit: "A"} + property VBusItem soc: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/Soc"); unit: "%"} + + // Get the battery charge state, see batteryState properties + property VBusItem state: VBusItem { bind: Utils.path(systemPrefix, "/Dc/Battery/State")} + } + + QtObject { + id: _dcSystem + property VBusItem power: VBusItem { bind: Utils.path(systemPrefix, "/Dc/System/Power"); unit: "W"} + } +} diff --git a/FileSets/v3.60~25/TileDigIn.qml b/FileSets/v3.60~25/TileDigIn.qml new file mode 100644 index 00000000..7bea849c --- /dev/null +++ b/FileSets/v3.60~25/TileDigIn.qml @@ -0,0 +1,133 @@ +// New for GuiMods to display digital inputs +// based on TileTank.qml + +import QtQuick 2 +import "utils.js" as Utils +import "tanksensor.js" as TankSensor + +Tile { + id: root + + property string bindPrefix: serviceName + property VBusItem nameItem: VBusItem { bind: Utils.path(bindPrefix, "/CustomName") } + property VBusItem deviceItem: VBusItem { bind: Utils.path(bindPrefix, "/DeviceInstance") } + property VBusItem aggregateItem: VBusItem { bind: Utils.path(bindPrefix, "/Aggregate") } + property string digInName: nameItem.valid && nameItem.value != "" ? nameItem.value : getType (type) + property VBusItem typeItem: VBusItem { bind: Utils.path(bindPrefix, "/Type") } + property VBusItem stateItem: VBusItem { bind: Utils.path(bindPrefix, "/State") } + property bool isPulseCounter: aggregateItem.valid + // pulse counter doesn't have /Type so fill it in here + property int type: isPulseCounter ? 1 : typeItem.valid ? typeItem.value : 0 + + property variant bkgdColors: [ "#b3b3b3", "#4aa3df", "#1abc9c", "#F39C12", "#95a5a6", "#95a5a6","#dcc6e0", "#f1a9a0", "#7f8c8d", "#ebbc3a" ] + property color bkgdColor: type > 0 && type < 10 ? bkgdColors [type] : "#b3b3b3" + property variant units: ["m3", "L", "gal", "gal"] + + + function getType(type) + { + switch (type) + { + case 0: + return qsTr("Disabled") + case 1: + return qsTr("Pulse meter") + case 2: + return qsTr("Door alarm") + case 3: + return qsTr("Bilge pump") + case 4: + return qsTr("Bilge alarm") + case 5: + return qsTr("Burglar alarm") + case 6: + return qsTr("Smoke alarm") + case 7: + return qsTr("Fire alarm") + case 8: + return qsTr("CO2 alarm") + case 9: + return qsTr("Generator") + case 10: + return qsTr("Generic I/O") +//// added for ExtTransferSwitch package + case 11: + return qsTr("Touch enable") + case 12: + return qsTr("Transfer switch") + default: + return "Unknown" + } + } + + function getState(st) + { + switch (st) + { + case 0: + return qsTr("Low") + case 1: + return qsTr("High") + case 2: + return qsTr("Off") + case 3: + return qsTr("On") + case 4: + return qsTr("No") + case 5: + return qsTr("Yes") + case 6: + return qsTr("Open") + case 7: + return qsTr("Closed") + case 8: + return qsTr("Ok") + case 9: + return qsTr("Alarm") + case 10: + return qsTr("Running") + case 11: + return qsTr("Stopped") +//// added for ExtTransferSwitch package + case 12: + return qsTr("On Generator") + case 13: + return qsTr("On Grid") + default: + return qsTr("Unknown") + } + + } + + title: digInName + " (In " + (deviceItem.valid ? (deviceItem.value.toString ()) : "?") + ")" + + color: bkgdColor + + VBusItem + { + id: unitItem + bind: Utils.path("com.victronenergy.settings/Settings/System/VolumeUnit") + } + + values: Item + { + width: root.width - 10 + height: 12 + TileText + { + width: root.width + text: + { + if (isPulseCounter) + return aggregateItem.value.toString() + (unitItem.valid ? units[unitItem.value] : "??") + else + return stateItem.valid ? getState (stateItem.value) : "??" + } + horizontalAlignment: Text.AlignHCenter + anchors + { + horizontalCenter: parent.horizontalCenter + } + } + } +} diff --git a/FileSets/v3.60~17/TileDigIn.qml.orig b/FileSets/v3.60~25/TileDigIn.qml.orig similarity index 100% rename from FileSets/v3.60~17/TileDigIn.qml.orig rename to FileSets/v3.60~25/TileDigIn.qml.orig diff --git a/FileSets/v3.60~25/TileRelay.qml b/FileSets/v3.60~25/TileRelay.qml new file mode 100644 index 00000000..d8b6feaa --- /dev/null +++ b/FileSets/v3.60~25/TileRelay.qml @@ -0,0 +1,498 @@ +// New for GuiMods to display and control relays on separate overview page + +import QtQuick 2 +import "utils.js" as Utils + +Tile { + id: root + + property string systemPrefix: "com.victronenergy.system" + property string settingsPrefix: "com.victronenergy.settings" + property string functionPath: relayNumber === 0 ? "/Settings/Relay/Function" : "/Settings/Relay/" + relayNumber + "/Function" + property string polarityPath: relayNumber === 0 ? "/Settings/Relay/Polarity" : "/Settings/Relay/" + relayNumber + "/Polarity" + + property int relayFunction: 0 + property bool relayInverted: polarityItem.valid ? polarityItem.value : false + property bool relayActive: flase + + property string activeText: "" + property string inactiveText: "" + property string offButtonText: "" + property string onButtonText: "" + property string autoButtonText: "" + property string functionText: "" + property bool autoButtonActive: false + property bool offButtonActive: false + property bool onButtonActive: false + +////// GuiMods — DarkMode + property VBusItem darkModeItem: VBusItem { bind: "com.victronenergy.settings/Settings/GuiMods/DarkMode" } + property bool darkMode: darkModeItem.valid && darkModeItem.value == 1 + + + VBusItem + { + id: stateItem + bind: Utils.path(systemPrefix, "/Relay/", relayNumber, "/State") + onValueChanged: updateButtons () + } + VBusItem + { + id: nameItem + bind: Utils.path(settingsPrefix, "/Settings/Relay/", relayNumber, "/CustomName") + } + VBusItem + { + id: functionItem + bind: Utils.path(settingsPrefix, functionPath) + onValueChanged: updateFunction () + } + VBusItem + { + id: polarityItem + bind: Utils.path(settingsPrefix, polarityPath) + } + VBusItem + { + id: generatorManualStartItem + bind: Utils.path("com.victronenergy.generator.startstop0" , "/ManualStart") + onValidChanged: updateButtons () + onValueChanged: updateButtons () + } + VBusItem + { + id: generatorAutoRunItem + bind: Utils.path(settingsPrefix, "/Settings/Generator0/AutoStartEnabled") + onValidChanged: updateButtons () + onValueChanged: updateButtons () + } + VBusItem + { + id: generatorStateItem + bind: Utils.path("com.victronenergy.generator.startstop0" , "/State") + } + VBusItem + { + id: generatorConditionItem + bind: Utils.path("com.victronenergy.generator.startstop0" , "/RunningByConditionCode") + } + VBusItem + { + id: generatorExternalOverrideItem + bind: Utils.path("com.victronenergy.generator.startstop0" , "/ExternalOverride") + } + VBusItem + { + id: pumpModeItem + bind: Utils.path(settingsPrefix, "/Settings/Pump0/Mode") + onValidChanged: updateButtons () + onValueChanged: updateButtons () + } + + Component.onCompleted: updateFunction () + +////// GuiMods — DarkMode + color: !darkMode ? "#d9d9d9" : "#202020" + border.color: !darkMode ? "#fff" : "#707070" + + function doScroll() + { + relayName.doScroll () + relayState.doScroll () + } + + values: Item + { + anchors.horizontalCenter: parent.horizontalCenter + Column + { + width: root.width + spacing: 4 + visible: true + anchors + { + horizontalCenter: parent.horizontalCenter + top: parent.top + } + Text + { + font.pixelSize: 12 + font.bold: true +////// GuiMods DarkMode + color: !darkMode ? "black" : "gray" + anchors.horizontalCenter: parent.horizontalCenter + horizontalAlignment: Text.AlignHCenter + text: "Relay " + (relayNumber + 1) + } + MarqueeEnhanced + { + id: relayName + width: parent.width - 4 + text: nameItem.valid && nameItem.value != "" ? nameItem.value : " " + fontSize: 12 + bold: true +////// GuiMods DarkMode + textColor: !darkMode ? "black" : "gray" + scroll: false + } + Text + { + font.pixelSize: 12 + font.bold: true +////// GuiMods DarkMode + color: !darkMode ? "black" : "gray" + anchors.horizontalCenter: parent.horizontalCenter + horizontalAlignment: Text.AlignHCenter + text: functionText + } + + MarqueeEnhanced + { + id: relayState + width: parent.width - 4 + fontSize: 12 + bold: true +////// GuiMods DarkMode + textColor: !darkMode ? "black" : "gray" + scroll: false + text: + { + // special handling for generator + if (relayFunction == 1) + { + if (generatorExternalOverrideItem.valid && generatorExternalOverrideItem.value == 1) + return qsTr ("External override - stopped") + else if (!generatorStateItem.valid) + return qsTr ("Error") + else if (generatorStateItem.value == 2) + return qsTr("Warm-up") + else if (generatorStateItem.value == 3) + return qsTr("Cool-down") + else if (generatorStateItem.value == 4) + return qsTr("Stopping") + else if (generatorConditionItem.valid) + { + switch (generatorConditionItem.value) + { + case 0: + return qsTr ("Stopped") + case 1: + return qsTr ("Man run") + case 2: + return qsTr ("Test run") + case 3: + return qsTr ("Loss of comms run") + case 4: + return qsTr ("SOC run") + case 5: + return qsTr ("Load run") + case 6: + return qsTr ("Battery current run") + case 7: + return qsTr ("Battery voltage run") + case 8: + return qsTr ("Inverter temperature run") + case 9: + return qsTr ("Inverter overload run") + default: + return "??" + } + } + else + return "??" + } + else if (stateItem.valid) + { + if (relayActive) + return activeText + else + return inactiveText + } + else + return "??" + } + } + // spacer + Text + { + font.pixelSize: 4 + font.bold: true + color: "black" + height: 4 + anchors.horizontalCenter: parent.horizontalCenter + horizontalAlignment: Text.AlignHCenter + text: " " + } + Button + { + id: onButton +////// GuiMods - DarkMode + baseColor: !darkMode ? (onButtonActive ? "green" : "#e6ffe6") : (onButtonActive ? "green" : "#003000") + pressedColor: "#979797" + height: 40 + width: parent.width - 6 + anchors.horizontalCenter: parent.horizontalCenter + onClicked: buttonPress (1) + content: TileText + { + text: onButtonText; font.bold: true; + color: onButtonActive ? "white" : "black" + } + } + Button + { + id: offButton +////// GuiMods - DarkMode + baseColor: !darkMode ? (offButtonActive ? "black" : "#e6e6e6") : (offButtonActive ? "black" : "gray") + pressedColor: "#979797" + height: 40 + width: parent.width - 6 + anchors.horizontalCenter: parent.horizontalCenter + onClicked: buttonPress (2) + content: TileText + { + text: offButtonText; font.bold: true; + color: offButtonActive ? "white" : "black" + } + } + Button + { + id: autoButton +////// GuiMods - DarkMode + baseColor: !darkMode ? (autoButtonActive ? "orange" : "#ffedcc") : (autoButtonActive ? "orange" : "#3a2600") + pressedColor: "#979797" + height: 40 + width: parent.width - 6 + anchors.horizontalCenter: parent.horizontalCenter + onClicked: buttonPress (3) + content: TileText + { + text: autoButtonText; font.bold: true; + color: autoButtonActive ? "white" : "black" + } + } + } + } + function updateFunction () + { + if (functionItem.valid) + { + relayFunction = functionItem.value + switch (relayFunction) + { + // Alarm - no buttons + case 0: + functionText = qsTr("Alarm") + activeText = qsTr("Alarm") + inactiveText = qsTr("No Alarm") + offButtonText = "" + onButtonText = "" + autoButtonText = "" + onButton.visible = false + offButton.visible = false + autoButton.visible = false + break;; + // Generator + case 1: + functionText = qsTr("Generator") + activeText = qsTr("") // generator state text handled below + inactiveText = qsTr("") + onButtonText = qsTr("Manual\nStart") + offButtonText = qsTr("Manual\nStop") + autoButtonText = qsTr("Auto\nEnable") + onButton.visible = true + offButton.visible = true + autoButton.visible = true + break;; + // pump + case 3: + functionText = qsTr("Pump") + activeText = qsTr("On") + inactiveText = qsTr("Off") + onButtonText = qsTr("On") + offButtonText = qsTr("Off") + autoButtonText = qsTr("Auto") + onButton.visible = true + offButton.visible = true + autoButton.visible = true + break;; + // temperature + case 4: + functionText = qsTr("Temp") + activeText = qsTr("Alarm") + inactiveText = qsTr("No Alarm") + onButtonText = "--" + offButtonText = "--" + autoButtonText = "--" + onButton.visible = false + offButton.visible = false + autoButton.visible = false + break;; + // manual (2) and undefined + default: + functionText = qsTr("Manual") + activeText = qsTr("On") + inactiveText = qsTr("Off") + onButtonText = qsTr("On") + offButtonText = qsTr("Off") + autoButtonText = "" + onButton.visible = true + offButton.visible = true + autoButton.visible = false + break;; + } + } + // only relay 1 has a function selector, so use manual settings for other relays + else + { + relayFunction = 2 + functionText = qsTr("Manual") + activeText = qsTr("On") + inactiveText = qsTr("Off") + onButtonText = qsTr("On") + offButtonText = qsTr("Off") + autoButtonText = "--" // empty string causes interactions + autoButton.visible = false + } + updateButtons () + } + + function updateButtons () + { + switch (relayFunction) + { + // alarm - no buttons + case 0: + break;; + // Generator + case 1: + if (generatorManualStartItem.valid) + { + onButtonActive = generatorManualStartItem.value === 1 + offButtonActive = ! onButtonActive + } + else + { + offButtonActive = false + onButtonActive = false + } + if (generatorAutoRunItem.valid) + autoButtonActive = generatorAutoRunItem.value + else + autoButtonActive = false + break;; + // pump + case 3: + if (pumpModeItem.valid) + { + switch (pumpModeItem.value) + { + // Auto + case 0: + onButtonActive = false + offButtonActive = false + autoButtonActive = true + break;; + // On + case 1: + onButtonActive = true + offButtonActive = false + autoButtonActive = false + break;; + // Off + case 2: + onButtonActive = false + offButtonActive = true + autoButtonActive = false + break;; + default: + onButtonActive = false + offButtonActive = false + autoButtonActive = false + break;; + } + } + else + { + offButtonActive = false + onButtonActive = false + autoButtonActive = false + } + break;; + // manual (2) and undefined + default: + relayActive = stateItem.value === 1 != relayInverted + onButtonActive = relayActive + offButtonActive = ! onButtonActive + autoButtonActive = false + break;; + } + } + + function buttonPress (button) + { + switch (relayFunction) + { + // Generator + case 1: + switch (button) + { + // on + case 1: + generatorManualStartItem.setValue (1) + break;; + // off + case 2: + generatorManualStartItem.setValue (0) + break;; + // auto + case 3: + // toggle value + generatorAutoRunItem.setValue (generatorAutoRunItem.value === 1 ? 0 : 1) + break;; + default: + break;; + } + break;; + // pump + case 3: + switch (button) + { + // on + case 1: + pumpModeItem.setValue (1) + break;; + // off + case 2: + pumpModeItem.setValue (2) + break;; + // auto + case 3: + pumpModeItem.setValue (0) + break;; + default: + break;; + } + break;; + // alarm - no buttons + case 0: + break;; + // manual (2) and undefined + default: + switch (button) + { + // on + case 1: + stateItem.setValue (1) + break;; + // off + case 2: + stateItem.setValue (0) + break;; + default: + break;; + } + break;; + } + } +} diff --git a/FileSets/v3.60~25/TileRelay.qml.orig b/FileSets/v3.60~25/TileRelay.qml.orig new file mode 100644 index 00000000..b5d309b3 --- /dev/null +++ b/FileSets/v3.60~25/TileRelay.qml.orig @@ -0,0 +1,23 @@ +import QtQuick 2 +import com.victron.velib 1.0 +import "utils.js" as Utils + +PageGenerator { + + title: qsTr("Generator start/stop") + settingsBindPrefix: "com.victronenergy.settings/Settings/Generator0" + startStopBindPrefix: "com.victronenergy.generator.startstop0" + + property VBusItem relayFunction: VBusItem { bind: Utils.path("com.victronenergy.settings", "/Settings/Relay/Function") } + + model: !relayFunction.valid || relayFunction.value === 1 ? startStopModel : disabledModel + + VisibleItemModel { + id: disabledModel + MbItemText { + wrapMode: Text.WordWrap + text: qsTr("Generator start/stop function is not enabled, go to relay settings and set " + + "function to \"Generator start/stop\"") + } + } +} diff --git a/changes b/changes index c807cf17..2f7d3cf2 100644 --- a/changes +++ b/changes @@ -1,3 +1,7 @@ +v10.81: + add support for v3.54, v3.60~25 + remove unused symbolic links + v10.80: add support for v3.60~17 and v3.53 moved more files from version dependent to patched diff --git a/firstCompatibleVersion b/firstCompatibleVersion index ecb23a01..6110b7f3 100644 --- a/firstCompatibleVersion +++ b/firstCompatibleVersion @@ -1 +1 @@ -v2.71 \ No newline at end of file +v3.10 \ No newline at end of file diff --git a/version b/version index d2971577..b47fb695 100644 --- a/version +++ b/version @@ -1 +1 @@ -v10.80 +v10.81