diff --git a/FileSets/PatchSource/main.qml-v3.50 b/FileSets/PatchSource/main.qml-v3.50 index 93ed5f8c..07459144 100644 --- a/FileSets/PatchSource/main.qml-v3.50 +++ b/FileSets/PatchSource/main.qml-v3.50 @@ -19,7 +19,6 @@ PageStackWindow { property bool completed: false property bool alarm: alarmNotification.valid ? alarmNotification.value : 0 property bool showAlert: alertNotification.valid ? alertNotification.value : 0 -//////// modified for GuiMods flow pages property bool overviewsLoaded: defaultOverview.valid && generatorOverview.valid && mobileOverview.valid && tanksOverview.valid && startWithMenu.valid && mobileOverviewEnhanced.valid && guiModsFlowOverview.valid && generatorOverviewEnhanced.valid property string bindPrefix: "com.victronenergy.settings" @@ -44,13 +43,6 @@ PageStackWindow { //////// modified for OverviewHubEnhanced page onShowInputLoadsChanged: selectHubOverview () - VBusItem - { - id: guiModsFlowOverview - bind: "com.victronenergy.settings/Settings/GuiMods/FlowOverview" - onValueChanged: selectHubOverview () - } - ////// GuiMods — DarkMode property VBusItem darkModeItem: VBusItem { bind: "com.victronenergy.settings/Settings/GuiMods/DarkMode" } property bool darkMode: darkModeItem.valid && darkModeItem.value == 1 @@ -358,76 +350,73 @@ PageStackWindow { } } - Item { - anchors.verticalCenter: parent.verticalCenter - anchors.left: mbTools.left - height: mbTools.height - width: 200 - - MouseArea { - anchors.fill: parent - onClicked: { - if (pageStack.currentPage) - pageStack.currentPage.toolbarHandler.leftAction(true) - } - } - - Row { - anchors.centerIn: parent + Item { + anchors.verticalCenter: parent.verticalCenter + height: mbTools.height + width: mbTools.width - pagesItem.width - menusItem.width - centerScrollIndicator.width - MbIcon { - anchors.verticalCenter: parent.verticalCenter - iconId: pageStack.currentPage ? pageStack.currentPage.leftIcon : "" + MouseArea + { + anchors.fill: parent + onClicked: + { + if (darkModeItem.valid) + darkModeItem.setValue (! darkMode) + } } - Text { - anchors.verticalCenter: parent.verticalCenter - text: pageStack.currentPage ? pageStack.currentPage.leftText : "" + Text + { + anchors.fill: parent + horizontalAlignment: Text.AlignHCenter + text: qsTr ("change to") + "\n" + (darkMode ? qsTr ("Light mode") : qsTr ("Dark mode")) color: "white" font.bold: true - font.pixelSize: 16 + font.pixelSize: 12 + visible: darkModeItem.valid } } - } - - MbIcon { - id: centerScrollIndicator - - anchors { - horizontalCenter: parent.horizontalCenter - verticalCenter: mbTools.verticalCenter - } - iconId: pageStack.currentPage ? pageStack.currentPage.scrollIndicator : "" - } - - Item { - anchors.verticalCenter: parent.verticalCenter - height: mbTools.height - anchors.right: mbTools.right - width: 200 - - MouseArea { - anchors.fill: parent - onClicked: { - if (pageStack.currentPage) - pageStack.currentPage.toolbarHandler.rightAction(true) + Item + { + id: centerScrollIndicator + anchors.verticalCenter: parent.verticalCenter + height: mbTools.height + width: 20 + MbIcon { + anchors.verticalCenter: parent.verticalCenter + iconId: pageStack.currentPage ? pageStack.currentPage.scrollIndicator : "" } } - Row { - anchors.centerIn: parent + Item { + id: menusItem + anchors.verticalCenter: parent.verticalCenter + height: mbTools.height + width: pagesItem.width - MbIcon { - iconId: pageStack.currentPage ? pageStack.currentPage.rightIcon : "" - anchors.verticalCenter: parent.verticalCenter + MouseArea { + anchors.fill: parent + onClicked: { + if (pageStack.currentPage) + pageStack.currentPage.toolbarHandler.rightAction(true) + } } - Text { - text: pageStack.currentPage ? pageStack.currentPage.rightText : "" - anchors.verticalCenter: parent.verticalCenter - color: "white" - font.bold: true - font.pixelSize: 16 + Row { + anchors.centerIn: parent + + MbIcon { + iconId: pageStack.currentPage ? pageStack.currentPage.rightIcon : "" + anchors.verticalCenter: parent.verticalCenter + } + + Text { + text: pageStack.currentPage ? pageStack.currentPage.rightText : "" + anchors.verticalCenter: parent.verticalCenter + color: "white" + font.bold: true + font.pixelSize: 16 + } } } } @@ -557,7 +546,6 @@ PageStackWindow { overviewModel.append({"pageSource": newPage}) } - // Central mover for the ball animation on the overviews // Instead of using a timer per line, using a central one // reduces the CPU usage a little bit and makes the animations diff --git a/FileSets/PatchSource/main.qml-v3.50.patch b/FileSets/PatchSource/main.qml-v3.50.patch index 7556bffa..1392a21d 100644 --- a/FileSets/PatchSource/main.qml-v3.50.patch +++ b/FileSets/PatchSource/main.qml-v3.50.patch @@ -1,5 +1,5 @@ ---- /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.50.orig 2024-10-11 05:51:41 -+++ /Users/Kevin/GitHub/GuiMods.copy/FileSets/PatchSource/main.qml-v3.50 2024-10-12 11:57:19 +--- /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-02 21:56:23 @@ -1,3 +1,6 @@ +//////// Modified to hide the OverviewTiles page +//////// Modified to substitute flow overview pages @@ -7,17 +7,16 @@ import QtQuick 1.1 import Qt.labs.components.native 1.0 -@@ -16,7 +19,8 @@ +@@ -16,7 +19,7 @@ property bool completed: false property bool alarm: alarmNotification.valid ? alarmNotification.value : 0 property bool showAlert: alertNotification.valid ? alertNotification.value : 0 - property bool overviewsLoaded: defaultOverview.valid && generatorOverview.valid && mobileOverview.valid && tanksOverview.valid && startWithMenu.valid -+//////// modified for GuiMods flow pages + property bool overviewsLoaded: defaultOverview.valid && generatorOverview.valid && mobileOverview.valid && tanksOverview.valid && startWithMenu.valid && mobileOverviewEnhanced.valid && guiModsFlowOverview.valid && generatorOverviewEnhanced.valid property string bindPrefix: "com.victronenergy.settings" property bool isNotificationPage: pageStack.currentPage && pageStack.currentPage.title === qsTr("Notifications") -@@ -27,19 +31,78 @@ +@@ -27,19 +30,71 @@ 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 @@ -39,13 +38,6 @@ +//////// modified for OverviewHubEnhanced page + onShowInputLoadsChanged: selectHubOverview () + -+ VBusItem -+ { -+ id: guiModsFlowOverview -+ bind: "com.victronenergy.settings/Settings/GuiMods/FlowOverview" -+ onValueChanged: selectHubOverview () -+ } -+ +////// GuiMods — DarkMode + property VBusItem darkModeItem: VBusItem { bind: "com.victronenergy.settings/Settings/GuiMods/DarkMode" } + property bool darkMode: darkModeItem.valid && darkModeItem.value == 1 @@ -101,7 +93,7 @@ VBusItem { id: generatorOverview bind: "com.victronenergy.settings/Settings/Relay/Function" -@@ -63,13 +126,98 @@ +@@ -63,13 +118,98 @@ } } @@ -206,11 +198,15 @@ VBusItem { id: tanksOverview bind: "com.victronenergy.settings/Settings/Gui/TanksOverview" -@@ -171,7 +319,45 @@ - ToolBarLayout { +@@ -172,76 +312,111 @@ id: mbTools height: parent.height -+ + +- Item { +- anchors.verticalCenter: parent.verticalCenter +- anchors.left: mbTools.left +- height: mbTools.height +- width: 200 +//// GuiMods - DarkMode + Row + { @@ -222,13 +218,18 @@ + height: mbTools.height + width: 170 +- MouseArea { +- anchors.fill: parent +- onClicked: { +- if (pageStack.currentPage) +- pageStack.currentPage.toolbarHandler.leftAction(true) + MouseArea { + anchors.fill: parent + onClicked: { + if (pageStack.currentPage) + pageStack.currentPage.toolbarHandler.leftAction(true) + } -+ } + } + + Row { + anchors.verticalCenter: parent.verticalCenter @@ -247,12 +248,122 @@ + font.pixelSize: 16 + } + } -+ } + } + +- Row { +- anchors.centerIn: parent ++ Item { ++ anchors.verticalCenter: parent.verticalCenter ++ height: mbTools.height ++ width: mbTools.width - pagesItem.width - menusItem.width - centerScrollIndicator.width + +- MbIcon { +- anchors.verticalCenter: parent.verticalCenter +- iconId: pageStack.currentPage ? pageStack.currentPage.leftIcon : "" ++ MouseArea ++ { ++ anchors.fill: parent ++ onClicked: ++ { ++ if (darkModeItem.valid) ++ darkModeItem.setValue (! darkMode) ++ } + } + +- Text { +- anchors.verticalCenter: parent.verticalCenter +- text: pageStack.currentPage ? pageStack.currentPage.leftText : "" ++ Text ++ { ++ anchors.fill: parent ++ horizontalAlignment: Text.AlignHCenter ++ text: qsTr ("change to") + "\n" + (darkMode ? qsTr ("Light mode") : qsTr ("Dark mode")) + color: "white" + font.bold: true +- font.pixelSize: 16 ++ font.pixelSize: 12 ++ visible: darkModeItem.valid + } + } +- } +- +- MbIcon { +- id: centerScrollIndicator +- +- anchors { +- horizontalCenter: parent.horizontalCenter +- verticalCenter: mbTools.verticalCenter +- } +- iconId: pageStack.currentPage ? pageStack.currentPage.scrollIndicator : "" +- } +- +- Item { +- anchors.verticalCenter: parent.verticalCenter +- height: mbTools.height +- anchors.right: mbTools.right +- width: 200 +- +- MouseArea { +- anchors.fill: parent +- onClicked: { +- if (pageStack.currentPage) +- pageStack.currentPage.toolbarHandler.rightAction(true) ++ Item ++ { ++ id: centerScrollIndicator ++ anchors.verticalCenter: parent.verticalCenter ++ height: mbTools.height ++ width: 20 ++ MbIcon { ++ anchors.verticalCenter: parent.verticalCenter ++ iconId: pageStack.currentPage ? pageStack.currentPage.scrollIndicator : "" + } + } + +- Row { +- anchors.centerIn: parent ++ Item { ++ id: menusItem ++ anchors.verticalCenter: parent.verticalCenter ++ height: mbTools.height ++ width: pagesItem.width + +- MbIcon { +- iconId: pageStack.currentPage ? pageStack.currentPage.rightIcon : "" +- anchors.verticalCenter: parent.verticalCenter ++ MouseArea { ++ anchors.fill: parent ++ onClicked: { ++ if (pageStack.currentPage) ++ pageStack.currentPage.toolbarHandler.rightAction(true) ++ } + } + +- Text { +- text: pageStack.currentPage ? pageStack.currentPage.rightText : "" +- anchors.verticalCenter: parent.verticalCenter +- color: "white" +- font.bold: true +- font.pixelSize: 16 ++ Row { ++ anchors.centerIn: parent ++ ++ MbIcon { ++ iconId: pageStack.currentPage ? pageStack.currentPage.rightIcon : "" ++ anchors.verticalCenter: parent.verticalCenter ++ } + - Item { - anchors.verticalCenter: parent.verticalCenter - anchors.left: mbTools.left -@@ -256,9 +442,10 @@ ++ Text { ++ text: pageStack.currentPage ? pageStack.currentPage.rightText : "" ++ anchors.verticalCenter: parent.verticalCenter ++ color: "white" ++ font.bold: true ++ font.pixelSize: 16 ++ } + } + } + } +@@ -256,9 +431,10 @@ ListElement { pageSource: "OverviewHub.qml" } @@ -266,7 +377,7 @@ } Component { -@@ -284,7 +471,12 @@ +@@ -284,7 +460,12 @@ id: newUiPopupTimer interval: 10000 running: false @@ -280,7 +391,7 @@ } function showNewUiPopup() -@@ -350,13 +542,22 @@ +@@ -350,11 +531,19 @@ } } @@ -299,7 +410,4 @@ + overviewModel.append({"pageSource": newPage}) } -+ // Central mover for the ball animation on the overviews - // Instead of using a timer per line, using a central one - // reduces the CPU usage a little bit and makes the animations diff --git a/FileSets/PatchSource/obsoletePatches/MbItemDigitalInput.qml-v2.94 b/FileSets/PatchSource/obsoletePatches/MbItemDigitalInput.qml-v2.94 deleted file mode 100644 index 46c3d655..00000000 --- a/FileSets/PatchSource/obsoletePatches/MbItemDigitalInput.qml-v2.94 +++ /dev/null @@ -1,30 +0,0 @@ -//// modified for ExtTransferSwitch package - -import QtQuick 1.1 - -MbItemOptions { - show: valid - signal disabled - property variant previousValue: undefined - possibleValues: [ - MbOption { description: qsTr("Disabled"); value: 0 }, - MbOption { description: qsTr("Pulse meter"); value: 1 }, - MbOption { description: qsTr("Door alarm"); value: 2 }, - MbOption { description: qsTr("Bilge pump"); value: 3 }, - MbOption { description: qsTr("Bilge alarm"); value: 4 }, - MbOption { description: qsTr("Burglar alarm"); value: 5 }, - MbOption { description: qsTr("Smoke alarm"); value: 6 }, - MbOption { description: qsTr("Fire alarm"); value: 7 }, - MbOption { description: qsTr("CO2 alarm"); value: 8 }, - MbOption { description: qsTr("Generator"); value: 9 }, - MbOption { description: qsTr("Generic I/O"); value: 10 }, -//// added for ExtTransferSwitch package - MbOption { description: qsTr("External transfer switch"); value: 11 } - ] - onValueChanged: { - if (valid) { - if (previousValue != undefined && value == 0) disabled() - previousValue = value - } - } -} diff --git a/FileSets/PatchSource/obsoletePatches/MbItemDigitalInput.qml-v2.94.orig b/FileSets/PatchSource/obsoletePatches/MbItemDigitalInput.qml-v2.94.orig deleted file mode 100644 index 2054a222..00000000 --- a/FileSets/PatchSource/obsoletePatches/MbItemDigitalInput.qml-v2.94.orig +++ /dev/null @@ -1,25 +0,0 @@ -import QtQuick 1.1 - -MbItemOptions { - show: valid - signal disabled - property variant previousValue: undefined - possibleValues: [ - MbOption { description: qsTr("Disabled"); value: 0 }, - MbOption { description: qsTr("Pulse meter"); value: 1 }, - MbOption { description: qsTr("Door alarm"); value: 2 }, - MbOption { description: qsTr("Bilge pump"); value: 3 }, - MbOption { description: qsTr("Bilge alarm"); value: 4 }, - MbOption { description: qsTr("Burglar alarm"); value: 5 }, - MbOption { description: qsTr("Smoke alarm"); value: 6 }, - MbOption { description: qsTr("Fire alarm"); value: 7 }, - MbOption { description: qsTr("CO2 alarm"); value: 8 }, - MbOption { description: qsTr("Generator"); value: 9 } - ] - onValueChanged: { - if (valid) { - if (previousValue != undefined && value == 0) disabled() - previousValue = value - } - } -} diff --git a/FileSets/PatchSource/obsoletePatches/MbSpinBox.qml-v2.73 b/FileSets/PatchSource/obsoletePatches/MbSpinBox.qml-v2.73 deleted file mode 100644 index 5553ee42..00000000 --- a/FileSets/PatchSource/obsoletePatches/MbSpinBox.qml-v2.73 +++ /dev/null @@ -1,202 +0,0 @@ -////// GuiMods — added DarkMode - -import QtQuick 1.1 -import Qt.labs.components.native 1.0 -import com.victron.velib 1.0 - -MbItem { - id: root - cornerMark: !readOnly && !spinbox.enabled - -////// GuiMods — DarkMode - property VBusItem darkModeItem: VBusItem { bind: "com.victronenergy.settings/Settings/GuiMods/DarkMode" } - property bool darkMode: darkModeItem.valid && darkModeItem.value == 1 - - property bool valid: vItem.value !== undefined - property alias bind: vItem.bind - property string description - property double stepSize: 0.5 - property int numOfDecimals: 1 - property bool readOnly: !userHasWriteAccess - property alias unit: unit.text - property alias localValue: spinbox.value - property alias value: vItem.value - property alias item: vItem - property alias min: spinbox.minimumValue - property alias max: spinbox.maximumValue - property bool hasDefault: vItem.def !== undefined - property bool useVirtualKeyboard - property variant spinboxToolbarHandler: editMode ? _spinboxToolbarHandler : navigationHandler - default property alias values: container.data - - editMode: spinbox.enabled - height: keyboard.y + keyboard.height + 1 - toolbarHandler: spinboxToolbarHandler - - ToolbarHandler { - id: _spinboxToolbarHandler - property string leftIcon: "icon-toolbar-cancel" - property string rightIcon: hasDefault ? "" : "icon-toolbar-ok" - property string rightText: hasDefault ? qsTr("Default") : "" - - function leftAction() - { - cancel() - } - - function rightAction() - { - save() - } - } - - signal exitEditMode(bool changed) - signal maxValueReached() - signal minValueReached() - - VBusItem { - id: vItem - isSetting: true - } - - Item { - id: verticalCentered - height: root.defaultHeight - width: root.width - - MbTextDescription { - id: name - anchors { - left: parent.left; leftMargin: style.marginDefault - verticalCenter: parent.verticalCenter - } - - isCurrentItem: root.ListView.isCurrentItem - text: root.description - opacity: valid ? style.opacityEnabled : style.opacityDisabled - } - - Item { - id: container - width: childrenRect.width - height: childrenRect.height - anchors { - right: graytag.left; rightMargin: style.marginDefault - verticalCenter: parent.verticalCenter - } - } - - MbBackgroundRect { - id: graytag -////// GuiMods — DarkMode - color: !darkMode ? (!spinbox.enabled ? "#ddd": "#fff") : (!spinbox.enabled ? "#4b4b4b": "#747474") - height: spinbox.height + 6 - width: spinbox.width + unit.width + 10 -////// GuiMods — DarkMode - border.color: !darkMode ? "#ddd" : "#4b4b4b" - border.width: spinbox.enabled ? 1 : 0 - anchors { - right: parent.right; rightMargin: style.marginDefault - verticalCenter: parent.verticalCenter - } - } - - MbTextValue { - id: unit - item.invalidText: "" - anchors { - right: parent.right; rightMargin: style.marginDefault + 5 - verticalCenter: spinbox.verticalCenter - } - } - - SpinBox { - id: spinbox - - color: style.color2 - font.pixelSize: name.font.pixelSize - font.family: name.font.family - font.bold: false - minimumValue: vItem.min === undefined ? 0 : vItem.min - maximumValue: vItem.max === undefined ? 100 : vItem.max - stepSize: root.stepSize - enabled: false - greyed: valid - numOfDecimals: root.numOfDecimals - anchors { - right: unit.left - verticalCenter: parent.verticalCenter - } - - /* note: these functions break binding hence the Binding item below */ - Keys.onRightPressed: { if (value === maximumValue) maxValueReached(); spinbox.up(event.isAutoRepeat); } - Keys.onLeftPressed: { if (value === minimumValue) minValueReached(); spinbox.down(event.isAutoRepeat); } - Keys.onUpPressed: { if (value === maximumValue) maxValueReached(); spinbox.up(event.isAutoRepeat); } - Keys.onDownPressed: { if (value === minimumValue) minValueReached(); spinbox.down(event.isAutoRepeat); } - - /* Focus is removed to ignore keypresses */ - Keys.onSpacePressed: save(false) - - Keys.onReturnPressed: { - if (hasDefault) { - value = vItem.defaultValue - } else { - save(false) - } - } - - Keys.onEscapePressed: cancel() - } - - MouseArea { - anchors { - fill: spinbox - leftMargin: -20 - rightMargin: -20 - } - - onPressed: handleMouseClick(true) - } - } - - Keyboard { - id: keyboard - anchors.top: verticalCentered.bottom - anchors.topMargin: -1 - width: root.width - active: spinbox.enabled && useVirtualKeyboard - layout: "KeyboardLoaderPlusMin.qml" - onAnimatingChanged: listview.positionViewAtIndex(currentIndex, ListView.Contain) - } - - /* binding is done explicitly to reenable binding after edit */ - Binding { - target: spinbox - property: "value" - value: vItem.value - when: valid && !spinbox.enabled - } - - function edit(isMouse) - { - if (valid && !readOnly) - { - useVirtualKeyboard = isMouse === true - spinbox.enabled = true - spinbox.focus = true - } - } - - function save() { - vItem.setValue(spinbox.value) - focus = true; - spinbox.enabled = false - exitEditMode(true) - } - - function cancel() { - focus = true - spinbox.enabled = false - exitEditMode(false) - } -} diff --git a/FileSets/PatchSource/obsoletePatches/MbSpinBox.qml-v2.73.orig b/FileSets/PatchSource/obsoletePatches/MbSpinBox.qml-v2.73.orig deleted file mode 100644 index 5499edfc..00000000 --- a/FileSets/PatchSource/obsoletePatches/MbSpinBox.qml-v2.73.orig +++ /dev/null @@ -1,194 +0,0 @@ -import QtQuick 1.1 -import Qt.labs.components.native 1.0 -import com.victron.velib 1.0 - -MbItem { - id: root - cornerMark: !readOnly && !spinbox.enabled - - property bool valid: vItem.value !== undefined - property alias bind: vItem.bind - property string description - property double stepSize: 0.5 - property int numOfDecimals: 1 - property bool readOnly: !userHasWriteAccess - property alias unit: unit.text - property alias localValue: spinbox.value - property alias value: vItem.value - property alias item: vItem - property alias min: spinbox.minimumValue - property alias max: spinbox.maximumValue - property bool hasDefault: vItem.def !== undefined - property bool useVirtualKeyboard - property variant spinboxToolbarHandler: editMode ? _spinboxToolbarHandler : navigationHandler - default property alias values: container.data - - editMode: spinbox.enabled - height: keyboard.y + keyboard.height + 1 - toolbarHandler: spinboxToolbarHandler - - ToolbarHandler { - id: _spinboxToolbarHandler - property string leftIcon: "icon-toolbar-cancel" - property string rightIcon: hasDefault ? "" : "icon-toolbar-ok" - property string rightText: hasDefault ? qsTr("Default") : "" - - function leftAction() - { - cancel() - } - - function rightAction() - { - save() - } - } - - signal exitEditMode(bool changed) - signal maxValueReached() - signal minValueReached() - - VBusItem { - id: vItem - isSetting: true - } - - Item { - id: verticalCentered - height: root.defaultHeight - width: root.width - - MbTextDescription { - id: name - anchors { - left: parent.left; leftMargin: style.marginDefault - verticalCenter: parent.verticalCenter - } - - isCurrentItem: root.ListView.isCurrentItem - text: root.description - opacity: valid ? style.opacityEnabled : style.opacityDisabled - } - - Item { - id: container - width: childrenRect.width - height: childrenRect.height - anchors { - right: graytag.left; rightMargin: style.marginDefault - verticalCenter: parent.verticalCenter - } - } - - MbBackgroundRect { - id: graytag - color: !spinbox.enabled? "#ddd": "#fff" - height: spinbox.height + 6 - width: spinbox.width + unit.width + 10 - border.color: "#ddd" - border.width: spinbox.enabled ? 1 : 0 - anchors { - right: parent.right; rightMargin: style.marginDefault - verticalCenter: parent.verticalCenter - } - } - - MbTextValue { - id: unit - item.invalidText: "" - anchors { - right: parent.right; rightMargin: style.marginDefault + 5 - verticalCenter: spinbox.verticalCenter - } - } - - SpinBox { - id: spinbox - - color: style.color2 - font.pixelSize: name.font.pixelSize - font.family: name.font.family - font.bold: false - minimumValue: vItem.min === undefined ? 0 : vItem.min - maximumValue: vItem.max === undefined ? 100 : vItem.max - stepSize: root.stepSize - enabled: false - greyed: valid - numOfDecimals: root.numOfDecimals - anchors { - right: unit.left - verticalCenter: parent.verticalCenter - } - - /* note: these functions break binding hence the Binding item below */ - Keys.onRightPressed: { if (value === maximumValue) maxValueReached(); spinbox.up(event.isAutoRepeat); } - Keys.onLeftPressed: { if (value === minimumValue) minValueReached(); spinbox.down(event.isAutoRepeat); } - Keys.onUpPressed: { if (value === maximumValue) maxValueReached(); spinbox.up(event.isAutoRepeat); } - Keys.onDownPressed: { if (value === minimumValue) minValueReached(); spinbox.down(event.isAutoRepeat); } - - /* Focus is removed to ignore keypresses */ - Keys.onSpacePressed: save(false) - - Keys.onReturnPressed: { - if (hasDefault) { - value = vItem.defaultValue - } else { - save(false) - } - } - - Keys.onEscapePressed: cancel() - } - - MouseArea { - anchors { - fill: spinbox - leftMargin: -20 - rightMargin: -20 - } - - onPressed: handleMouseClick(true) - } - } - - Keyboard { - id: keyboard - anchors.top: verticalCentered.bottom - anchors.topMargin: -1 - width: root.width - active: spinbox.enabled && useVirtualKeyboard - layout: "KeyboardLoaderPlusMin.qml" - onAnimatingChanged: listview.positionViewAtIndex(currentIndex, ListView.Contain) - } - - /* binding is done explicitly to reenable binding after edit */ - Binding { - target: spinbox - property: "value" - value: vItem.value - when: valid && !spinbox.enabled - } - - function edit(isMouse) - { - if (valid && !readOnly) - { - useVirtualKeyboard = isMouse === true - spinbox.enabled = true - spinbox.focus = true - } - } - - function save() { - vItem.setValue(spinbox.value) - focus = true; - spinbox.enabled = false - exitEditMode(true) - } - - function cancel() { - focus = true - spinbox.enabled = false - exitEditMode(false) - } -} diff --git a/FileSets/PatchSource/obsoletePatches/MbSpinBox.qml-v2.89 b/FileSets/PatchSource/obsoletePatches/MbSpinBox.qml-v2.89 deleted file mode 100644 index 8f4d20db..00000000 --- a/FileSets/PatchSource/obsoletePatches/MbSpinBox.qml-v2.89 +++ /dev/null @@ -1,203 +0,0 @@ -////// GuiMods — added DarkMode - -import QtQuick 1.1 -import Qt.labs.components.native 1.0 -import com.victron.velib 1.0 - -MbItem { - id: root - cornerMark: !readOnly && !spinbox.enabled - -////// GuiMods — DarkMode - property VBusItem darkModeItem: VBusItem { bind: "com.victronenergy.settings/Settings/GuiMods/DarkMode" } - property bool darkMode: darkModeItem.valid && darkModeItem.value == 1 - - property bool valid: vItem.value !== undefined - property alias bind: vItem.bind - property string description - property double stepSize: 0.5 - property int numOfDecimals: 1 - property bool readOnly: !userHasWriteAccess - property alias unit: unit.text - property alias localValue: spinbox.value - property alias value: vItem.value - property alias item: vItem - property alias min: spinbox.minimumValue - property alias max: spinbox.maximumValue - property bool hasDefault: vItem.def !== undefined - property bool useVirtualKeyboard - property variant spinboxToolbarHandler: editMode ? _spinboxToolbarHandler : navigationHandler - default property alias values: container.data - - editMode: spinbox.enabled - height: keyboard.y + keyboard.height + 1 - toolbarHandler: spinboxToolbarHandler - - ToolbarHandler { - id: _spinboxToolbarHandler - property string leftIcon: "icon-toolbar-cancel" - property string rightIcon: hasDefault ? "" : "icon-toolbar-ok" - property string rightText: hasDefault ? qsTr("Default") : "" - - function leftAction() - { - cancel() - } - - function rightAction() - { - save() - } - } - - signal exitEditMode(bool changed, variant newValue) - signal maxValueReached() - signal minValueReached() - - VBusItem { - id: vItem - isSetting: true - } - - Item { - id: verticalCentered - height: root.defaultHeight - width: root.width - - MbTextDescription { - id: name - anchors { - left: parent.left; leftMargin: style.marginDefault - verticalCenter: parent.verticalCenter - } - - isCurrentItem: root.ListView.isCurrentItem - text: root.description - opacity: valid ? style.opacityEnabled : style.opacityDisabled - } - - Item { - id: container - width: childrenRect.width - height: childrenRect.height - anchors { - right: graytag.left; rightMargin: style.marginDefault - verticalCenter: parent.verticalCenter - } - } - - MbBackgroundRect { - id: graytag -////// GuiMods — DarkMode - color: !darkMode ? (!spinbox.enabled ? "#ddd": "#fff") : (!spinbox.enabled ? "#4b4b4b": "#747474") - height: spinbox.height + 6 - width: spinbox.width + unit.width + 10 -////// GuiMods — DarkMode - border.color: !darkMode ? "#ddd" : "#4b4b4b" - border.width: spinbox.enabled ? 1 : 0 - anchors { - right: parent.right; rightMargin: style.marginDefault - verticalCenter: parent.verticalCenter - } - } - - MbTextValue { - id: unit - item.invalidText: "" - anchors { - right: parent.right; rightMargin: style.marginDefault + 5 - verticalCenter: spinbox.verticalCenter - } - } - - SpinBox { - id: spinbox - - color: style.color2 - font.pixelSize: name.font.pixelSize - font.family: name.font.family - font.bold: false - minimumValue: vItem.min === undefined ? 0 : vItem.min - maximumValue: vItem.max === undefined ? 100 : vItem.max - stepSize: root.stepSize - enabled: false - greyed: valid - numOfDecimals: root.numOfDecimals - anchors { - right: unit.left - verticalCenter: parent.verticalCenter - } - - /* note: these functions break binding hence the Binding item below */ - Keys.onRightPressed: { if (value === maximumValue) maxValueReached(); spinbox.up(event.isAutoRepeat); } - Keys.onLeftPressed: { if (value === minimumValue) minValueReached(); spinbox.down(event.isAutoRepeat); } - Keys.onUpPressed: { if (value === maximumValue) maxValueReached(); spinbox.up(event.isAutoRepeat); } - Keys.onDownPressed: { if (value === minimumValue) minValueReached(); spinbox.down(event.isAutoRepeat); } - - /* Focus is removed to ignore keypresses */ - Keys.onSpacePressed: save(false) - - Keys.onReturnPressed: { - if (hasDefault) { - value = vItem.defaultValue - } else { - save(false) - } - } - - Keys.onEscapePressed: cancel() - } - - MouseArea { - anchors { - fill: spinbox - leftMargin: -20 - rightMargin: -20 - } - - onPressed: handleMouseClick(true) - } - } - - Keyboard { - id: keyboard - anchors.top: verticalCentered.bottom - anchors.topMargin: -1 - width: root.width - active: spinbox.enabled && useVirtualKeyboard - layout: "KeyboardLoaderPlusMin.qml" - onAnimatingChanged: listview.positionViewAtIndex(currentIndex, ListView.Contain) - } - - /* binding is done explicitly to reenable binding after edit */ - Binding { - target: spinbox - property: "value" - value: vItem.value - when: valid && !spinbox.enabled - } - - function edit(isMouse) - { - if (valid && !readOnly) - { - useVirtualKeyboard = isMouse === true - spinbox.enabled = true - spinbox.focus = true - } - } - - function save() { - var newValue = spinbox.value - vItem.setValue(spinbox.value) - focus = true; - spinbox.enabled = false - exitEditMode(true, newValue) - } - - function cancel() { - focus = true - spinbox.enabled = false - exitEditMode(false, undefined) - } -} diff --git a/FileSets/PatchSource/obsoletePatches/MbSpinBox.qml-v2.89.orig b/FileSets/PatchSource/obsoletePatches/MbSpinBox.qml-v2.89.orig deleted file mode 100644 index 29a24602..00000000 --- a/FileSets/PatchSource/obsoletePatches/MbSpinBox.qml-v2.89.orig +++ /dev/null @@ -1,195 +0,0 @@ -import QtQuick 1.1 -import Qt.labs.components.native 1.0 -import com.victron.velib 1.0 - -MbItem { - id: root - cornerMark: !readOnly && !spinbox.enabled - - property bool valid: vItem.value !== undefined - property alias bind: vItem.bind - property string description - property double stepSize: 0.5 - property int numOfDecimals: 1 - property bool readOnly: !userHasWriteAccess - property alias unit: unit.text - property alias localValue: spinbox.value - property alias value: vItem.value - property alias item: vItem - property alias min: spinbox.minimumValue - property alias max: spinbox.maximumValue - property bool hasDefault: vItem.def !== undefined - property bool useVirtualKeyboard - property variant spinboxToolbarHandler: editMode ? _spinboxToolbarHandler : navigationHandler - default property alias values: container.data - - editMode: spinbox.enabled - height: keyboard.y + keyboard.height + 1 - toolbarHandler: spinboxToolbarHandler - - ToolbarHandler { - id: _spinboxToolbarHandler - property string leftIcon: "icon-toolbar-cancel" - property string rightIcon: hasDefault ? "" : "icon-toolbar-ok" - property string rightText: hasDefault ? qsTr("Default") : "" - - function leftAction() - { - cancel() - } - - function rightAction() - { - save() - } - } - - signal exitEditMode(bool changed, variant newValue) - signal maxValueReached() - signal minValueReached() - - VBusItem { - id: vItem - isSetting: true - } - - Item { - id: verticalCentered - height: root.defaultHeight - width: root.width - - MbTextDescription { - id: name - anchors { - left: parent.left; leftMargin: style.marginDefault - verticalCenter: parent.verticalCenter - } - - isCurrentItem: root.ListView.isCurrentItem - text: root.description - opacity: valid ? style.opacityEnabled : style.opacityDisabled - } - - Item { - id: container - width: childrenRect.width - height: childrenRect.height - anchors { - right: graytag.left; rightMargin: style.marginDefault - verticalCenter: parent.verticalCenter - } - } - - MbBackgroundRect { - id: graytag - color: !spinbox.enabled? "#ddd": "#fff" - height: spinbox.height + 6 - width: spinbox.width + unit.width + 10 - border.color: "#ddd" - border.width: spinbox.enabled ? 1 : 0 - anchors { - right: parent.right; rightMargin: style.marginDefault - verticalCenter: parent.verticalCenter - } - } - - MbTextValue { - id: unit - item.invalidText: "" - anchors { - right: parent.right; rightMargin: style.marginDefault + 5 - verticalCenter: spinbox.verticalCenter - } - } - - SpinBox { - id: spinbox - - color: style.color2 - font.pixelSize: name.font.pixelSize - font.family: name.font.family - font.bold: false - minimumValue: vItem.min === undefined ? 0 : vItem.min - maximumValue: vItem.max === undefined ? 100 : vItem.max - stepSize: root.stepSize - enabled: false - greyed: valid - numOfDecimals: root.numOfDecimals - anchors { - right: unit.left - verticalCenter: parent.verticalCenter - } - - /* note: these functions break binding hence the Binding item below */ - Keys.onRightPressed: { if (value === maximumValue) maxValueReached(); spinbox.up(event.isAutoRepeat); } - Keys.onLeftPressed: { if (value === minimumValue) minValueReached(); spinbox.down(event.isAutoRepeat); } - Keys.onUpPressed: { if (value === maximumValue) maxValueReached(); spinbox.up(event.isAutoRepeat); } - Keys.onDownPressed: { if (value === minimumValue) minValueReached(); spinbox.down(event.isAutoRepeat); } - - /* Focus is removed to ignore keypresses */ - Keys.onSpacePressed: save(false) - - Keys.onReturnPressed: { - if (hasDefault) { - value = vItem.defaultValue - } else { - save(false) - } - } - - Keys.onEscapePressed: cancel() - } - - MouseArea { - anchors { - fill: spinbox - leftMargin: -20 - rightMargin: -20 - } - - onPressed: handleMouseClick(true) - } - } - - Keyboard { - id: keyboard - anchors.top: verticalCentered.bottom - anchors.topMargin: -1 - width: root.width - active: spinbox.enabled && useVirtualKeyboard - layout: "KeyboardLoaderPlusMin.qml" - onAnimatingChanged: listview.positionViewAtIndex(currentIndex, ListView.Contain) - } - - /* binding is done explicitly to reenable binding after edit */ - Binding { - target: spinbox - property: "value" - value: vItem.value - when: valid && !spinbox.enabled - } - - function edit(isMouse) - { - if (valid && !readOnly) - { - useVirtualKeyboard = isMouse === true - spinbox.enabled = true - spinbox.focus = true - } - } - - function save() { - var newValue = spinbox.value - vItem.setValue(spinbox.value) - focus = true; - spinbox.enabled = false - exitEditMode(true, newValue) - } - - function cancel() { - focus = true - spinbox.enabled = false - exitEditMode(false, undefined) - } -} diff --git a/FileSets/PatchSource/obsoletePatches/MbStyle.qml-v2.94 b/FileSets/PatchSource/obsoletePatches/MbStyle.qml-v2.94 deleted file mode 100644 index 6224d128..00000000 --- a/FileSets/PatchSource/obsoletePatches/MbStyle.qml-v2.94 +++ /dev/null @@ -1,47 +0,0 @@ -import QtQuick 1.1 - -/* - * common style properties - */ -QtObject { -////// GuiMods — DarkMode - property VBusItem darkModeItem: VBusItem { bind: "com.victronenergy.settings/Settings/GuiMods/DarkMode" } - property bool darkMode: darkModeItem.valid && darkModeItem.value == 1 - - property bool isCurrentItem - - // Default MbItem size - property int itemHeight: 35 - - // Default font and size for e.g. the menus - property string fontFamily: "DejaVu Sans Condensed" - property int fontPixelSize: 16 - -////// GuiMods — DarkMode - property string borderColor: !darkMode ? "#ddd" : "#4b4b4b" - property string backgroundColor: isCurrentItem ? (!darkMode ? '#4790d0' : '#234468') : 'transparent' - property string backgroundColorService: !darkMode ? (isCurrentItem ? "#2969a1" : '#ffe9b7') : (isCurrentItem ? "#234468" : '#7f745b') - property string backgroundColorComponent: borderColor - - // Text mainly used for description etc. -////// GuiMods — DarkMode - property string textColor: !darkMode ? "#000000" : "#fdfdfd" - property string textColorSelected: !darkMode ? "#FFFFFF" : "#fdfdfd" - - // Color typically used for values -////// GuiMods — DarkMode - property string valueColor: !darkMode ? "#333333" : "#fdfdfd" - property int valueHorizontalAlignment: Text.AlignRight - property string color2: !darkMode ? "#333333" : "#fdfdfd" - - property int marginDefault: 8 - // margin between MbItem border and components for bottom / top - property int marginItemVertical: 3 - // margin from the "sides", typically left / right - property int marginItemHorizontal: 8 - // prefered left / right text margin within text components - property int marginTextHorizontal: 5 - - property real opacityEnabled: 1.0 - property real opacityDisabled: 0.5 -} diff --git a/FileSets/PatchSource/obsoletePatches/MbStyle.qml-v2.94.orig b/FileSets/PatchSource/obsoletePatches/MbStyle.qml-v2.94.orig deleted file mode 100644 index 0f7fcc45..00000000 --- a/FileSets/PatchSource/obsoletePatches/MbStyle.qml-v2.94.orig +++ /dev/null @@ -1,40 +0,0 @@ -import QtQuick 1.1 - -/* - * common style properties - */ -QtObject { - property bool isCurrentItem - - // Default MbItem size - property int itemHeight: 35 - - // Default font and size for e.g. the menus - property string fontFamily: "DejaVu Sans Condensed" - property int fontPixelSize: 16 - - property string borderColor: "#ddd" - property string backgroundColor: isCurrentItem ? '#4790d0' : 'transparent' - property string backgroundColorService: isCurrentItem ? "#2969a1" : '#ffe9b7' - property string backgroundColorComponent: borderColor - - // Text mainly used for description etc. - property string textColor: "#000000" - property string textColorSelected: "#FFFFFF" - - // Color typically used for values - property string valueColor: "#333333" - property int valueHorizontalAlignment: Text.AlignRight - property string color2: "#333333" - - property int marginDefault: 8 - // margin between MbItem border and components for bottom / top - property int marginItemVertical: 3 - // margin from the "sides", typically left / right - property int marginItemHorizontal: 8 - // prefered left / right text margin within text components - property int marginTextHorizontal: 5 - - property real opacityEnabled: 1.0 - property real opacityDisabled: 0.5 -} diff --git a/FileSets/PatchSource/obsoletePatches/PageMain.qml-v2.94 b/FileSets/PatchSource/obsoletePatches/PageMain.qml-v2.94 deleted file mode 100644 index 97c5ce73..00000000 --- a/FileSets/PatchSource/obsoletePatches/PageMain.qml-v2.94 +++ /dev/null @@ -1,281 +0,0 @@ -//////// 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 - -MbPage { - id: root - title: qsTr("Device 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 { -//////// put Settings at top of list - VisualItemModel { //////// use VisualItemModel below for v2.93 and earlier - MbSubMenu { - description: qsTr("Settings") - subpage: Component { PageSettings {} } - show: settingsAtTop - } - -//////// put Notifications second - MbSubMenu { - id: menuNotificationsTop - description: qsTr("Notifications") - item: VBusItem { - property variant active: NotificationCenter.notifications.filter( - function isActive(obj) { return obj.active} ) - value: active.length > 0 ? active.length : "" - } - subpage: Component { PageNotifications {} } - show: settingsAtTop - } - - MbOK { - description: qsTr("Remove disconnected devices") - value: qsTr("Press to remove") - show: settingsAtTop && deviceList.disconnectedDevices != 0 - editable: true - - function clicked() { - listview.decrementCurrentIndex() - deviceList.removeDisconnected() - } - } - } - VisualDataModel { - 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 - } - } - - VisualItemModel { //////// use VisualItemModel below for v2.93 and earlier - MbSubMenu { - id: menuNotifications - description: qsTr("Notifications") - item: VBusItem { - property variant active: NotificationCenter.notifications.filter( - function isActive(obj) { return obj.active} ) - value: active.length > 0 ? active.length : "" - } - subpage: Component { PageNotifications {} } - show: !settingsAtTop - } - - MbSubMenu { - description: qsTr("Settings") - subpage: Component { PageSettings {} } - show: !settingsAtTop - } - - MbOK { - description: qsTr("Remove disconnected devices") - value: qsTr("Press to remove") - show: !settingsAtTop && deviceList.disconnectedDevices != 0 - editable: true - - function clicked() { - listview.decrementCurrentIndex() - deviceList.removeDisconnected() - } - } - } - } - - Component { - id: vebusPage - PageVebus {} - } - - Component { - id: multiRsPage - PageMultiRs {} - } - - 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 {} - } - - 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: - page = multiRsPage - break; - 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: - page = acInPage - 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 - default: - console.log("unknown service " + name) - return; - } - - deviceList.append(service, page.createObject(root, {service: service, bindPrefix: service.name})) - initListView() - } - - 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/obsoletePatches/PageMain.qml-v2.94.orig b/FileSets/PatchSource/obsoletePatches/PageMain.qml-v2.94.orig deleted file mode 100644 index a82f35c5..00000000 --- a/FileSets/PatchSource/obsoletePatches/PageMain.qml-v2.94.orig +++ /dev/null @@ -1,240 +0,0 @@ -import QtQuick 1.1 -import com.victron.velib 1.0 - -MbPage { - id: root - title: qsTr("Device List") - - model: VisualModels { - VisualDataModel { - 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 - } - } - VisualItemModel { - MbSubMenu { - id: menuNotifications - description: qsTr("Notifications") - item: VBusItem { - property variant active: NotificationCenter.notifications.filter( - function isActive(obj) { return obj.active} ) - value: active.length > 0 ? active.length : "" - } - subpage: Component { 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 - - function clicked() { - listview.decrementCurrentIndex() - deviceList.removeDisconnected() - } - } - } - } - - Component { - id: vebusPage - PageVebus {} - } - - Component { - id: multiRsPage - PageMultiRs {} - } - - 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 {} - } - - 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: - page = multiRsPage - break; - 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: - page = acInPage - 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 - default: - console.log("unknown service " + name) - return; - } - - deviceList.append(service, page.createObject(root, {service: service, bindPrefix: service.name})) - initListView() - } - - 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/obsoletePatches/PageSettingsDisplay.qml-v2.89 b/FileSets/PatchSource/obsoletePatches/PageSettingsDisplay.qml-v2.89 deleted file mode 100644 index 3066b278..00000000 --- a/FileSets/PatchSource/obsoletePatches/PageSettingsDisplay.qml-v2.89 +++ /dev/null @@ -1,109 +0,0 @@ -//////// modified to add GuiMods controls -import QtQuick 1.1 -import com.victron.velib 1.0 -import "utils.js" as Utils - -MbPage { - id: root - property string bindPrefix: "com.victronenergy.settings/Settings/Gui" - - model: VisualItemModel { - MbSwitch { - id: autoBrightness - name: qsTr("Adaptive brightness") - bind: Utils.path(bindPrefix, "/AutoBrightness") - show: vePlatform.hasAutoBrightness - onClicked: vePlatform.autoBrightness = checked; - } - - // note: the backlight is changed during edit, and saved afterwards - MbItemSlider { - id: backlight - show: vePlatform.hasBacklight && !autoBrightness.checked - icondId: "icon-items-brightness" - directUpdates: true - item { - min: 1 - max: vePlatform.maxBrightness - step: 1 - value: vePlatform.brightness - onValueChanged: if (editMode) vePlatform.brightness = item.value; - } - writeAccessLevel: User.AccessUser - onEditModeChanged: if (!editMode) storedBacklight.setValue(item.value) - - VBusItem { - id: storedBacklight - bind: Utils.path(bindPrefix, "/Brightness") - } - } - - MbItemOptions { - show: vePlatform.hasScreenSaver - description: qsTr("Display off time") - bind: Utils.path(bindPrefix, "/DisplayOff") - writeAccessLevel: User.AccessUser - possibleValues: [ - MbOption { description: qsTr("10 sec"); value: 10 }, - MbOption { description: qsTr("30 sec"); value: 30 }, - MbOption { description: qsTr("1 min"); value: 60 }, - MbOption { description: qsTr("10 min"); value: 600 }, - MbOption { description: qsTr("30 min"); value: 1800 }, - MbOption { description: qsTr("Never"); value: 0 } - ] - } - - -////// GuiMods — DarkMode - MbSwitch - { - id: enableDarkMode - bind: "com.victronenergy.settings/Settings/GuiMods/DarkMode" - name: qsTr ("Dark Mode") - writeAccessLevel: User.AccessUser - } - - MbSwitch { - bind: Utils.path(bindPrefix, "/MobileOverview") - name: qsTr("Show boat & motorhome overview") - // When enabled set OverviewMobile as default overview - onClicked: if (checked) defaultOverview.setValue("OverviewMobile") - VBusItem { id: defaultOverview; bind: "com.victronenergy.settings/Settings/Gui/DefaultOverview" } - } - MbSwitch { - bind: Utils.path(bindPrefix, "/TanksOverview") - name: qsTr("Show tanks overview") - } - -//////// add Gui Mods menu - MbSubMenu { - id: guiModsMenu - description: qsTr("Gui Mods") - subpage: Component { - PageSettingsGuiMods { } - } - } - - MbItemOptions { - id: languageSelect - description: qsTr("Language") - writeAccessLevel: User.AccessUser - bind: Utils.path(bindPrefix, "/Language") - possibleValues: [ - MbOption { description: "English"; value: "en" }, - MbOption { description: "Čeština"; value: "cs" }, - MbOption { description: "Deutsch"; value: "de" }, - MbOption { description: "Español"; value: "es" }, - MbOption { description: "Français"; value: "fr" }, - MbOption { description: "Italiano"; value: "it" }, - MbOption { description: "Nederlands"; value: "nl" }, - MbOption { description: "Русский"; value: "ru" }, - MbOption { description: "Română"; value: "ro" }, - MbOption { description: "Svenska"; value: "se" }, - MbOption { description: "Türkçe"; value: "tr" }, - MbOption { description: "中文"; value: "zh" }, - MbOption { description: "العربية"; value: "ar" } - ] - } - } -} diff --git a/FileSets/PatchSource/obsoletePatches/PageSettingsDisplay.qml-v2.89.orig b/FileSets/PatchSource/obsoletePatches/PageSettingsDisplay.qml-v2.89.orig deleted file mode 100644 index dd347ba0..00000000 --- a/FileSets/PatchSource/obsoletePatches/PageSettingsDisplay.qml-v2.89.orig +++ /dev/null @@ -1,90 +0,0 @@ -import QtQuick 1.1 -import com.victron.velib 1.0 -import "utils.js" as Utils - -MbPage { - id: root - property string bindPrefix: "com.victronenergy.settings/Settings/Gui" - - model: VisualItemModel { - MbSwitch { - id: autoBrightness - name: qsTr("Adaptive brightness") - bind: Utils.path(bindPrefix, "/AutoBrightness") - show: vePlatform.hasAutoBrightness - onClicked: vePlatform.autoBrightness = checked; - } - - // note: the backlight is changed during edit, and saved afterwards - MbItemSlider { - id: backlight - show: vePlatform.hasBacklight && !autoBrightness.checked - icondId: "icon-items-brightness" - directUpdates: true - item { - min: 1 - max: vePlatform.maxBrightness - step: 1 - value: vePlatform.brightness - onValueChanged: if (editMode) vePlatform.brightness = item.value; - } - writeAccessLevel: User.AccessUser - onEditModeChanged: if (!editMode) storedBacklight.setValue(item.value) - - VBusItem { - id: storedBacklight - bind: Utils.path(bindPrefix, "/Brightness") - } - } - - MbItemOptions { - show: vePlatform.hasScreenSaver - description: qsTr("Display off time") - bind: Utils.path(bindPrefix, "/DisplayOff") - writeAccessLevel: User.AccessUser - possibleValues: [ - MbOption { description: qsTr("10 sec"); value: 10 }, - MbOption { description: qsTr("30 sec"); value: 30 }, - MbOption { description: qsTr("1 min"); value: 60 }, - MbOption { description: qsTr("10 min"); value: 600 }, - MbOption { description: qsTr("30 min"); value: 1800 }, - MbOption { description: qsTr("Never"); value: 0 } - ] - } - - MbSwitch { - bind: Utils.path(bindPrefix, "/MobileOverview") - name: qsTr("Show boat & motorhome overview") - // When enabled set OverviewMobile as default overview - onClicked: if (checked) defaultOverview.setValue("OverviewMobile") - VBusItem { id: defaultOverview; bind: "com.victronenergy.settings/Settings/Gui/DefaultOverview" } - } - - MbSwitch { - bind: Utils.path(bindPrefix, "/TanksOverview") - name: qsTr("Show tanks overview") - } - - MbItemOptions { - id: languageSelect - description: qsTr("Language") - writeAccessLevel: User.AccessUser - bind: Utils.path(bindPrefix, "/Language") - possibleValues: [ - MbOption { description: "English"; value: "en" }, - MbOption { description: "Čeština"; value: "cs" }, - MbOption { description: "Deutsch"; value: "de" }, - MbOption { description: "Español"; value: "es" }, - MbOption { description: "Français"; value: "fr" }, - MbOption { description: "Italiano"; value: "it" }, - MbOption { description: "Nederlands"; value: "nl" }, - MbOption { description: "Русский"; value: "ru" }, - MbOption { description: "Română"; value: "ro" }, - MbOption { description: "Svenska"; value: "se" }, - MbOption { description: "Türkçe"; value: "tr" }, - MbOption { description: "中文"; value: "zh" }, - MbOption { description: "العربية"; value: "ar" } - ] - } - } -} diff --git a/FileSets/PatchSource/obsoletePatches/PageSettingsDisplay.qml-v2.94 b/FileSets/PatchSource/obsoletePatches/PageSettingsDisplay.qml-v2.94 deleted file mode 100644 index 14f49717..00000000 --- a/FileSets/PatchSource/obsoletePatches/PageSettingsDisplay.qml-v2.94 +++ /dev/null @@ -1,120 +0,0 @@ -//////// modified to add GuiMods controls - -import QtQuick 1.1 -import com.victron.velib 1.0 -import "utils.js" as Utils - -MbPage { - id: root - property string bindPrefix: "com.victronenergy.settings/Settings/Gui" - - model: VisualItemModel { - MbSwitch { - id: autoBrightness - name: qsTr("Adaptive brightness") - bind: Utils.path(bindPrefix, "/AutoBrightness") - show: vePlatform.hasAutoBrightness - onClicked: vePlatform.autoBrightness = checked; - } - - // note: the backlight is changed during edit, and saved afterwards - MbItemSlider { - id: backlight - show: vePlatform.hasBacklight && !autoBrightness.checked - icondId: "icon-items-brightness" - directUpdates: true - item { - min: 1 - max: vePlatform.maxBrightness - step: 1 - value: vePlatform.brightness - onValueChanged: if (editMode) vePlatform.brightness = item.value; - } - writeAccessLevel: User.AccessUser - onEditModeChanged: if (!editMode) storedBacklight.setValue(item.value) - - VBusItem { - id: storedBacklight - bind: Utils.path(bindPrefix, "/Brightness") - } - } - - MbItemOptions { - show: vePlatform.hasScreenSaver - description: qsTr("Display off time") - bind: Utils.path(bindPrefix, "/DisplayOff") - writeAccessLevel: User.AccessUser - possibleValues: [ - MbOption { description: qsTr("10 sec"); value: 10 }, - MbOption { description: qsTr("30 sec"); value: 30 }, - MbOption { description: qsTr("1 min"); value: 60 }, - MbOption { description: qsTr("10 min"); value: 600 }, - MbOption { description: qsTr("30 min"); value: 1800 }, - MbOption { description: qsTr("Never"); value: 0 } - ] - } - -////// GuiMods — DarkMode - MbSwitch - { - id: enableDarkMode - bind: "com.victronenergy.settings/Settings/GuiMods/DarkMode" - name: qsTr ("Dark Mode") - writeAccessLevel: User.AccessUser - } - - MbSwitch { - bind: Utils.path(bindPrefix, "/MobileOverview") - name: qsTr("Show boat & motorhome overview") - // When enabled set OverviewMobile as default overview - onClicked: if (checked) defaultOverview.setValue("OverviewMobile") - VBusItem { id: defaultOverview; bind: "com.victronenergy.settings/Settings/Gui/DefaultOverview" } - } - - MbSwitch { - bind: Utils.path(bindPrefix, "/TanksOverview") - name: qsTr("Show tanks overview") - } - -//////// add Gui Mods menu - MbSubMenu { - id: guiModsMenu - description: qsTr("Gui Mods") - subpage: Component { - PageSettingsGuiMods { } - } - } - - MbItemOptions { - id: languageSelect - description: qsTr("Language") - writeAccessLevel: User.AccessUser - bind: Utils.path(bindPrefix, "/Language") - possibleValues: [ - MbOption { description: "English"; value: "en" }, - MbOption { description: "Čeština"; value: "cs" }, - MbOption { description: "Dansk"; value: "da" }, - MbOption { description: "Deutsch"; value: "de" }, - MbOption { description: "Español"; value: "es" }, - MbOption { description: "Français"; value: "fr" }, - MbOption { description: "Italiano"; value: "it" }, - MbOption { description: "Nederlands"; value: "nl" }, - MbOption { description: "Русский"; value: "ru" }, - MbOption { description: "Română"; value: "ro" }, - MbOption { description: "Svenska"; value: "se" }, - MbOption { description: "Türkçe"; value: "tr" }, - MbOption { description: "中文"; value: "zh" }, - MbOption { description: "العربية"; value: "ar" } - ] - } - - MbSubMenu { - description: qsTr("Units") - subpage: Component { - PageSettingsDisplayUnits { - title: qsTr("Units") - } - } - } - } -} diff --git a/FileSets/PatchSource/obsoletePatches/PageSettingsDisplay.qml-v2.94.orig b/FileSets/PatchSource/obsoletePatches/PageSettingsDisplay.qml-v2.94.orig deleted file mode 100644 index 29a94e49..00000000 --- a/FileSets/PatchSource/obsoletePatches/PageSettingsDisplay.qml-v2.94.orig +++ /dev/null @@ -1,100 +0,0 @@ -import QtQuick 1.1 -import com.victron.velib 1.0 -import "utils.js" as Utils - -MbPage { - id: root - property string bindPrefix: "com.victronenergy.settings/Settings/Gui" - - model: VisualItemModel { - MbSwitch { - id: autoBrightness - name: qsTr("Adaptive brightness") - bind: Utils.path(bindPrefix, "/AutoBrightness") - show: vePlatform.hasAutoBrightness - onClicked: vePlatform.autoBrightness = checked; - } - - // note: the backlight is changed during edit, and saved afterwards - MbItemSlider { - id: backlight - show: vePlatform.hasBacklight && !autoBrightness.checked - icondId: "icon-items-brightness" - directUpdates: true - item { - min: 1 - max: vePlatform.maxBrightness - step: 1 - value: vePlatform.brightness - onValueChanged: if (editMode) vePlatform.brightness = item.value; - } - writeAccessLevel: User.AccessUser - onEditModeChanged: if (!editMode) storedBacklight.setValue(item.value) - - VBusItem { - id: storedBacklight - bind: Utils.path(bindPrefix, "/Brightness") - } - } - - MbItemOptions { - show: vePlatform.hasScreenSaver - description: qsTr("Display off time") - bind: Utils.path(bindPrefix, "/DisplayOff") - writeAccessLevel: User.AccessUser - possibleValues: [ - MbOption { description: qsTr("10 sec"); value: 10 }, - MbOption { description: qsTr("30 sec"); value: 30 }, - MbOption { description: qsTr("1 min"); value: 60 }, - MbOption { description: qsTr("10 min"); value: 600 }, - MbOption { description: qsTr("30 min"); value: 1800 }, - MbOption { description: qsTr("Never"); value: 0 } - ] - } - - MbSwitch { - bind: Utils.path(bindPrefix, "/MobileOverview") - name: qsTr("Show boat & motorhome overview") - // When enabled set OverviewMobile as default overview - onClicked: if (checked) defaultOverview.setValue("OverviewMobile") - VBusItem { id: defaultOverview; bind: "com.victronenergy.settings/Settings/Gui/DefaultOverview" } - } - - MbSwitch { - bind: Utils.path(bindPrefix, "/TanksOverview") - name: qsTr("Show tanks overview") - } - - MbItemOptions { - id: languageSelect - description: qsTr("Language") - writeAccessLevel: User.AccessUser - bind: Utils.path(bindPrefix, "/Language") - possibleValues: [ - MbOption { description: "English"; value: "en" }, - MbOption { description: "Čeština"; value: "cs" }, - MbOption { description: "Dansk"; value: "da" }, - MbOption { description: "Deutsch"; value: "de" }, - MbOption { description: "Español"; value: "es" }, - MbOption { description: "Français"; value: "fr" }, - MbOption { description: "Italiano"; value: "it" }, - MbOption { description: "Nederlands"; value: "nl" }, - MbOption { description: "Русский"; value: "ru" }, - MbOption { description: "Română"; value: "ro" }, - MbOption { description: "Svenska"; value: "se" }, - MbOption { description: "Türkçe"; value: "tr" }, - MbOption { description: "中文"; value: "zh" }, - MbOption { description: "العربية"; value: "ar" } - ] - } - - MbSubMenu { - description: qsTr("Units") - subpage: Component { - PageSettingsDisplayUnits { - title: qsTr("Units") - } - } - } - } -} diff --git a/FileSets/PatchSource/obsoletePatches/PageSettingsDisplay.qml-v3.01 b/FileSets/PatchSource/obsoletePatches/PageSettingsDisplay.qml-v3.01 deleted file mode 100644 index cd0f51d1..00000000 --- a/FileSets/PatchSource/obsoletePatches/PageSettingsDisplay.qml-v3.01 +++ /dev/null @@ -1,123 +0,0 @@ -//////// modified to add GuiMods controls - -import QtQuick 1.1 -import com.victron.velib 1.0 -import "utils.js" as Utils - -MbPage { - id: root - property string bindPrefix: "com.victronenergy.settings/Settings/Gui" - - model: VisibleItemModel { - MbSwitch { - id: autoBrightness - name: qsTr("Adaptive brightness") - bind: Utils.path(bindPrefix, "/AutoBrightness") - show: vePlatform.hasAutoBrightness - onClicked: vePlatform.autoBrightness = checked; - } - - // note: the backlight is changed during edit, and saved afterwards - MbItemSlider { - id: backlight - show: vePlatform.hasBacklight && !autoBrightness.checked - icondId: "icon-items-brightness" - directUpdates: true - item { - min: 1 - max: vePlatform.maxBrightness - step: 1 - value: vePlatform.brightness - onValueChanged: if (editMode) vePlatform.brightness = item.value; - } - writeAccessLevel: User.AccessUser - onEditModeChanged: if (!editMode) storedBacklight.setValue(item.value) - - VBusItem { - id: storedBacklight - bind: Utils.path(bindPrefix, "/Brightness") - } - } - - MbItemOptions { - show: vePlatform.hasScreenSaver - description: qsTr("Display off time") - bind: Utils.path(bindPrefix, "/DisplayOff") - writeAccessLevel: User.AccessUser - possibleValues: [ - MbOption { description: qsTr("10 sec"); value: 10 }, - MbOption { description: qsTr("30 sec"); value: 30 }, - MbOption { description: qsTr("1 min"); value: 60 }, - MbOption { description: qsTr("10 min"); value: 600 }, - MbOption { description: qsTr("30 min"); value: 1800 }, - MbOption { description: qsTr("Never"); value: 0 } - ] - } - -////// GuiMods — DarkMode - MbSwitch - { - id: colorScheme - bind: "com.victronenergy.settings/Settings/GuiMods/DarkMode" - name: qsTr ("Dark Mode") - writeAccessLevel: User.AccessUser - } - - MbSwitch { - bind: Utils.path(bindPrefix, "/MobileOverview") - name: qsTr("Show boat & motorhome overview") - // When enabled set OverviewMobile as default overview - onClicked: if (checked) defaultOverview.setValue("OverviewMobile") - VBusItem { id: defaultOverview; bind: "com.victronenergy.settings/Settings/Gui/DefaultOverview" } - } - - MbSwitch { - bind: Utils.path(bindPrefix, "/TanksOverview") - name: qsTr("Show tanks overview") - } - -//////// add Gui Mods menu - MbSubMenu { - id: guiModsMenu - description: qsTr("Gui Mods") - subpage: Component { - PageSettingsGuiMods { } - } - } - - MbItemOptions { - id: languageSelect - description: qsTr("Language") - writeAccessLevel: User.AccessUser - bind: Utils.path(bindPrefix, "/Language") - // 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. - possibleValues: [ - MbOptionLang { description: "English"; value: "en" }, - MbOptionLang { description: "Čeština"; value: "cs" }, - MbOptionLang { description: "Dansk"; value: "da" }, - MbOptionLang { description: "Deutsch"; value: "de" }, - MbOptionLang { description: "Español"; value: "es" }, - MbOptionLang { description: "Français"; value: "fr" }, - MbOptionLang { description: "Italiano"; value: "it" }, - MbOptionLang { description: "Nederlands"; value: "nl" }, - MbOptionLang { description: "Русский"; value: "ru" }, - MbOptionLang { description: "Română"; value: "ro" }, - MbOptionLang { description: "Svenska"; value: "se" }, - MbOptionLang { description: "Türkçe"; value: "tr" }, - MbOptionLang { description: "中文"; value: "zh" }, - MbOptionLang { description: "العربية"; value: "ar" } - ] - } - - MbSubMenu { - description: qsTr("Units") - subpage: Component { - PageSettingsDisplayUnits { - title: qsTr("Units") - } - } - } - } -} diff --git a/FileSets/PatchSource/obsoletePatches/PageSettingsDisplay.qml-v3.01.orig b/FileSets/PatchSource/obsoletePatches/PageSettingsDisplay.qml-v3.01.orig deleted file mode 100644 index 6d34913d..00000000 --- a/FileSets/PatchSource/obsoletePatches/PageSettingsDisplay.qml-v3.01.orig +++ /dev/null @@ -1,106 +0,0 @@ -import QtQuick 1.1 -import com.victron.velib 1.0 -import "utils.js" as Utils - -MbPage { - id: root - property string bindPrefix: "com.victronenergy.settings/Settings/Gui" - - model: VisibleItemModel { - MbSwitch { - id: autoBrightness - name: qsTr("Adaptive brightness") - bind: Utils.path(bindPrefix, "/AutoBrightness") - show: vePlatform.hasAutoBrightness - onClicked: vePlatform.autoBrightness = checked; - } - - // note: the backlight is changed during edit, and saved afterwards - MbItemSlider { - id: backlight - show: vePlatform.hasBacklight && !autoBrightness.checked - icondId: "icon-items-brightness" - directUpdates: true - item { - min: 1 - max: vePlatform.maxBrightness - step: 1 - value: vePlatform.brightness - onValueChanged: if (editMode) vePlatform.brightness = item.value; - } - writeAccessLevel: User.AccessUser - onEditModeChanged: if (!editMode) storedBacklight.setValue(item.value) - - VBusItem { - id: storedBacklight - bind: Utils.path(bindPrefix, "/Brightness") - } - } - - MbItemOptions { - show: vePlatform.hasScreenSaver - description: qsTr("Display off time") - bind: Utils.path(bindPrefix, "/DisplayOff") - writeAccessLevel: User.AccessUser - possibleValues: [ - MbOption { description: qsTr("10 sec"); value: 10 }, - MbOption { description: qsTr("30 sec"); value: 30 }, - MbOption { description: qsTr("1 min"); value: 60 }, - MbOption { description: qsTr("10 min"); value: 600 }, - MbOption { description: qsTr("30 min"); value: 1800 }, - MbOption { description: qsTr("Never"); value: 0 } - ] - } - - MbSwitch { - bind: Utils.path(bindPrefix, "/MobileOverview") - name: qsTr("Show boat & motorhome overview") - // When enabled set OverviewMobile as default overview - onClicked: if (checked) defaultOverview.setValue("OverviewMobile") - VBusItem { id: defaultOverview; bind: "com.victronenergy.settings/Settings/Gui/DefaultOverview" } - } - - MbSwitch { - bind: Utils.path(bindPrefix, "/TanksOverview") - name: qsTr("Show tanks overview") - } - - MbItemOptions { - id: languageSelect - description: qsTr("Language") - writeAccessLevel: User.AccessUser - bind: Utils.path(bindPrefix, "/Language") - - // 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. - possibleValues: [ - MbOptionLang { description: "English"; value: "en" }, - MbOptionLang { description: "Čeština"; value: "cs" }, - MbOptionLang { description: "Dansk"; value: "da" }, - MbOptionLang { description: "Deutsch"; value: "de" }, - MbOptionLang { description: "Español"; value: "es" }, - MbOptionLang { description: "Français"; value: "fr" }, - MbOptionLang { description: "Italiano"; value: "it" }, - MbOptionLang { description: "Nederlands"; value: "nl" }, - MbOptionLang { description: "Polski"; value: "pl" }, - MbOptionLang { description: "Русский"; value: "ru" }, - MbOptionLang { description: "Română"; value: "ro" }, - MbOptionLang { description: "Svenska"; value: "se" }, - MbOptionLang { description: "ไทย"; value: "th" }, - MbOptionLang { description: "Türkçe"; value: "tr" }, - MbOptionLang { description: "中文"; value: "zh" }, - MbOptionLang { description: "العربية"; value: "ar" } - ] - } - - MbSubMenu { - description: qsTr("Units") - subpage: Component { - PageSettingsDisplayUnits { - title: qsTr("Units") - } - } - } - } -} diff --git a/FileSets/PatchSource/obsoletePatches/PageSettingsDisplay.qml-v3.40~8 b/FileSets/PatchSource/obsoletePatches/PageSettingsDisplay.qml-v3.40~8 deleted file mode 100644 index 80132568..00000000 --- a/FileSets/PatchSource/obsoletePatches/PageSettingsDisplay.qml-v3.40~8 +++ /dev/null @@ -1,135 +0,0 @@ -//////// modified to add GuiMods controls - -import QtQuick 1.1 -import com.victron.velib 1.0 -import "utils.js" as Utils - -MbPage { - id: root - property string bindPrefix: "com.victronenergy.settings/Settings/Gui" - - VBusItem { - id: autoBrightnessSetting - bind: Utils.path(bindPrefix, "/AutoBrightness") - isSetting: true - - // max is set to 0 if AutoBrightness is not supported. - // The setting used to be added unconditionally. - property bool hasAutoBrightness: valid && max === 1 - } - - model: VisibleItemModel { - MbSwitch { - id: autoBrightness - name: qsTr("Adaptive brightness") - bind: Utils.path(bindPrefix, "/AutoBrightness") - show: autoBrightnessSetting.hasAutoBrightness - } - - MbItemSlider { - id: backlight - show: item.valid && (!autoBrightnessSetting.hasAutoBrightness || autoBrightnessSetting.value !== 1) - icondId: "icon-items-brightness" - directUpdates: true - item { - bind: Utils.path(bindPrefix, "/Brightness") - isSetting: true - step: 1 - } - writeAccessLevel: User.AccessUser - } - - MbItemOptions { - show: vePlatform.hasScreenSaver - description: qsTr("Display off time") - bind: Utils.path(bindPrefix, "/DisplayOff") - writeAccessLevel: User.AccessUser - possibleValues: [ - MbOption { description: qsTr("10 sec"); value: 10 }, - MbOption { description: qsTr("30 sec"); value: 30 }, - MbOption { description: qsTr("1 min"); value: 60 }, - MbOption { description: qsTr("10 min"); value: 600 }, - MbOption { description: qsTr("30 min"); value: 1800 }, - MbOption { description: qsTr("Never"); value: 0 } - ] - } - - MbSwitch { - bind: Utils.path(bindPrefix, "/MobileOverview") - name: qsTr("Show boat & motorhome overview") - // When enabled set OverviewMobile as default overview - onClicked: if (checked) defaultOverview.setValue("OverviewMobile") - VBusItem { id: defaultOverview; bind: "com.victronenergy.settings/Settings/Gui/DefaultOverview" } - } - - MbSwitch { - bind: Utils.path(bindPrefix, "/TanksOverview") - name: qsTr("Show tanks overview") - } - -//////// add Gui Mods menu - MbSubMenu { - id: guiModsMenu - description: qsTr("Gui Mods") - subpage: Component { - PageSettingsGuiMods { } - } - } - - MbItemOptions { - id: languageSelect - description: qsTr("Language") - writeAccessLevel: User.AccessUser - bind: Utils.path(bindPrefix, "/Language") - - // 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. - possibleValues: [ - MbOptionLang { description: "English"; value: "en" }, - MbOptionLang { description: "Čeština"; value: "cs" }, - MbOptionLang { description: "Dansk"; value: "da" }, - MbOptionLang { description: "Deutsch"; value: "de" }, - MbOptionLang { description: "Español"; value: "es" }, - MbOptionLang { description: "Français"; value: "fr" }, - MbOptionLang { description: "Italiano"; value: "it" }, - MbOptionLang { description: "Nederlands"; value: "nl" }, - MbOptionLang { description: "Polski"; value: "pl" }, - MbOptionLang { description: "Русский"; value: "ru" }, - MbOptionLang { description: "Română"; value: "ro" }, - MbOptionLang { description: "Svenska"; value: "se" }, - MbOptionLang { description: "ไทย"; value: "th" }, - MbOptionLang { description: "Türkçe"; value: "tr" }, - MbOptionLang { description: "Українська"; value: "uk" }, - MbOptionLang { description: "中文"; value: "zh" }, - MbOptionLang { description: "العربية"; value: "ar" } - ] - } - - MbSubMenu { - description: qsTr("Units") - subpage: Component { - PageSettingsDisplayUnits { - title: qsTr("Units") - } - } - } - - MbItemOptions { - property VBusItem updateFeed: VBusItem { bind: "com.victronenergy.settings/Settings/System/ReleaseType" } - show: vePlatform.isGuiv2Installed() && vePlatform.displayPresent() && updateFeed.value >= Updater.FirmwareCandidate - bind: "com.victronenergy.settings/Settings/Gui/RunningVersion" - description: "Onscreen UI (GX Touch & Ekrano)" - writeAccessLevel: User.AccessUser - possibleValues: [ - MbOption { description: "Standard version"; value: 1 }, - MbOption { description: "Gui-v2 (beta) version"; value: 2 } - ] - onOptionSelected: { - if (newValue === 2) { - vePlatform.splash("Starting the beta UI") - } - } - } - } -} diff --git a/FileSets/PatchSource/obsoletePatches/PageSettingsDisplay.qml-v3.40~8.orig b/FileSets/PatchSource/obsoletePatches/PageSettingsDisplay.qml-v3.40~8.orig deleted file mode 100644 index 39fb990d..00000000 --- a/FileSets/PatchSource/obsoletePatches/PageSettingsDisplay.qml-v3.40~8.orig +++ /dev/null @@ -1,124 +0,0 @@ -import QtQuick 1.1 -import com.victron.velib 1.0 -import "utils.js" as Utils - -MbPage { - id: root - property string bindPrefix: "com.victronenergy.settings/Settings/Gui" - - VBusItem { - id: autoBrightnessSetting - bind: Utils.path(bindPrefix, "/AutoBrightness") - isSetting: true - - // max is set to 0 if AutoBrightness is not supported. - // The setting used to be added unconditionally. - property bool hasAutoBrightness: valid && max === 1 - } - - model: VisibleItemModel { - MbSwitch { - id: autoBrightness - name: qsTr("Adaptive brightness") - bind: Utils.path(bindPrefix, "/AutoBrightness") - show: autoBrightnessSetting.hasAutoBrightness - } - - MbItemSlider { - id: backlight - show: item.valid && (!autoBrightnessSetting.hasAutoBrightness || autoBrightnessSetting.value !== 1) - icondId: "icon-items-brightness" - directUpdates: true - item { - bind: Utils.path(bindPrefix, "/Brightness") - isSetting: true - step: 1 - } - writeAccessLevel: User.AccessUser - } - - MbItemOptions { - show: vePlatform.hasScreenSaver - description: qsTr("Display off time") - bind: Utils.path(bindPrefix, "/DisplayOff") - writeAccessLevel: User.AccessUser - possibleValues: [ - MbOption { description: qsTr("10 sec"); value: 10 }, - MbOption { description: qsTr("30 sec"); value: 30 }, - MbOption { description: qsTr("1 min"); value: 60 }, - MbOption { description: qsTr("10 min"); value: 600 }, - MbOption { description: qsTr("30 min"); value: 1800 }, - MbOption { description: qsTr("Never"); value: 0 } - ] - } - - MbSwitch { - bind: Utils.path(bindPrefix, "/MobileOverview") - name: qsTr("Show boat & motorhome overview") - // When enabled set OverviewMobile as default overview - onClicked: if (checked) defaultOverview.setValue("OverviewMobile") - VBusItem { id: defaultOverview; bind: "com.victronenergy.settings/Settings/Gui/DefaultOverview" } - } - - MbSwitch { - bind: Utils.path(bindPrefix, "/TanksOverview") - name: qsTr("Show tanks overview") - } - - MbItemOptions { - id: languageSelect - description: qsTr("Language") - writeAccessLevel: User.AccessUser - bind: Utils.path(bindPrefix, "/Language") - - // 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. - possibleValues: [ - MbOptionLang { description: "English"; value: "en" }, - MbOptionLang { description: "Čeština"; value: "cs" }, - MbOptionLang { description: "Dansk"; value: "da" }, - MbOptionLang { description: "Deutsch"; value: "de" }, - MbOptionLang { description: "Español"; value: "es" }, - MbOptionLang { description: "Français"; value: "fr" }, - MbOptionLang { description: "Italiano"; value: "it" }, - MbOptionLang { description: "Nederlands"; value: "nl" }, - MbOptionLang { description: "Polski"; value: "pl" }, - MbOptionLang { description: "Русский"; value: "ru" }, - MbOptionLang { description: "Română"; value: "ro" }, - MbOptionLang { description: "Svenska"; value: "se" }, - MbOptionLang { description: "ไทย"; value: "th" }, - MbOptionLang { description: "Türkçe"; value: "tr" }, - MbOptionLang { description: "Українська"; value: "uk" }, - MbOptionLang { description: "中文"; value: "zh" }, - MbOptionLang { description: "العربية"; value: "ar" } - ] - } - - MbSubMenu { - description: qsTr("Units") - subpage: Component { - PageSettingsDisplayUnits { - title: qsTr("Units") - } - } - } - - MbItemOptions { - property VBusItem updateFeed: VBusItem { bind: "com.victronenergy.settings/Settings/System/ReleaseType" } - show: vePlatform.isGuiv2Installed() && vePlatform.displayPresent() && updateFeed.value >= Updater.FirmwareCandidate - bind: "com.victronenergy.settings/Settings/Gui/RunningVersion" - description: "Onscreen UI (GX Touch & Ekrano)" - writeAccessLevel: User.AccessUser - possibleValues: [ - MbOption { description: "Standard version"; value: 1 }, - MbOption { description: "Gui-v2 (beta) version"; value: 2 } - ] - onOptionSelected: { - if (newValue === 2) { - vePlatform.splash("Starting the beta UI") - } - } - } - } -} diff --git a/FileSets/PatchSource/obsoletePatches/attributes.csv-v2.94 b/FileSets/PatchSource/obsoletePatches/attributes.csv-v2.94 deleted file mode 100644 index 6b6ea53e..00000000 --- a/FileSets/PatchSource/obsoletePatches/attributes.csv-v2.94 +++ /dev/null @@ -1,626 +0,0 @@ -com.victronenergy.gps,/Position/Latitude,d,Decimal degrees,2800,int32,10000000,R -com.victronenergy.gps,/Position/Longitude,d,Decimal degrees,2802,int32,10000000,R -com.victronenergy.gps,/Course,d,Degrees,2804,uint16,100,R -com.victronenergy.gps,/Speed,d,m/s,2805,uint16,100,R -com.victronenergy.gps,/Fix,y,,2806,uint16,1,R -com.victronenergy.gps,/NrOfSatellites,y,,2807,uint16,1,R -com.victronenergy.gps,/Altitude,d,m,2808,int32,10,R -com.victronenergy.settings,/Settings/CGwacs/BatteryLife/State,i,0=BL disabled;1=Restarting;2=Self-consumption;3=Self-consumption;4=Self-consumption;5=Discharge disabled;6=Force charge;7=Sustain;9=Keep batteries charged;10=BL Disabled;11=BL Disabled (Low SoC),2900,uint16,1,W -com.victronenergy.settings,/Settings/CGwacs/BatteryLife/MinimumSocLimit,d,%,2901,uint16,10,W -com.victronenergy.settings,/Settings/CGwacs/Hub4Mode,i,1=ESS with Phase Compensation;2=ESS without phase compensation;3=Disabled/External Control,2902,uint16,1,W -com.victronenergy.settings,/Settings/CGwacs/BatteryLife/SocLimit,d,%,2903,uint16,10,R -com.victronenergy.vebus,/Ac/ActiveIn/L1/V,d,V AC,3,uint16,10,R -com.victronenergy.vebus,/Ac/ActiveIn/L2/V,d,V AC,4,uint16,10,R -com.victronenergy.vebus,/Ac/ActiveIn/L3/V,d,V AC,5,uint16,10,R -com.victronenergy.vebus,/Ac/ActiveIn/L1/I,d,A AC,6,int16,10,R -com.victronenergy.vebus,/Ac/ActiveIn/L2/I,d,A AC,7,int16,10,R -com.victronenergy.vebus,/Ac/ActiveIn/L3/I,d,A AC,8,int16,10,R -com.victronenergy.vebus,/Ac/ActiveIn/L1/F,d,Hz,9,int16,100,R -com.victronenergy.vebus,/Ac/ActiveIn/L2/F,d,Hz,10,int16,100,R -com.victronenergy.vebus,/Ac/ActiveIn/L3/F,d,Hz,11,int16,100,R -com.victronenergy.vebus,/Ac/ActiveIn/L1/P,i,VA or Watts,12,int16,0.1,R -com.victronenergy.vebus,/Ac/ActiveIn/L2/P,i,VA or Watts,13,int16,0.1,R -com.victronenergy.vebus,/Ac/ActiveIn/L3/P,i,VA or Watts,14,int16,0.1,R -com.victronenergy.vebus,/Ac/Out/L1/V,d,V AC,15,uint16,10,R -com.victronenergy.vebus,/Ac/Out/L2/V,d,V AC,16,uint16,10,R -com.victronenergy.vebus,/Ac/Out/L3/V,d,V AC,17,uint16,10,R -com.victronenergy.vebus,/Ac/Out/L1/I,d,A AC,18,int16,10,R -com.victronenergy.vebus,/Ac/Out/L2/I,d,A AC,19,int16,10,R -com.victronenergy.vebus,/Ac/Out/L3/I,d,A AC,20,int16,10,R -com.victronenergy.vebus,/Ac/Out/L1/F,d,Hz,21,int16,100,R -com.victronenergy.vebus,/Ac/ActiveIn/CurrentLimit,d,A,22,int16,10,W -com.victronenergy.vebus,/Ac/Out/L1/P,i,VA or Watts,23,int16,0.1,R -com.victronenergy.vebus,/Ac/Out/L2/P,i,VA or Watts,24,int16,0.1,R -com.victronenergy.vebus,/Ac/Out/L3/P,i,VA or Watts,25,int16,0.1,R -com.victronenergy.vebus,/Dc/0/Voltage,d,V DC,26,uint16,100,R -com.victronenergy.vebus,/Dc/0/Current,d,A DC,27,int16,10,R -com.victronenergy.vebus,/Ac/NumberOfPhases,u,count,28,uint16,1,R -com.victronenergy.vebus,/Ac/ActiveIn/ActiveInput,u,0=AC Input 1;1=AC Input 2;240=Disconnected,29,uint16,1,R -com.victronenergy.vebus,/Soc,d,%,30,uint16,10,W -com.victronenergy.vebus,/State,u,0=Off;1=Low Power;2=Fault;3=Bulk;4=Absorption;5=Float;6=Storage;7=Equalize;8=Passthru;9=Inverting;10=Power assist;11=Power supply;252=Bulk protection,31,uint16,1,R -com.victronenergy.vebus,/VebusError,u,0=No error;1=VE.Bus Error 1: Device is switched off because one of the other phases in the system has switched off;2=VE.Bus Error 2: New and old types MK2 are mixed in the system;3=VE.Bus Error 3: Not all- or more than- the expected devices were found in the system;4=VE.Bus Error 4: No other device whatsoever detected;5=VE.Bus Error 5: Overvoltage on AC-out;6=VE.Bus Error 6: Error in DDC Program;7=VE.Bus BMS connected- which requires an Assistant- but no assistant found;10=VE.Bus Error 10: System time synchronisation problem occurred;14=VE.Bus Error 14: Device cannot transmit data;16=VE.Bus Error 16: Dongle missing;17=VE.Bus Error 17: One of the devices assumed master status because the original master failed;18=VE.Bus Error 18: AC Overvoltage on the output of a slave has occurred while already switched off;22=VE.Bus Error 22: This device cannot function as slave;24=VE.Bus Error 24: Switch-over system protection initiated;25=VE.Bus Error 25: Firmware incompatibility. The firmware of one of the connected device is not sufficiently up to date to operate in conjunction with this device;26=VE.Bus Error 26: Internal error,32,uint16,1,R -com.victronenergy.vebus,/Mode,u,1=Charger Only;2=Inverter Only;3=On;4=Off,33,uint16,1,W -com.victronenergy.vebus,/Alarms/HighTemperature,u,0=Ok;1=Warning;2=Alarm,34,uint16,1,R -com.victronenergy.vebus,/Alarms/LowBattery,u,0=Ok;1=Warning;2=Alarm,35,uint16,1,R -com.victronenergy.vebus,/Alarms/Overload,u,0=Ok;1=Warning;2=Alarm,36,uint16,1,R -com.victronenergy.vebus,/Alarms/TemperatureSensor,u,0=Ok;1=Warning;2=Alarm,42,uint16,1,R -com.victronenergy.vebus,/Alarms/VoltageSensor,u,0=Ok;1=Warning;2=Alarm,43,uint16,1,R -com.victronenergy.vebus,/Alarms/L1/HighTemperature,u,0=Ok;1=Warning;2=Alarm,44,uint16,1,R -com.victronenergy.vebus,/Alarms/L1/LowBattery,u,0=Ok;1=Warning;2=Alarm,45,uint16,1,R -com.victronenergy.vebus,/Alarms/L1/Overload,u,0=Ok;1=Warning;2=Alarm,46,uint16,1,R -com.victronenergy.vebus,/Alarms/L1/Ripple,u,0=Ok;1=Warning;2=Alarm,47,uint16,1,R -com.victronenergy.vebus,/Alarms/L2/HighTemperature,u,0=Ok;1=Warning;2=Alarm,48,uint16,1,R -com.victronenergy.vebus,/Alarms/L2/LowBattery,u,0=Ok;1=Warning;2=Alarm,49,uint16,1,R -com.victronenergy.vebus,/Alarms/L2/Overload,u,0=Ok;1=Warning;2=Alarm,50,uint16,1,R -com.victronenergy.vebus,/Alarms/L2/Ripple,u,0=Ok;1=Warning;2=Alarm,51,uint16,1,R -com.victronenergy.vebus,/Alarms/L3/HighTemperature,u,0=Ok;1=Warning;2=Alarm,52,uint16,1,R -com.victronenergy.vebus,/Alarms/L3/LowBattery,u,0=Ok;1=Warning;2=Alarm,53,uint16,1,R -com.victronenergy.vebus,/Alarms/L3/Overload,u,0=Ok;1=Warning;2=Alarm,54,uint16,1,R -com.victronenergy.vebus,/Alarms/L3/Ripple,u,0=Ok;1=Warning;2=Alarm,55,uint16,1,R -com.victronenergy.vebus,/Hub4/L1/AcPowerSetpoint,d,W,37,int16,1,W -com.victronenergy.vebus,/Hub4/DisableCharge,u,0=Charge allowed;1=Charge disabled,38,uint16,1,W -com.victronenergy.vebus,/Hub4/DisableFeedIn,u,0=Feed in allowed;1=Feed in disabled,39,uint16,1,W -com.victronenergy.vebus,/Hub4/L2/AcPowerSetpoint,d,W,40,int16,1,W -com.victronenergy.vebus,/Hub4/L3/AcPowerSetpoint,d,W,41,int16,1,W -com.victronenergy.vebus,/PvInverter/Disable,u,0=PV enabled;1=PV disabled,56,uint16,1,W -com.victronenergy.vebus,/Bms/AllowToCharge,u,0=No;1=Yes,57,uint16,1,R -com.victronenergy.vebus,/Bms/AllowToDischarge,u,0=No;1=Yes,58,uint16,1,R -com.victronenergy.vebus,/Bms/BmsExpected,u,0=No;1=Yes,59,uint16,1,R -com.victronenergy.vebus,/Bms/Error,u,0=No;1=Yes,60,uint16,1,R -com.victronenergy.vebus,/Dc/0/Temperature,d,Degrees celsius,61,int16,10,R -com.victronenergy.vebus,/SystemReset,y,0=No action;1=VE.Bus Reset,62,uint16,1,W -com.victronenergy.vebus,/Alarms/PhaseRotation,u,0=Ok;1=Warning,63,uint16,1,R -com.victronenergy.vebus,/Alarms/GridLost,i,0=No alarm;2=Alarm,64,uint16,1,R -com.victronenergy.vebus,/Hub4/DoNotFeedInOvervoltage,i,0=Feed in overvoltage;1=Don't feed in overvoltage,65,uint16,1,W -com.victronenergy.vebus,/Hub4/L1/MaxFeedInPower,d,W,66,uint16,0.01,W -com.victronenergy.vebus,/Hub4/L2/MaxFeedInPower,d,W,67,uint16,0.01,W -com.victronenergy.vebus,/Hub4/L3/MaxFeedInPower,d,W,68,uint16,0.01,W -com.victronenergy.vebus,/Ac/State/IgnoreAcIn1,i,0=AC input not ignored; 1=AC input ignored,69,uint16,1,R -com.victronenergy.vebus,/Ac/State/IgnoreAcIn2,i,0=AC input not ignored; 1=AC input ignored,70,uint16,1,R -com.victronenergy.vebus,/Hub4/TargetPowerIsMaxFeedIn,i,0=AcPowerSetpoint interpreted normally; 1=AcPowerSetpoint as OvervoltageFeedIn limit,71,uint16,,W -com.victronenergy.vebus,/Hub4/FixSolarOffsetTo100mV,i,0=OvervoltageFeedIn uses 1V offset; 1=OvervoltageFeedIn uses 0.1V offset,72,uint16,,W -com.victronenergy.vebus,/Hub4/Sustain,i,0=Sustain inactive; 1=Sustain active,73,uint16,1,R -com.victronenergy.vebus,/Energy/AcIn1ToAcOut,d,kWh,74,uint32,100,R -com.victronenergy.vebus,/Energy/AcIn1ToInverter,d,kWh,76,uint32,100,R -com.victronenergy.vebus,/Energy/AcIn2ToAcOut,d,kWh,78,uint32,100,R -com.victronenergy.vebus,/Energy/AcIn2ToInverter,d,kWh,80,uint32,100,R -com.victronenergy.vebus,/Energy/AcOutToAcIn1,d,kWh,82,uint32,100,R -com.victronenergy.vebus,/Energy/AcOutToAcIn2,d,kWh,84,uint32,100,R -com.victronenergy.vebus,/Energy/InverterToAcIn1,d,kWh,86,uint32,100,R -com.victronenergy.vebus,/Energy/InverterToAcIn2,d,kWh,88,uint32,100,R -com.victronenergy.vebus,/Energy/InverterToAcOut,d,kWh,90,uint32,100,R -com.victronenergy.vebus,/Energy/OutToInverter,d,kWh,92,uint32,100,R -com.victronenergy.battery,/Dc/0/Voltage,d,V DC,259,uint16,100,R -com.victronenergy.battery,/Dc/1/Voltage,d,V DC,260,uint16,100,R -com.victronenergy.battery,/Dc/0/Current,d,A DC,261,int16,10,R -com.victronenergy.battery,/Dc/0/Temperature,d,Degrees celsius,262,int16,10,R -com.victronenergy.battery,/Dc/0/MidVoltage,d,V DC,263,uint16,100,R -com.victronenergy.battery,/Dc/0/MidVoltageDeviation,d,%,264,uint16,100,R -com.victronenergy.battery,/ConsumedAmphours,d,Ah,265,uint16,-10,R -com.victronenergy.battery,/Soc,y,%,266,uint16,10,R -com.victronenergy.battery,/Soh,y,%,304,uint16,10,R -com.victronenergy.battery,/TimeToGo,q,seconds,303,uint16,0.01,R -com.victronenergy.battery,/Info/MaxChargeVoltage,u,V DC,305,uint16,10,R -com.victronenergy.battery,/Info/BatteryLowVoltage,u,V DC,306,uint16,10,R -com.victronenergy.battery,/Info/MaxChargeCurrent,u,A DC,307,uint16,10,R -com.victronenergy.battery,/Info/MaxDischargeCurrent,u,A DC,308,uint16,10,R -com.victronenergy.battery,/Alarms/Alarm,y,0=No alarm;2=Alarm,267,uint16,1,R -com.victronenergy.battery,/Alarms/LowVoltage,u,0=No alarm;2=Alarm,268,uint16,1,R -com.victronenergy.battery,/Alarms/HighVoltage,u,0=No alarm;2=Alarm,269,uint16,1,R -com.victronenergy.battery,/Alarms/LowStarterVoltage,u,0=No alarm;2=Alarm,270,uint16,1,R -com.victronenergy.battery,/Alarms/HighStarterVoltage,u,0=No alarm;2=Alarm,271,uint16,1,R -com.victronenergy.battery,/Alarms/LowSoc,u,0=No alarm;2=Alarm,272,uint16,1,R -com.victronenergy.battery,/Alarms/LowTemperature,u,0=No alarm;2=Alarm,273,uint16,1,R -com.victronenergy.battery,/Alarms/HighTemperature,u,0=No alarm;2=Alarm,274,uint16,1,R -com.victronenergy.battery,/Alarms/MidVoltage,u,0=No alarm;2=Alarm,275,uint16,1,R -com.victronenergy.battery,/Alarms/LowFusedVoltage,u,0=No alarm;2=Alarm,276,uint16,1,R -com.victronenergy.battery,/Alarms/HighFusedVoltage,u,0=No alarm;2=Alarm,277,uint16,1,R -com.victronenergy.battery,/Alarms/FuseBlown,u,0=No alarm;2=Alarm,278,uint16,1,R -com.victronenergy.battery,/Alarms/HighInternalTemperature,u,0=No alarm;2=Alarm,279,uint16,1,R -com.victronenergy.battery,/Alarms/HighChargeCurrent,u,0=No alarm;2=Alarm,320,uint16,1,R -com.victronenergy.battery,/Alarms/HighDischargeCurrent,u,0=No alarm;2=Alarm,321,uint16,1,R -com.victronenergy.battery,/Alarms/CellImbalance,u,0=No alarm;2=Alarm,322,uint16,1,R -com.victronenergy.battery,/Alarms/InternalFailure,u,0=No alarm;2=Alarm,323,uint16,1,R -com.victronenergy.battery,/Alarms/HighChargeTemperature,u,0=No alarm;2=Alarm,324,uint16,1,R -com.victronenergy.battery,/Alarms/LowChargeTemperature,u,0=No alarm;2=Alarm,325,uint16,1,R -com.victronenergy.battery,/Alarms/LowCellVoltage,u,0=No alarm;1=Almost discharged;2=Alarm,326,uint16,1,R -com.victronenergy.battery,/Relay/0/State,y,0=Open;1=Closed,280,uint16,1,W -com.victronenergy.battery,/History/DeepestDischarge,d,Ah,281,uint16,-10,R -com.victronenergy.battery,/History/LastDischarge,d,Ah,282,uint16,-10,R -com.victronenergy.battery,/History/AverageDischarge,d,Ah,283,uint16,-10,R -com.victronenergy.battery,/History/ChargeCycles,i,count,284,uint16,1,R -com.victronenergy.battery,/History/FullDischarges,i,count,285,uint16,1,R -com.victronenergy.battery,/History/TotalAhDrawn,d,Ah,286,uint16,-10,R -com.victronenergy.battery,/History/MinimumVoltage,d,V DC,287,uint16,100,R -com.victronenergy.battery,/History/MaximumVoltage,d,V DC,288,uint16,100,R -com.victronenergy.battery,/History/TimeSinceLastFullCharge,i,seconds,289,uint16,0.01,R -com.victronenergy.battery,/History/AutomaticSyncs,i,count,290,uint16,1,R -com.victronenergy.battery,/History/LowVoltageAlarms,i,count,291,uint16,1,R -com.victronenergy.battery,/History/HighVoltageAlarms,i,count,292,uint16,1,R -com.victronenergy.battery,/History/LowStarterVoltageAlarms,i,count,293,uint16,1,R -com.victronenergy.battery,/History/HighStarterVoltageAlarms,i,count,294,uint16,1,R -com.victronenergy.battery,/History/MinimumStarterVoltage,d,V DC,295,uint16,100,R -com.victronenergy.battery,/History/MaximumStarterVoltage,d,V DC,296,uint16,100,R -com.victronenergy.battery,/History/LowFusedVoltageAlarms,i,count,297,uint16,1,R -com.victronenergy.battery,/History/HighFusedVoltageAlarms,i,count,298,uint16,1,R -com.victronenergy.battery,/History/MinimumFusedVoltage,d,V DC,299,uint16,100,R -com.victronenergy.battery,/History/MaximumFusedVoltage,d,V DC,300,uint16,100,R -com.victronenergy.battery,/History/DischargedEnergy,d,kWh,301,uint16,10,R -com.victronenergy.battery,/History/ChargedEnergy,d,kWh,302,uint16,10,R -com.victronenergy.battery,/Capacity,d,Ah,309,uint16,10,R -com.victronenergy.battery,/State,y,0=Initializing (Wait start);1=Initializing (before boot);2=Initializing (Before boot delay);3=Initializing (Wait boot);4=Initializing;5=Initializing (Measure battery voltage);6=Initializing (Calculate battery voltage);7=Initializing (Wait bus voltage);8=Initializing (Wait for lynx shunt);9=Running;10=Error (10);11=Error (11),1282,uint16,1,R -com.victronenergy.battery,/ErrorCode,y,0=No error;1=Battery initialization error;2=No batteries connected;3=Unknown battery connected;4=Different battery type;5=Number of batteries incorrect;6=Lynx Shunt not found;7=Battery measure error;8=Internal calculation error;9=Batteries in series not ok;10=Number of batteries incorrect;11=Hardware error;12=Watchdog error;13=Over voltage;14=Under voltage;15=Over temperature;16=Under temperature;17=Hardware fault;18=Standby shutdown;19=Pre-charge charge error;20=Safety contactor check error;21=Pre-charge discharge error;22=ADC error;23=Slave error;24=Slave warning;25=Pre-charge error;26=Safety contactor error;27=Over current;28=Slave update failed;29=Slave update unavailable,1283,uint16,1,R -com.victronenergy.battery,/SystemSwitch,y,0=Disabled;1=Enabled,1284,uint16,1,R -com.victronenergy.battery,/Balancing,y,0=Inactive;1=Active,1285,uint16,1,R -com.victronenergy.battery,/System/NrOfBatteries,y,count,1286,uint16,1,R -com.victronenergy.battery,/System/BatteriesParallel,y,count,1287,uint16,1,R -com.victronenergy.battery,/System/BatteriesSeries,y,count,1288,uint16,1,R -com.victronenergy.battery,/System/NrOfCellsPerBattery,y,count,1289,uint16,1,R -com.victronenergy.battery,/System/MinCellVoltage,d,V DC,1290,uint16,100,R -com.victronenergy.battery,/System/MaxCellVoltage,d,V DC,1291,uint16,100,R -com.victronenergy.battery,/Diagnostics/ShutDownsDueError,q,count,1292,uint16,1,R -com.victronenergy.battery,/Diagnostics/LastErrors/1/Error,y,0=No error;1=Battery initialization error;2=No batteries connected;3=Unknown battery connected;4=Different battery type;5=Number of batteries incorrect;6=Lynx Shunt not found;7=Battery measure error;8=Internal calculation error;9=Batteries in series not ok;10=Number of batteries incorrect;11=Hardware error;12=Watchdog error;13=Over voltage;14=Under voltage;15=Over temperature;16=Under temperature;17=Hardware fault;18=Standby shutdown;19=Pre-charge charge error;20=Safety contactor check error;21=Pre-charge discharge error;22=ADC error;23=Slave error;24=Slave warning;25=Pre-charge error;26=Safety contactor error;27=Over current;28=Slave update failed;29=Slave update unavailable,1293,uint16,1,R -com.victronenergy.battery,/Diagnostics/LastErrors/2/Error,y,0=No error;1=Battery initialization error;2=No batteries connected;3=Unknown battery connected;4=Different battery type;5=Number of batteries incorrect;6=Lynx Shunt not found;7=Battery measure error;8=Internal calculation error;9=Batteries in series not ok;10=Number of batteries incorrect;11=Hardware error;12=Watchdog error;13=Over voltage;14=Under voltage;15=Over temperature;16=Under temperature;17=Hardware fault;18=Standby shutdown;19=Pre-charge charge error;20=Safety contactor check error;21=Pre-charge discharge error;22=ADC error;23=Slave error;24=Slave warning;25=Pre-charge error;26=Safety contactor error;27=Over current;28=Slave update failed;29=Slave update unavailable,1294,uint16,1,R -com.victronenergy.battery,/Diagnostics/LastErrors/3/Error,y,0=No error;1=Battery initialization error;2=No batteries connected;3=Unknown battery connected;4=Different battery type;5=Number of batteries incorrect;6=Lynx Shunt not found;7=Battery measure error;8=Internal calculation error;9=Batteries in series not ok;10=Number of batteries incorrect;11=Hardware error;12=Watchdog error;13=Over voltage;14=Under voltage;15=Over temperature;16=Under temperature;17=Hardware fault;18=Standby shutdown;19=Pre-charge charge error;20=Safety contactor check error;21=Pre-charge discharge error;22=ADC error;23=Slave error;24=Slave warning;25=Pre-charge error;26=Safety contactor error;27=Over current;28=Slave update failed;29=Slave update unavailable,1295,uint16,1,R -com.victronenergy.battery,/Diagnostics/LastErrors/4/Error,y,0=No error;1=Battery initialization error;2=No batteries connected;3=Unknown battery connected;4=Different battery type;5=Number of batteries incorrect;6=Lynx Shunt not found;7=Battery measure error;8=Internal calculation error;9=Batteries in series not ok;10=Number of batteries incorrect;11=Hardware error;12=Watchdog error;13=Over voltage;14=Under voltage;15=Over temperature;16=Under temperature;17=Hardware fault;18=Standby shutdown;19=Pre-charge charge error;20=Safety contactor check error;21=Pre-charge discharge error;22=ADC error;23=Slave error;24=Slave warning;25=Pre-charge error;26=Safety contactor error;27=Over current;28=Slave update failed;29=Slave update unavailable,1296,uint16,1,R -com.victronenergy.battery,/Diagnostics/LastErrors/1/Time,u,,310,int32,1,R -com.victronenergy.battery,/Diagnostics/LastErrors/2/Time,u,,312,int32,1,R -com.victronenergy.battery,/Diagnostics/LastErrors/3/Time,u,,314,int32,1,R -com.victronenergy.battery,/Diagnostics/LastErrors/4/Time,u,,316,int32,1,R -com.victronenergy.battery,/System/MinCellTemperature,d,Degrees celsius,318,int16,10,R -com.victronenergy.battery,/System/MaxCellTemperature,d,Degrees celsius,319,int16,10,R -com.victronenergy.battery,/Io/AllowToCharge,y,0=No;1=Yes,1297,uint16,1,R -com.victronenergy.battery,/Io/AllowToDischarge,y,0=No;1=Yes,1298,uint16,1,R -com.victronenergy.battery,/Io/ExternalRelay,y,0=Inactive;1=Active,1299,uint16,1,R -com.victronenergy.battery,/History/MinimumCellVoltage,d,V DC,1300,uint16,100,R -com.victronenergy.battery,/History/MaximumCellVoltage,d,V DC,1301,uint16,100,R -com.victronenergy.battery,/System/NrOfModulesOffline,u,,1302,uint16,1,R -com.victronenergy.battery,/System/NrOfModulesOnline,u,,1303,uint16,1,R -com.victronenergy.battery,/System/NrOfModulesBlockingCharge,u,,1304,uint16,1,R -com.victronenergy.battery,/System/NrOfModulesBlockingDischarge,u,,1305,uint16,1,R -com.victronenergy.battery,/System/MinVoltageCellId,s,,1306,string[4],1,R -com.victronenergy.battery,/System/MaxVoltageCellId,s,,1310,string[4],1,R -com.victronenergy.battery,/System/MinTemperatureCellId,s,,1314,string[4],1,R -com.victronenergy.battery,/System/MaxTemperatureCellId,s,,1318,string[4],1,R -com.victronenergy.solarcharger,/Dc/0/Voltage,d,V DC,771,uint16,100,R -com.victronenergy.solarcharger,/Dc/0/Current,d,A DC,772,int16,10,R -com.victronenergy.solarcharger,/Dc/0/Temperature,d,Degrees celsius,773,int16,10,R -com.victronenergy.solarcharger,/Mode,i,1=On;4=Off,774,uint16,1,W -com.victronenergy.solarcharger,/State,i,0=Off;2=Fault;3=Bulk;4=Absorption;5=Float;6=Storage;7=Equalize;11=Other (Hub-1);252=Hub-1,775,uint16,1,R -com.victronenergy.solarcharger,/Pv/V,d,V DC,776,uint16,100,R -com.victronenergy.solarcharger,/Pv/I,d,A DC,777,,10,R,HANDLED IN mappings.c -com.victronenergy.solarcharger,/Yield/Power,d,W,789,uint16,10,R -com.victronenergy.solarcharger,/Equalization/Pending,i,0=No;1=Yes;2=Error;3=Unavailable- Unknown,778,uint16,1,R -com.victronenergy.solarcharger,/Equalization/TimeRemaining,d,seconds,779,uint16,10,R -com.victronenergy.solarcharger,/Relay/0/State,i,0=Open;1=Closed,780,uint16,1,R -com.victronenergy.solarcharger,/Alarms/Alarm,i,0=No alarm;2=Alarm,781,uint16,1,R -com.victronenergy.solarcharger,/Alarms/LowVoltage,i,0=No alarm;2=Alarm,782,uint16,1,R -com.victronenergy.solarcharger,/Alarms/HighVoltage,i,0=No alarm;2=Alarm,783,uint16,1,R -com.victronenergy.solarcharger,/History/Daily/0/Yield,d,kWh,784,uint16,10,R -com.victronenergy.solarcharger,/History/Daily/0/MaxPower,d,W,785,uint16,1,R -com.victronenergy.solarcharger,/History/Daily/1/Yield,d,kWh,786,uint16,10,R -com.victronenergy.solarcharger,/History/Daily/1/MaxPower,d,W,787,uint16,1,R -com.victronenergy.solarcharger,/ErrorCode,i,0=No error;1=Battery temperature too high;2=Battery voltage too high;3=Battery temperature sensor miswired (+);4=Battery temperature sensor miswired (-);5=Battery temperature sensor disconnected;6=Battery voltage sense miswired (+);7=Battery voltage sense miswired (-);8=Battery voltage sense disconnected;9=Battery voltage wire losses too high;17=Charger temperature too high;18=Charger over-current;19=Charger current polarity reversed;20=Bulk time limit reached;22=Charger temperature sensor miswired;23=Charger temperature sensor disconnected;34=Input current too high,788,uint16,1,R -com.victronenergy.solarcharger,/Yield/User,d,kWh,790,uint16,10,R -com.victronenergy.solarcharger,/MppOperationMode,i,0=Off;1=Voltage/current limited;2=MPPT active;255=Not available,791,uint16,1,R -com.victronenergy.solarcharger,/Pv/0/V,d,V DC,3700,uint16,100,R -com.victronenergy.solarcharger,/Pv/1/V,d,V DC,3701,uint16,100,R -com.victronenergy.solarcharger,/Pv/2/V,d,V DC,3702,uint16,100,R -com.victronenergy.solarcharger,/Pv/3/V,d,V DC,3703,uint16,100,R -com.victronenergy.solarcharger,/History/Daily/0/Pv/0/Yield,d,kWh,3708,uint16,10,R -com.victronenergy.solarcharger,/History/Daily/0/Pv/1/Yield,d,kWh,3709,uint16,10,R -com.victronenergy.solarcharger,/History/Daily/0/Pv/2/Yield,d,kWh,3710,uint16,10,R -com.victronenergy.solarcharger,/History/Daily/0/Pv/3/Yield,d,kWh,3711,uint16,10,R -com.victronenergy.solarcharger,/History/Daily/1/Pv/0/Yield,d,kWh,3712,uint16,10,R -com.victronenergy.solarcharger,/History/Daily/1/Pv/1/Yield,d,kWh,3713,uint16,10,R -com.victronenergy.solarcharger,/History/Daily/1/Pv/2/Yield,d,kWh,3714,uint16,10,R -com.victronenergy.solarcharger,/History/Daily/1/Pv/3/Yield,d,kWh,3715,uint16,10,R -com.victronenergy.solarcharger,/History/Daily/0/Pv/0/MaxPower,d,W,3716,uint16,1,R -com.victronenergy.solarcharger,/History/Daily/0/Pv/1/MaxPower,d,W,3717,uint16,1,R -com.victronenergy.solarcharger,/History/Daily/0/Pv/2/MaxPower,d,W,3718,uint16,1,R -com.victronenergy.solarcharger,/History/Daily/0/Pv/3/MaxPower,d,W,3719,uint16,1,R -com.victronenergy.solarcharger,/History/Daily/1/Pv/0/MaxPower,d,W,3720,uint16,1,R -com.victronenergy.solarcharger,/History/Daily/1/Pv/1/MaxPower,d,W,3721,uint16,1,R -com.victronenergy.solarcharger,/History/Daily/1/Pv/2/MaxPower,d,W,3722,uint16,1,R -com.victronenergy.solarcharger,/History/Daily/1/Pv/3/MaxPower,d,W,3723,uint16,1,R -com.victronenergy.solarcharger,/Pv/0/P,d,W,3724,uint16,1,R -com.victronenergy.solarcharger,/Pv/1/P,d,W,3725,uint16,1,R -com.victronenergy.solarcharger,/Pv/2/P,d,W,3726,uint16,1,R -com.victronenergy.solarcharger,/Pv/3/P,d,W,3727,uint16,1,R -com.victronenergy.system,/Ac/PvOnOutput/L1/Power,d,W,808,uint16,1,R -com.victronenergy.system,/Ac/PvOnOutput/L2/Power,d,W,809,uint16,1,R -com.victronenergy.system,/Ac/PvOnOutput/L3/Power,d,W,810,uint16,1,R -com.victronenergy.system,/Ac/PvOnGrid/L1/Power,d,W,811,uint16,1,R -com.victronenergy.system,/Ac/PvOnGrid/L2/Power,d,W,812,uint16,1,R -com.victronenergy.system,/Ac/PvOnGrid/L3/Power,d,W,813,uint16,1,R -com.victronenergy.system,/Ac/PvOnGenset/L1/Power,d,W,814,uint16,1,R -com.victronenergy.system,/Ac/PvOnGenset/L2/Power,d,W,815,uint16,1,R -com.victronenergy.system,/Ac/PvOnGenset/L3/Power,d,W,816,uint16,1,R -com.victronenergy.system,/Dc/Pv/Power,d,W,850,uint16,1,R -com.victronenergy.system,/Dc/Pv/Current,d,A DC,851,int16,10,R -com.victronenergy.system,/Dc/Charger/Power,d,W,855,uint16,1,R -com.victronenergy.system,/Ac/Consumption/L1/Power,d,W,817,uint16,1,R -com.victronenergy.system,/Ac/Consumption/L2/Power,d,W,818,uint16,1,R -com.victronenergy.system,/Ac/Consumption/L3/Power,d,W,819,uint16,1,R -com.victronenergy.system,/Ac/Grid/L1/Power,d,W,820,int16,1,R -com.victronenergy.system,/Ac/Grid/L2/Power,d,W,821,int16,1,R -com.victronenergy.system,/Ac/Grid/L3/Power,d,W,822,int16,1,R -com.victronenergy.system,/Ac/Genset/L1/Power,d,W,823,int16,1,R -com.victronenergy.system,/Ac/Genset/L2/Power,d,W,824,int16,1,R -com.victronenergy.system,/Ac/Genset/L3/Power,d,W,825,int16,1,R -com.victronenergy.system,/Ac/ActiveIn/Source,u,0=Not available;1=Grid;2=Generator;3=Shore power;240=Not connected,826,int16,1,R -com.victronenergy.system,/Dc/System/Power,d,W,860,int16,1,R -com.victronenergy.system,/Dc/Battery/Voltage,d,V DC,840,uint16,10,R -com.victronenergy.system,/Dc/Battery/Current,d,A DC,841,int16,10,R -com.victronenergy.system,/Dc/Battery/Power,d,W,842,int16,1,R -com.victronenergy.system,/Dc/Vebus/Current,d,A DC,865,int16,10,R -com.victronenergy.system,/Dc/Vebus/Power,d,W,866,int16,1,R -com.victronenergy.system,/Dc/Battery/Soc,d,%,843,uint16,1,R -com.victronenergy.system,/Dc/Battery/State,i,0=idle;1=charging;2=discharging,844,uint16,1,R -com.victronenergy.system,/Dc/Battery/ConsumedAmphours,d,Ah,845,uint16,-10,R -com.victronenergy.system,/Dc/Battery/TimeToGo,d,s,846,uint16,0.01,R -com.victronenergy.system,/Relay/0/State,i,0=Open;1=Closed,806,uint16,1,W -com.victronenergy.system,/Relay/1/State,i,0=Open;1=Closed,807,uint16,1,W -com.victronenergy.system,/Serial,s,,800,string[6],1,R -com.victronenergy.pvinverter,/Position,i,0=AC input 1;1=AC output;2=AC input 2,1026,uint16,1,R -com.victronenergy.pvinverter,/Serial,s,,1039,string[7],1,R -com.victronenergy.pvinverter,/Ac/L1/Voltage,d,V AC,1027,uint16,10,R -com.victronenergy.pvinverter,/Ac/L1/Current,d,A AC,1028,int16,10,R -com.victronenergy.pvinverter,/Ac/L1/Power,i,W,1029,uint16,1,R -com.victronenergy.pvinverter,/Ac/L1/Energy/Forward,u,kWh,1030,uint16,100,R -com.victronenergy.pvinverter,/Ac/L2/Voltage,d,V AC,1031,uint16,10,R -com.victronenergy.pvinverter,/Ac/L2/Current,d,A AC,1032,int16,10,R -com.victronenergy.pvinverter,/Ac/L2/Power,i,W,1033,uint16,1,R -com.victronenergy.pvinverter,/Ac/L2/Energy/Forward,u,kWh,1034,uint16,100,R -com.victronenergy.pvinverter,/Ac/L3/Voltage,d,V AC,1035,uint16,10,R -com.victronenergy.pvinverter,/Ac/L3/Current,d,A AC,1036,int16,10,R -com.victronenergy.pvinverter,/Ac/L3/Power,i,W,1037,uint16,1,R -com.victronenergy.pvinverter,/Ac/L3/Energy/Forward,u,kWh,1038,uint16,100,R -com.victronenergy.pvinverter,/Ac/L1/Energy/Forward,u,kWh,1046,uint32,100,R -com.victronenergy.pvinverter,/Ac/L2/Energy/Forward,u,kWh,1048,uint32,100,R -com.victronenergy.pvinverter,/Ac/L3/Energy/Forward,u,kWh,1050,uint32,100,R -com.victronenergy.pvinverter,/Ac/Power,i,kW,1052,int32,1,W -com.victronenergy.pvinverter,/Ac/MaxPower,u,kW,1054,uint32,1,W -com.victronenergy.pvinverter,/Ac/PowerLimit,u,kW,1056,uint32,1,W -com.victronenergy.motordrive,/Motor/RPM,d,RPM,2048,int16,1,R -com.victronenergy.motordrive,/Motor/Temperature,d,Degrees celsius,2049,int16,10,R -com.victronenergy.motordrive,/Dc/0/Voltage,d,V DC,2050,uint16,100,R -com.victronenergy.motordrive,/Dc/0/Current,d,A DC,2051,int16,10,R -com.victronenergy.motordrive,/Dc/0/Power,d,W,2052,int16,10,R -com.victronenergy.motordrive,/Controller/Temperature,d,Degrees celsius,2053,int16,10,R -com.victronenergy.charger,/Dc/0/Voltage,d,V DC,2307,uint16,100,R -com.victronenergy.charger,/Dc/0/Current,d,A DC,2308,int16,10,R -com.victronenergy.charger,/Dc/0/Temperature,d,Degrees celsius,2309,int16,10,R -com.victronenergy.charger,/Dc/1/Voltage,d,V DC,2310,uint16,100,R -com.victronenergy.charger,/Dc/1/Current,d,A DC,2311,int16,10,R -com.victronenergy.charger,/Dc/2/Voltage,d,V DC,2312,uint16,100,R -com.victronenergy.charger,/Dc/2/Current,d,A DC,2313,int16,10,R -com.victronenergy.charger,/Ac/In/L1/I,d,A AC,2314,int16,10,R -com.victronenergy.charger,/Ac/In/L1/P,d,W DC,2315,uint16,1,R -com.victronenergy.charger,/Ac/In/CurrentLimit,d,A AC,2316,int16,10,W -com.victronenergy.charger,/Mode,i,0=Off;1=On;2=Error;3=Unavailable- Unknown,2317,uint16,1,W -com.victronenergy.charger,/State,i,0=Off;1=Low Power Mode;2=Fault;3=Bulk;4=Absorption;5=Float;6=Storage;7=Equalize;8=Passthru;9=Inverting;10=Power assist;11=Power supply mode;252=Bulk protection,2318,uint16,1,R -com.victronenergy.charger,/ErrorCode,i,0=No error;1=Battery temperature too high;2=Battery voltage too high;3=Battery temperature sensor miswired (+);4=Battery temperature sensor miswired (-);5=Battery temperature sensor disconnected;6=Battery voltage sense miswired (+);7=Battery voltage sense miswired (-);8=Battery voltage sense disconnected;9=Battery voltage wire losses too high;17=Charger temperature too high;18=Charger over-current;19=Charger current polarity reversed;20=Bulk time limit reached;22=Charger temperature sensor miswired;23=Charger temperature sensor disconnected;34=Input current too high,2319,uint16,1,R -com.victronenergy.charger,/Relay/0/State,i,0=Open;1=Closed,2320,uint16,1,R -com.victronenergy.charger,/Alarms/LowVoltage,i,0=No alarm;2=Alarm,2321,uint16,1,R -com.victronenergy.charger,/Alarms/HighVoltage,i,0=No alarm;2=Alarm,2322,uint16,1,R -com.victronenergy.grid,/Ac/L1/Power,d,W,2600,int16,1,R -com.victronenergy.grid,/Ac/L2/Power,d,W,2601,int16,1,R -com.victronenergy.grid,/Ac/L3/Power,d,W,2602,int16,1,R -com.victronenergy.grid,/Ac/L1/Energy/Forward,d,kWh,2603,uint16,100,R -com.victronenergy.grid,/Ac/L2/Energy/Forward,d,kWh,2604,uint16,100,R -com.victronenergy.grid,/Ac/L3/Energy/Forward,d,kWh,2605,uint16,100,R -com.victronenergy.grid,/Ac/L1/Energy/Reverse,d,kWh,2606,uint16,100,R -com.victronenergy.grid,/Ac/L2/Energy/Reverse,d,kWh,2607,uint16,100,R -com.victronenergy.grid,/Ac/L3/Energy/Reverse,d,kWh,2608,uint16,100,R -com.victronenergy.grid,/Serial,s,,2609,string[7],,R -com.victronenergy.grid,/Ac/L1/Voltage,d,V AC,2616,uint16,10,R -com.victronenergy.grid,/Ac/L1/Current,d,A AC,2617,int16,10,R -com.victronenergy.grid,/Ac/L2/Voltage,d,V AC,2618,uint16,10,R -com.victronenergy.grid,/Ac/L2/Current,d,A AC,2619,int16,10,R -com.victronenergy.grid,/Ac/L3/Voltage,d,V AC,2620,uint16,10,R -com.victronenergy.grid,/Ac/L3/Current,d,A AC,2621,int16,10,R -com.victronenergy.grid,/Ac/L1/Energy/Forward,d,kWh,2622,uint32,100,R -com.victronenergy.grid,/Ac/L2/Energy/Forward,d,kWh,2624,uint32,100,R -com.victronenergy.grid,/Ac/L3/Energy/Forward,d,kWh,2626,uint32,100,R -com.victronenergy.grid,/Ac/L1/Energy/Reverse,d,kWh,2628,uint32,100,R -com.victronenergy.grid,/Ac/L2/Energy/Reverse,d,kWh,2630,uint32,100,R -com.victronenergy.grid,/Ac/L3/Energy/Reverse,d,kWh,2632,uint32,100,R -com.victronenergy.grid,/Ac/Energy/Forward,d,kWh,2634,uint32,100,R -com.victronenergy.grid,/Ac/Energy/Reverse,d,kWh,2636,uint32,100,R -com.victronenergy.settings,/Settings/CGwacs/AcPowerSetPoint,d,W,2700,int16,1,W -com.victronenergy.settings,/Settings/CGwacs/AcPowerSetPoint,d,W,2703,int16,0.01,W -com.victronenergy.settings,/Settings/CGwacs/MaxChargePercentage,d,%,2701,uint16,1,W -com.victronenergy.settings,/Settings/CGwacs/MaxDischargePercentage,d,%,2702,uint16,1,W -com.victronenergy.settings,/Settings/CGwacs/MaxDischargePower,d,W,2704,uint16,0.1,W -com.victronenergy.settings,/Settings/SystemSetup/MaxChargeCurrent,d,A,2705,int16,1,W -com.victronenergy.settings,/Settings/CGwacs/MaxFeedInPower,d,W,2706,int16,0.01,W -com.victronenergy.settings,/Settings/CGwacs/OvervoltageFeedIn,i,0=Don't feed excess DC-coupled PV into grid;1=Feed excess DC-coupled PV into the grid,2707,uint16,1,W -com.victronenergy.settings,/Settings/CGwacs/PreventFeedback,i,0=Feed excess AC-coupled PV into grid;1=Don't feed excess AC-coupled PV into the grid,2708,uint16,1,W -com.victronenergy.settings,/Settings/SystemSetup/MaxChargeVoltage,d,V,2710,uint16,10,W -com.victronenergy.hub4,/PvPowerLimiterActive,i,0=Feed-in limiting inactive;1=Feed-in limiting active,2709,uint16,1,R -com.victronenergy.tank,/ProductId,u,,3000,uint16,1,R -com.victronenergy.tank,/Capacity,d,m3,3001,uint32,10000,R -com.victronenergy.tank,/FluidType,u,0=Fuel;1=Fresh water;2=Waste water;3=Live well;4=Oil;5=Black water (sewage);6=Gasoline;7=Diesel;8=LPG;9=LNG;10=Hydraulic oil;11=Raw water,3003,uint16,1,R -com.victronenergy.tank,/Level,d,%,3004,uint16,10,R -com.victronenergy.tank,/Remaining,d,m3,3005,uint32,10000,R -com.victronenergy.tank,/Status,u,0=OK;1=Disconnected;2=Short circuited;3=Reverse Polarity;4=Unknown,3007,uint16,1,R -com.victronenergy.inverter,/ProductId,u,,3127,uint16,1,R -com.victronenergy.inverter,/FirmwareVersion,u,,3125,uint16,1,R -com.victronenergy.inverter,/Ac/Out/L1/I,d,A AC,3100,int16,10,R -com.victronenergy.inverter,/Ac/Out/L1/V,d,V AC,3101,uint16,10,R -com.victronenergy.inverter,/Ac/Out/L1/P,d,W AC,3102,int16,0.1,R -com.victronenergy.inverter,/Alarms/HighTemperature,u,0=No alarm;1=Warning;2=Alarm,3110,uint16,1,R -com.victronenergy.inverter,/Alarms/HighVoltage,u,0=No alarm;1=Warning;2=Alarm,3111,uint16,1,R -com.victronenergy.inverter,/Alarms/HighVoltageAcOut,u,0=No alarm;1=Warning;2=Alarm,3112,uint16,1,R -com.victronenergy.inverter,/Alarms/LowTemperature,u,0=No alarm;1=Warning;2=Alarm,3113,uint16,1,R -com.victronenergy.inverter,/Alarms/LowVoltage,u,0=No alarm;1=Warning;2=Alarm,3114,uint16,1,R -com.victronenergy.inverter,/Alarms/LowVoltageAcOut,u,0=No alarm;1=Warning;2=Alarm,3115,uint16,1,R -com.victronenergy.inverter,/Alarms/Overload,u,0=No alarm;1=Warning;2=Alarm,3116,uint16,1,R -com.victronenergy.inverter,/Alarms/Ripple,u,0=No alarm;1=Warning;2=Alarm,3117,uint16,1,R -com.victronenergy.inverter,/Dc/0/Voltage,d,V DC,3105,uint16,100,R -com.victronenergy.inverter,/Dc/0/Current,d,A DC,3106,int16,10,R -com.victronenergy.inverter,/Mode,u,2=On;4=Off;5=Eco,3126,uint16,1,W -com.victronenergy.inverter,/State,u,0=Off;1=Low power mode (search mode);2=Fault;9=Inverting (on),3128,uint16,1,R -com.victronenergy.inverter,/Energy/InverterToAcOut,d,kWh,3130,uint32,100,R -com.victronenergy.inverter,/Energy/OutToInverter,d,kWh,3132,uint32,100,R -com.victronenergy.inverter,/Energy/SolarToAcOut,d,kWh,3134,uint32,100,R -com.victronenergy.inverter,/Energy/SolarToBattery,d,kWh,3136,uint32,100,R -com.victronenergy.inverter,/Pv/V,d,V DC,3138,uint16,10,R -com.victronenergy.inverter,/Pv/0/V,d,V DC,3140,uint16,10,R -com.victronenergy.inverter,/Pv/1/V,d,V DC,3141,uint16,10,R -com.victronenergy.inverter,/Pv/2/V,d,V DC,3142,uint16,10,R -com.victronenergy.inverter,/Pv/3/V,d,V DC,3143,uint16,10,R -com.victronenergy.inverter,/History/Daily/0/Pv/0/Yield,d,kWh,3148,uint16,10,R -com.victronenergy.inverter,/History/Daily/0/Pv/1/Yield,d,kWh,3149,uint16,10,R -com.victronenergy.inverter,/History/Daily/0/Pv/2/Yield,d,kWh,3150,uint16,10,R -com.victronenergy.inverter,/History/Daily/0/Pv/3/Yield,d,kWh,3151,uint16,10,R -com.victronenergy.inverter,/History/Daily/1/Pv/0/Yield,d,kWh,3152,uint16,10,R -com.victronenergy.inverter,/History/Daily/1/Pv/1/Yield,d,kWh,3153,uint16,10,R -com.victronenergy.inverter,/History/Daily/1/Pv/2/Yield,d,kWh,3154,uint16,10,R -com.victronenergy.inverter,/History/Daily/1/Pv/3/Yield,d,kWh,3155,uint16,10,R -com.victronenergy.inverter,/History/Daily/0/Pv/0/MaxPower,d,W,3156,uint16,1,R -com.victronenergy.inverter,/History/Daily/0/Pv/1/MaxPower,d,W,3157,uint16,1,R -com.victronenergy.inverter,/History/Daily/0/Pv/2/MaxPower,d,W,3158,uint16,1,R -com.victronenergy.inverter,/History/Daily/0/Pv/3/MaxPower,d,W,3159,uint16,1,R -com.victronenergy.inverter,/History/Daily/1/Pv/0/MaxPower,d,W,3160,uint16,1,R -com.victronenergy.inverter,/History/Daily/1/Pv/1/MaxPower,d,W,3161,uint16,1,R -com.victronenergy.inverter,/History/Daily/1/Pv/2/MaxPower,d,W,3162,uint16,1,R -com.victronenergy.inverter,/History/Daily/1/Pv/3/MaxPower,d,W,3163,uint16,1,R -com.victronenergy.inverter,/Pv/0/P,d,W,3164,uint16,1,R -com.victronenergy.inverter,/Pv/1/P,d,W,3165,uint16,1,R -com.victronenergy.inverter,/Pv/2/P,d,W,3166,uint16,1,R -com.victronenergy.inverter,/Pv/3/P,d,W,3167,uint16,1,R -com.victronenergy.inverter,/Alarms/LowSoc,u,,3168,uint16,1,R -com.victronenergy.genset,/ProductId,u,,3212,uint16,1,R -com.victronenergy.genset,/StatusCode,u,0=Standby;1=Startup 1;2=Startup 2;3=Startup 3;4=Startup 4;5=Startup 5;6=Startup 6;7=Startup 7;8=Running;9=Stopping;10=Error,3213,uint16,1,R -com.victronenergy.genset,/ErrorCode,u,0=No error;1=AC voltage L1 too low;2=AC frequency L1 too low;3=AC current too low;4=AC power too low;5=Emergency stop;6=Servo current too low;7=Oil pressure too low;8=Engine temperature too low;9=Winding temperature too low;10=Exhaust temperature too low;13=Starter current too low;14=Glow current too low;15=Glow current too low;16=Fuel holding magnet current too low;17=Stop solenoid hold coil current too low;18=Stop solenoid pull coil current too low;19=Optional DC out current too low;20=5V output voltage too low;21=Boost output current too low;22=Panel supply current too high;25=Starter battery voltage too low;26=Startup aborted (rotation too low);28=Rotation too low;29=Power contactor current too low;30=AC voltage L2 too low;31=AC frequency L2 too low;32=AC current L2 too low;33=AC power L2 too low;34=AC voltage L3 too low;35=AC frequency L3 too low;36=AC current L3 too low;37=AC power L3 too low;62=Fuel temperature too low;63=Fuel level too low;65=AC voltage L1 too high;66=AC frequency too high;67=AC current too high;68=AC power too high;70=Servo current too high;71=Oil pressure too high;72=Engine temperature too high;73=Winding temperature too high;74=Exhaust temperature too low;77=Starter current too low;78=Glow current too high;79=Glow current too high;80=Fuel holding magnet current too high;81=Stop solenoid hold coil current too high;82=Stop solenoid pull coil current too high;83=Optional DC out current too high;84=5V output voltage too high;85=Boost output current too high;89=Starter battery voltage too high;90=Startup aborted (rotation too high);92=Rotation too high;93=Power contactor current too high;94=AC voltage L2 too high;95=AC frequency L2 too high;96=AC current L2 too high;97=AC power L2 too high;98=AC voltage L3 too high;99=AC frequency L3 too high;100=AC current L3 too high;101=AC power L3 too high;126=Fuel temperature too high;127=Fuel level too high;130=Lost control unit;131=Lost panel;132=Service needed;133=Lost 3-phase module;134=Lost AGT module;135=Synchronization failure;137=Intake airfilter;139=Lost sync. module;140=Load-balance failed;141=Sync-mode deactivated;142=Engine controller;148=Rotating field wrong;149=Fuel level sensor lost;150=Init failed;151=Watchdog;152=Out: winding;153=Out: exhaust;154=Out: Cyl. head;155=Inverter over temperature;156=Inverter overload;157=Inverter communication lost;158=Inverter sync failed;159=CAN communication lost;160=L1 overload;161=L2 overload;162=L3 overload;163=DC overload;164=DC overvoltage;165=Emergency stop;166=No connection,3214,uint16,1,R -com.victronenergy.genset,/AutoStart,u,0=Disabled;1=Enabled,3215,uint16,1,R -com.victronenergy.genset,/Start,u,0=Stop;1=Start,3223,uint16,1,W -com.victronenergy.genset,/Engine/Load,d,%,3216,uint16,1,R -com.victronenergy.genset,/Engine/Speed,d,RPM,3217,uint16,1,R -com.victronenergy.genset,/Engine/OperatingHours,d,s,3218,uint16,0.01,R -com.victronenergy.genset,/Engine/CoolantTemperature,d,Degrees celsius,3219,int16,10,R -com.victronenergy.genset,/Engine/WindingTemperature,d,Degrees celsius,3220,int16,10,R -com.victronenergy.genset,/Engine/ExaustTemperature,d,Degrees celsius,3221,int16,10,R -com.victronenergy.genset,/StarterVoltage,d,V DC,3222,uint16,100,R -com.victronenergy.genset,/Ac/L1/Voltage,d,V AC,3200,uint16,10,R -com.victronenergy.genset,/Ac/L1/Current,d,A AC,3203,int16,10,R -com.victronenergy.genset,/Ac/L1/Power,d,W,3206,int16,1,R -com.victronenergy.genset,/Ac/L1/Frequency,d,Hz,3209,uint16,100,R -com.victronenergy.genset,/Ac/L2/Voltage,d,V AC,3201,uint16,10,R -com.victronenergy.genset,/Ac/L2/Current,d,A AC,3204,int16,10,R -com.victronenergy.genset,/Ac/L2/Power,d,W,3207,int16,1,R -com.victronenergy.genset,/Ac/L2/Frequency,d,Hz,3210,uint16,100,R -com.victronenergy.genset,/Ac/L3/Voltage,d,V AC,3202,uint16,10,R -com.victronenergy.genset,/Ac/L3/Current,d,A AC,3205,int16,10,R -com.victronenergy.genset,/Ac/L3/Power,d,W,3208,int16,1,R -com.victronenergy.genset,/Ac/L3/Frequency,d,Hz,3211,uint16,100,R -com.victronenergy.temperature,/ProductId,u,,3300,uint16,1,R -com.victronenergy.temperature,/Scale,d,,3301,uint16,100,R -com.victronenergy.temperature,/Offset,d,,3302,int16,100,R -com.victronenergy.temperature,/TemperatureType,u,0=Battery;1=Fridge;2=Generic,3303,uint16,1,R -com.victronenergy.temperature,/Temperature,d,Degrees celsius,3304,int16,100,R -com.victronenergy.temperature,/Status,u,0=OK;1=Disconnected;2=Short circuited;3=Reverse Polarity;4=Unknown,3305,uint16,1,R -com.victronenergy.temperature,/Humidity,u,%,3306,uint16,10,R -com.victronenergy.temperature,/BatteryVoltage,d,V,3307,uint16,100,R -com.victronenergy.temperature,/Pressure,u,hPa,3308,uint16,1,R -com.victronenergy.pulsemeter,/Aggregate,i,,3400,uint32,1,R -com.victronenergy.pulsemeter,/Count,i,,3402,uint32,1,R -com.victronenergy.digitalinput,/Count,i,,3420,uint32,1,R -com.victronenergy.digitalinput,/State,i,,3422,uint16,1,R -com.victronenergy.digitalinput,/Alarm,i,0=No alarm;2=Alarm,3423,uint16,1,R -com.victronenergy.digitalinput,/Type,i,2=Door;3=Bilge pump;4=Bilge alarm;5=Burglar alarm;6=Smoke alarm;7=Fire alarm;8=CO2 alarm;9=Generator;10=ExtTransferSwitch,3424,uint16,1,R -com.victronenergy.generator,/ManualStart,i,0=Stop generator;1=Start generator,3500,uint16,1,W -com.victronenergy.generator,/RunningByConditionCode,i,0=Stopped;1=Manual;2=TestRun;3=LossOfComms;4=Soc;5=AcLoad;6=BatteryCurrent;7=BatteryVoltage;8=InverterTemperatur;9=InverterOverload;10=StopOnAc1,3501,uint16,1,R -com.victronenergy.generator,/Runtime,i,seconds,3502,uint16,1,R -com.victronenergy.generator,/QuietHours,i,0=Quiet hours inactive; 1=Quiet hours active,3503,uint16,1,R -com.victronenergy.generator,/Runtime,u,seconds,3504,uint32,1,R -com.victronenergy.generator,/State,i,0=Stopped;1=Running;10=Error,3506,uint16,1,R -com.victronenergy.generator,/Error,i,0=No Error;1=Remote disabled;2=Remote fault,3507,uint16,1,R -com.victronenergy.generator,/Alarms/NoGeneratorAtAcIn,i,0=No Alarm;2=Alarm,3508,uint16,1,R -com.victronenergy.generator,/AutoStartEnabled,i,0=Autostart disabled;1=Autostart enabled,3509,uint16,1,W -com.victronenergy.meteo,/Irradiance,d,W/m^2,3600,uint16,10,R -com.victronenergy.meteo,/WindSpeed,d,m/s,3601,uint16,10,R -com.victronenergy.meteo,/CellTemperature,d,Degrees celsius,3602,int16,10,R -com.victronenergy.meteo,/ExternalTemperature,d,Degrees celsius,3603,int16,10,R -com.victronenergy.evcharger,/ProductId,u,,3800,uint16,1,R -com.victronenergy.evcharger,/FirmwareVersion,u,,3802,uint32,1,R -com.victronenergy.evcharger,/Serial,s,,3804,string[6],,R -com.victronenergy.evcharger,/Model,s,,3810,string[4],,R -com.victronenergy.evcharger,/MaxCurrent,d,A,3814,uint16,1,W -com.victronenergy.evcharger,/Mode,u,0=Manual;1=Auto,3815,uint16,1,W -com.victronenergy.evcharger,/Ac/Energy/Forward,d,kWh,3816,uint32,100,R -com.victronenergy.evcharger,/Ac/L1/Power,d,W,3818,uint16,1,R -com.victronenergy.evcharger,/Ac/L2/Power,d,W,3819,uint16,1,R -com.victronenergy.evcharger,/Ac/L3/Power,d,W,3820,uint16,1,R -com.victronenergy.evcharger,/Ac/Power,d,W,3821,uint16,1,R -com.victronenergy.evcharger,/ChargingTime,d,s,3822,uint16,0.01,R -com.victronenergy.evcharger,/Current,d,A,3823,uint16,1,W -com.victronenergy.evcharger,/Status,u,0=Disconnected; 1=Connected; 2=Charging; 3=Charged; 4=Waiting for sun; 5=Waiting for RFID; 6=Waiting for start; 7=Low SOC; 8=Ground fault; 9=Welded contacts; 10=CP Input shorted; 11=Residual current detected; 12=Under voltage detected; 13=Overvoltage detected; 14=Overheating detected,3824,uint16,1,R -com.victronenergy.evcharger,/SetCurrent,d,A,3825,uint16,1,W -com.victronenergy.evcharger,/StartStop,u,0=Stop;1=Start,3826,uint16,1,W -com.victronenergy.evcharger,/Position,i,0=AC input 1;1=AC output;2=AC input 2,3827,uint16,1,W -com.victronenergy.acload,/Ac/L1/Power,d,W,3900,int16,1,R -com.victronenergy.acload,/Ac/L2/Power,d,W,3901,int16,1,R -com.victronenergy.acload,/Ac/L3/Power,d,W,3902,int16,1,R -com.victronenergy.acload,/Serial,s,,3903,string[7],,R -com.victronenergy.acload,/Ac/L1/Voltage,d,V AC,3910,uint16,10,R -com.victronenergy.acload,/Ac/L1/Current,d,A AC,3911,int16,10,R -com.victronenergy.acload,/Ac/L2/Voltage,d,V AC,3912,uint16,10,R -com.victronenergy.acload,/Ac/L2/Current,d,A AC,3913,int16,10,R -com.victronenergy.acload,/Ac/L3/Voltage,d,V AC,3914,uint16,10,R -com.victronenergy.acload,/Ac/L3/Current,d,A AC,3915,int16,10,R -com.victronenergy.acload,/Ac/L1/Energy/Forward,d,kWh,3916,uint32,100,R -com.victronenergy.acload,/Ac/L2/Energy/Forward,d,kWh,3918,uint32,100,R -com.victronenergy.acload,/Ac/L3/Energy/Forward,d,kWh,3920,uint32,100,R -com.victronenergy.fuelcell,/Dc/0/Voltage,d,V DC,4000,uint16,100,R -com.victronenergy.fuelcell,/Dc/0/Current,d,A DC,4001,int16,10,R -com.victronenergy.fuelcell,/Dc/1/Voltage,d,V DC,4002,int16,10,R -com.victronenergy.fuelcell,/Dc/0/Temperature,d,Degrees celsius,4003,int16,10,R -com.victronenergy.fuelcell,/History/EnergyOut,d,kWh,4004,uint32,100,R -com.victronenergy.fuelcell,/Alarms/LowVoltage,u,0=No alarm;2=Alarm,4006,uint16,1,R -com.victronenergy.fuelcell,/Alarms/HighVoltage,u,0=No alarm;2=Alarm,4007,uint16,1,R -com.victronenergy.fuelcell,/Alarms/LowStarterVoltage,u,0=No alarm;2=Alarm,4008,uint16,1,R -com.victronenergy.fuelcell,/Alarms/HighStarterVoltage,u,0=No alarm;2=Alarm,4009,uint16,1,R -com.victronenergy.fuelcell,/Alarms/LowTemperature,u,0=No alarm;2=Alarm,4010,uint16,1,R -com.victronenergy.fuelcell,/Alarms/HighTemperature,u,0=No alarm;2=Alarm,4011,uint16,1,R -com.victronenergy.alternator,/Dc/0/Voltage,d,V DC,4100,uint16,100,R -com.victronenergy.alternator,/Dc/0/Current,d,A DC,4101,int16,10,R -com.victronenergy.alternator,/Dc/1/Voltage,d,V DC,4102,int16,10,R -com.victronenergy.alternator,/Dc/0/Temperature,d,Degrees celsius,4103,int16,10,R -com.victronenergy.alternator,/History/EnergyOut,d,kWh,4104,uint32,100,R -com.victronenergy.alternator,/Alarms/LowVoltage,u,0=No alarm;2=Alarm,4106,uint16,1,R -com.victronenergy.alternator,/Alarms/HighVoltage,u,0=No alarm;2=Alarm,4107,uint16,1,R -com.victronenergy.alternator,/Alarms/LowStarterVoltage,u,0=No alarm;2=Alarm,4108,uint16,1,R -com.victronenergy.alternator,/Alarms/HighStarterVoltage,u,0=No alarm;2=Alarm,4109,uint16,1,R -com.victronenergy.alternator,/Alarms/LowTemperature,u,0=No alarm;2=Alarm,4110,uint16,1,R -com.victronenergy.alternator,/Alarms/HighTemperature,u,0=No alarm;2=Alarm,4111,uint16,1,R -com.victronenergy.alternator,/State,u,0=Off;2=Fault;3=Bulk;4=Absorption;5=Float;6=Storage;7=Equalize;252=External control,4112,uint16,1,R -com.victronenergy.alternator,/ErrorCode,u,12=High battery temperature;13=High battery voltage;14=Low battery voltage;15=VBat exceeds $CPB;21=High alternator temperature;22=Alternator overspeed;24=Internal error;41=High field FET temperature;42=Sensor missing;43=Low VAlt;44=High Voltage offset;45=VAlt exceeds $CPB;51-52=Battery disconnect request;53=Battery instance out of range;54=Too many BMSes;55=AEBus fault;56=Too many Victron devices;58-61=Battery requested disconnection;91=BMS lost;92=Forced idle;201=DCDC converter fail;201-207=DCDC error,4113,uint16,1,R -com.victronenergy.alternator,/Engine/Speed,d,RPM,4114,uint16,1,R -com.victronenergy.alternator,/Speed,d,RPM,4115,uint16,1,R -com.victronenergy.alternator,/FieldDrive,q,%,4116,uint16,1,R -com.victronenergy.dcsource,/Dc/0/Voltage,d,V DC,4200,uint16,100,R -com.victronenergy.dcsource,/Dc/0/Current,d,A DC,4201,int16,10,R -com.victronenergy.dcsource,/Dc/1/Voltage,d,V DC,4202,int16,10,R -com.victronenergy.dcsource,/Dc/0/Temperature,d,Degrees centigrade,4203,int16,10,R -com.victronenergy.dcsource,/History/EnergyOut,d,kWh,4204,uint32,100,R -com.victronenergy.dcsource,/Alarms/LowVoltage,u,0=No alarm;2=Alarm,4206,uint16,1,R -com.victronenergy.dcsource,/Alarms/HighVoltage,u,0=No alarm;2=Alarm,4207,uint16,1,R -com.victronenergy.dcsource,/Alarms/LowStarterVoltage,u,0=No alarm;2=Alarm,4208,uint16,1,R -com.victronenergy.dcsource,/Alarms/HighStarterVoltage,u,0=No alarm;2=Alarm,4209,uint16,1,R -com.victronenergy.dcsource,/Alarms/LowTemperature,u,0=No alarm;2=Alarm,4210,uint16,1,R -com.victronenergy.dcsource,/Alarms/HighTemperature,u,0=No alarm;2=Alarm,4211,uint16,1,R -com.victronenergy.dcload,/Dc/0/Voltage,d,V DC,4300,uint16,100,R -com.victronenergy.dcload,/Dc/0/Current,d,A DC,4301,int16,10,R -com.victronenergy.dcload,/Dc/1/Voltage,d,V DC,4302,int16,10,R -com.victronenergy.dcload,/Dc/0/Temperature,d,Degrees centigrade,4303,int16,10,R -com.victronenergy.dcload,/History/EnergyIn,d,kWh,4304,uint32,100,R -com.victronenergy.dcload,/Alarms/LowVoltage,u,0=No alarm;2=Alarm,4306,uint16,1,R -com.victronenergy.dcload,/Alarms/HighVoltage,u,0=No alarm;2=Alarm,4307,uint16,1,R -com.victronenergy.dcload,/Alarms/LowStarterVoltage,u,0=No alarm;2=Alarm,4308,uint16,1,R -com.victronenergy.dcload,/Alarms/HighStarterVoltage,u,0=No alarm;2=Alarm,4309,uint16,1,R -com.victronenergy.dcload,/Alarms/LowTemperature,u,0=No alarm;2=Alarm,4310,uint16,1,R -com.victronenergy.dcload,/Alarms/HighTemperature,u,0=No alarm;2=Alarm,4311,uint16,1,R -com.victronenergy.dcsystem,/Dc/0/Voltage,d,V DC,4400,uint16,100,R -com.victronenergy.dcsystem,/Dc/0/Current,d,A DC,4401,int16,10,R -com.victronenergy.dcsystem,/Dc/1/Voltage,d,V DC,4402,int16,10,R -com.victronenergy.dcsystem,/Dc/0/Temperature,d,Degrees centigrade,4403,int16,10,R -com.victronenergy.dcsystem,/History/EnergyOut,d,kWh,4404,uint32,100,R -com.victronenergy.dcsystem,/History/EnergyIn,d,kWh,4406,uint32,100,R -com.victronenergy.dcsystem,/Alarms/LowVoltage,u,0=No alarm;2=Alarm,4408,uint16,1,R -com.victronenergy.dcsystem,/Alarms/HighVoltage,u,0=No alarm;2=Alarm,4409,uint16,1,R -com.victronenergy.dcsystem,/Alarms/LowStarterVoltage,u,0=No alarm;2=Alarm,4410,uint16,1,R -com.victronenergy.dcsystem,/Alarms/HighStarterVoltage,u,0=No alarm;2=Alarm,4411,uint16,1,R -com.victronenergy.dcsystem,/Alarms/LowTemperature,u,0=No alarm;2=Alarm,4412,uint16,1,R -com.victronenergy.dcsystem,/Alarms/HighTemperature,u,0=No alarm;2=Alarm,4413,uint16,1,R -com.victronenergy.multi,/Ac/In/1/L1/V,d,V AC,4500,uint16,10,R -com.victronenergy.multi,/Ac/In/1/L2/V,d,V AC,4501,uint16,10,R -com.victronenergy.multi,/Ac/In/1/L3/V,d,V AC,4502,uint16,10,R -com.victronenergy.multi,/Ac/In/1/L1/I,d,A AC,4503,uint16,10,R -com.victronenergy.multi,/Ac/In/1/L2/I,d,A AC,4504,uint16,10,R -com.victronenergy.multi,/Ac/In/1/L3/I,d,A AC,4505,uint16,10,R -com.victronenergy.multi,/Ac/In/1/L1/P,d,W,4506,int16,0.1,R -com.victronenergy.multi,/Ac/In/1/L2/P,d,W,4507,int16,0.1,R -com.victronenergy.multi,/Ac/In/1/L3/P,d,W,4508,int16,0.1,R -com.victronenergy.multi,/Ac/In/1/L1/F,d,Hz,4509,uint16,100,R -com.victronenergy.multi,/Ac/Out/L1/V,d,V AC,4510,uint16,10,R -com.victronenergy.multi,/Ac/Out/L2/V,d,V AC,4511,uint16,10,R -com.victronenergy.multi,/Ac/Out/L3/V,d,V AC,4512,uint16,10,R -com.victronenergy.multi,/Ac/Out/L1/I,d,A AC,4513,uint16,10,R -com.victronenergy.multi,/Ac/Out/L2/I,d,A AC,4514,uint16,10,R -com.victronenergy.multi,/Ac/Out/L3/I,d,A AC,4515,uint16,10,R -com.victronenergy.multi,/Ac/Out/L1/P,d,W,4516,int16,0.1,R -com.victronenergy.multi,/Ac/Out/L2/P,d,W,4517,int16,0.1,R -com.victronenergy.multi,/Ac/Out/L3/P,d,W,4518,int16,0.1,R -com.victronenergy.multi,/Ac/Out/L1/F,d,Hz,4519,uint16,100,R -com.victronenergy.multi,/Ac/In/1/Type,u,0=Unused;1=Grid;2=Genset;3=Shore,4520,uint16,1,R -com.victronenergy.multi,/Ac/In/2/Type,u,0=Unused;1=Grid;2=Genset;3=Shore,4521,uint16,1,R -com.victronenergy.multi,/Ac/In/1/CurrentLimit,d,A,4522,uint16,10,W -com.victronenergy.multi,/Ac/In/2/CurrentLimit,d,A,4523,uint16,10,W -com.victronenergy.multi,/Ac/NumberOfPhases,u,count,4524,uint16,1,R -com.victronenergy.multi,/Ac/ActiveIn/ActiveInput,u,0=AC Input 1;1=AC Input 2;240=Disconnected,4525,uint16,1,R -com.victronenergy.multi,/Dc/0/Voltage,d,V DC,4526,uint16,100,R -com.victronenergy.multi,/Dc/0/Current,d,A DC,4527,int16,10,R -com.victronenergy.multi,/Dc/0/Temperature,d,Degrees celsius,4528,int16,10,R -com.victronenergy.multi,/Soc,d,%,4529,uint16,10,R -com.victronenergy.multi,/State,u,0=Off;1=Low Power;2=Fault;3=Bulk;4=Absorption;5=Float;6=Storage;7=Equalize;8=Passthru;9=Inverting;10=Power assist;11=Power supply;252=Bulk protection,4530,uint16,1,R -com.victronenergy.multi,/Mode,u,1=Charger Only;2=Inverter Only;3=On;4=Off,4531,uint16,1,W -com.victronenergy.multi,/Alarms/HighTemperature,u,0=Ok;1=Warning;2=Alarm,4532,uint16,1,R -com.victronenergy.multi,/Alarms/HighVoltage,u,0=Ok;1=Warning;2=Alarm,4533,uint16,1,R -com.victronenergy.multi,/Alarms/HighVoltageAcOut,u,0=Ok;1=Warning;2=Alarm,4534,uint16,1,R -com.victronenergy.multi,/Alarms/LowTemperature,u,0=Ok;1=Warning;2=Alarm,4535,uint16,1,R -com.victronenergy.multi,/Alarms/LowVoltage,u,0=Ok;1=Warning;2=Alarm,4536,uint16,1,R -com.victronenergy.multi,/Alarms/LowVoltageAcOut,u,0=Ok;1=Warning;2=Alarm,4537,uint16,1,R -com.victronenergy.multi,/Alarms/Overload,u,0=Ok;1=Warning;2=Alarm,4538,uint16,1,R -com.victronenergy.multi,/Alarms/Ripple,u,0=Ok;1=Warning;2=Alarm,4539,uint16,1,R -com.victronenergy.multi,/Yield/Power,d,W,4540,uint16,1,R -com.victronenergy.multi,/Yield/User,d,kWh,4541,uint16,10,R -com.victronenergy.multi,/Relay/0/State,i,0=Open;1=Closed,4542,uint16,1,R -com.victronenergy.multi,/MppOperationMode,i,0=Off;1=Voltage/current limited;2=MPPT active;255=Not available,4543,uint16,1,R -com.victronenergy.multi,/Pv/V,d,V DC,4544,uint16,10,R -com.victronenergy.multi,/ErrorCode,i,0=No error;1=Battery temperature too high;2=Battery voltage too high;3=Battery temperature sensor miswired (+);4=Battery temperature sensor miswired (-);5=Battery temperature sensor disconnected;6=Battery voltage sense miswired (+);7=Battery voltage sense miswired (-);8=Battery voltage sense disconnected;9=Battery voltage wire losses too high;17=Charger temperature too high;18=Charger over-current;19=Charger current polarity reversed;20=Bulk time limit reached;22=Charger temperature sensor miswired;23=Charger temperature sensor disconnected;34=Input current too high,4545,uint16,1,R -com.victronenergy.multi,/Energy/AcIn1ToAcOut,d,kWh,4546,uint32,100,R -com.victronenergy.multi,/Energy/AcIn1ToInverter,d,kWh,4548,uint32,100,R -com.victronenergy.multi,/Energy/AcIn2ToAcOut,d,kWh,4550,uint32,100,R -com.victronenergy.multi,/Energy/AcIn2ToInverter,d,kWh,4552,uint32,100,R -com.victronenergy.multi,/Energy/AcOutToAcIn1,d,kWh,4554,uint32,100,R -com.victronenergy.multi,/Energy/AcOutToAcIn2,d,kWh,4556,uint32,100,R -com.victronenergy.multi,/Energy/InverterToAcIn1,d,kWh,4558,uint32,100,R -com.victronenergy.multi,/Energy/InverterToAcIn2,d,kWh,4560,uint32,100,R -com.victronenergy.multi,/Energy/InverterToAcOut,d,kWh,4562,uint32,100,R -com.victronenergy.multi,/Energy/OutToInverter,d,kWh,4564,uint32,100,R -com.victronenergy.multi,/Energy/SolarToAcIn1,d,kWh,4566,uint32,100,R -com.victronenergy.multi,/Energy/SolarToAcIn2,d,kWh,4568,uint32,100,R -com.victronenergy.multi,/Energy/SolarToAcOut,d,kWh,4570,uint32,100,R -com.victronenergy.multi,/Energy/SolarToBattery,d,kWh,4572,uint32,100,R -com.victronenergy.multi,/History/Daily/0/Yield,d,kWh,4574,uint16,10,R -com.victronenergy.multi,/History/Daily/0/MaxPower,d,W,4575,uint16,1,R -com.victronenergy.multi,/History/Daily/1/Yield,d,kWh,4576,uint16,10,R -com.victronenergy.multi,/History/Daily/1/MaxPower,d,W,4577,uint16,1,R -com.victronenergy.multi,/History/Daily/0/Pv/0/Yield,d,kWh,4578,uint16,10,R -com.victronenergy.multi,/History/Daily/0/Pv/1/Yield,d,kWh,4579,uint16,10,R -com.victronenergy.multi,/History/Daily/0/Pv/2/Yield,d,kWh,4580,uint16,10,R -com.victronenergy.multi,/History/Daily/0/Pv/3/Yield,d,kWh,4581,uint16,10,R -com.victronenergy.multi,/History/Daily/1/Pv/0/Yield,d,kWh,4582,uint16,10,R -com.victronenergy.multi,/History/Daily/1/Pv/1/Yield,d,kWh,4583,uint16,10,R -com.victronenergy.multi,/History/Daily/1/Pv/2/Yield,d,kWh,4584,uint16,10,R -com.victronenergy.multi,/History/Daily/1/Pv/3/Yield,d,kWh,4585,uint16,10,R -com.victronenergy.multi,/History/Daily/0/Pv/0/MaxPower,d,W,4586,uint16,1,R -com.victronenergy.multi,/History/Daily/0/Pv/1/MaxPower,d,W,4587,uint16,1,R -com.victronenergy.multi,/History/Daily/0/Pv/2/MaxPower,d,W,4588,uint16,1,R -com.victronenergy.multi,/History/Daily/0/Pv/3/MaxPower,d,W,4589,uint16,1,R -com.victronenergy.multi,/History/Daily/1/Pv/0/MaxPower,d,W,4590,uint16,1,R -com.victronenergy.multi,/History/Daily/1/Pv/1/MaxPower,d,W,4591,uint16,1,R -com.victronenergy.multi,/History/Daily/1/Pv/2/MaxPower,d,W,4592,uint16,1,R -com.victronenergy.multi,/History/Daily/1/Pv/3/MaxPower,d,W,4593,uint16,1,R -com.victronenergy.multi,/Pv/0/V,d,V DC,4594,uint16,10,R -com.victronenergy.multi,/Pv/1/V,d,V DC,4595,uint16,10,R -com.victronenergy.multi,/Pv/2/V,d,V DC,4596,uint16,10,R -com.victronenergy.multi,/Pv/3/V,d,V DC,4597,uint16,10,R -com.victronenergy.multi,/Pv/0/P,d,W,4598,uint16,1,R -com.victronenergy.multi,/Pv/1/P,d,W,4599,uint16,1,R -com.victronenergy.multi,/Pv/2/P,d,W,4600,uint16,1,R -com.victronenergy.multi,/Pv/3/P,d,W,4601,uint16,1,R -com.victronenergy.multi,/Alarms/LowSoc,u,,4602,uint16,1,R diff --git a/FileSets/PatchSource/obsoletePatches/attributes.csv-v2.94.orig b/FileSets/PatchSource/obsoletePatches/attributes.csv-v2.94.orig deleted file mode 100644 index b5f1097c..00000000 --- a/FileSets/PatchSource/obsoletePatches/attributes.csv-v2.94.orig +++ /dev/null @@ -1,626 +0,0 @@ -com.victronenergy.gps,/Position/Latitude,d,Decimal degrees,2800,int32,10000000,R -com.victronenergy.gps,/Position/Longitude,d,Decimal degrees,2802,int32,10000000,R -com.victronenergy.gps,/Course,d,Degrees,2804,uint16,100,R -com.victronenergy.gps,/Speed,d,m/s,2805,uint16,100,R -com.victronenergy.gps,/Fix,y,,2806,uint16,1,R -com.victronenergy.gps,/NrOfSatellites,y,,2807,uint16,1,R -com.victronenergy.gps,/Altitude,d,m,2808,int32,10,R -com.victronenergy.settings,/Settings/CGwacs/BatteryLife/State,i,0=BL disabled;1=Restarting;2=Self-consumption;3=Self-consumption;4=Self-consumption;5=Discharge disabled;6=Force charge;7=Sustain;9=Keep batteries charged;10=BL Disabled;11=BL Disabled (Low SoC),2900,uint16,1,W -com.victronenergy.settings,/Settings/CGwacs/BatteryLife/MinimumSocLimit,d,%,2901,uint16,10,W -com.victronenergy.settings,/Settings/CGwacs/Hub4Mode,i,1=ESS with Phase Compensation;2=ESS without phase compensation;3=Disabled/External Control,2902,uint16,1,W -com.victronenergy.settings,/Settings/CGwacs/BatteryLife/SocLimit,d,%,2903,uint16,10,R -com.victronenergy.vebus,/Ac/ActiveIn/L1/V,d,V AC,3,uint16,10,R -com.victronenergy.vebus,/Ac/ActiveIn/L2/V,d,V AC,4,uint16,10,R -com.victronenergy.vebus,/Ac/ActiveIn/L3/V,d,V AC,5,uint16,10,R -com.victronenergy.vebus,/Ac/ActiveIn/L1/I,d,A AC,6,int16,10,R -com.victronenergy.vebus,/Ac/ActiveIn/L2/I,d,A AC,7,int16,10,R -com.victronenergy.vebus,/Ac/ActiveIn/L3/I,d,A AC,8,int16,10,R -com.victronenergy.vebus,/Ac/ActiveIn/L1/F,d,Hz,9,int16,100,R -com.victronenergy.vebus,/Ac/ActiveIn/L2/F,d,Hz,10,int16,100,R -com.victronenergy.vebus,/Ac/ActiveIn/L3/F,d,Hz,11,int16,100,R -com.victronenergy.vebus,/Ac/ActiveIn/L1/P,i,VA or Watts,12,int16,0.1,R -com.victronenergy.vebus,/Ac/ActiveIn/L2/P,i,VA or Watts,13,int16,0.1,R -com.victronenergy.vebus,/Ac/ActiveIn/L3/P,i,VA or Watts,14,int16,0.1,R -com.victronenergy.vebus,/Ac/Out/L1/V,d,V AC,15,uint16,10,R -com.victronenergy.vebus,/Ac/Out/L2/V,d,V AC,16,uint16,10,R -com.victronenergy.vebus,/Ac/Out/L3/V,d,V AC,17,uint16,10,R -com.victronenergy.vebus,/Ac/Out/L1/I,d,A AC,18,int16,10,R -com.victronenergy.vebus,/Ac/Out/L2/I,d,A AC,19,int16,10,R -com.victronenergy.vebus,/Ac/Out/L3/I,d,A AC,20,int16,10,R -com.victronenergy.vebus,/Ac/Out/L1/F,d,Hz,21,int16,100,R -com.victronenergy.vebus,/Ac/ActiveIn/CurrentLimit,d,A,22,int16,10,W -com.victronenergy.vebus,/Ac/Out/L1/P,i,VA or Watts,23,int16,0.1,R -com.victronenergy.vebus,/Ac/Out/L2/P,i,VA or Watts,24,int16,0.1,R -com.victronenergy.vebus,/Ac/Out/L3/P,i,VA or Watts,25,int16,0.1,R -com.victronenergy.vebus,/Dc/0/Voltage,d,V DC,26,uint16,100,R -com.victronenergy.vebus,/Dc/0/Current,d,A DC,27,int16,10,R -com.victronenergy.vebus,/Ac/NumberOfPhases,u,count,28,uint16,1,R -com.victronenergy.vebus,/Ac/ActiveIn/ActiveInput,u,0=AC Input 1;1=AC Input 2;240=Disconnected,29,uint16,1,R -com.victronenergy.vebus,/Soc,d,%,30,uint16,10,W -com.victronenergy.vebus,/State,u,0=Off;1=Low Power;2=Fault;3=Bulk;4=Absorption;5=Float;6=Storage;7=Equalize;8=Passthru;9=Inverting;10=Power assist;11=Power supply;252=Bulk protection,31,uint16,1,R -com.victronenergy.vebus,/VebusError,u,0=No error;1=VE.Bus Error 1: Device is switched off because one of the other phases in the system has switched off;2=VE.Bus Error 2: New and old types MK2 are mixed in the system;3=VE.Bus Error 3: Not all- or more than- the expected devices were found in the system;4=VE.Bus Error 4: No other device whatsoever detected;5=VE.Bus Error 5: Overvoltage on AC-out;6=VE.Bus Error 6: Error in DDC Program;7=VE.Bus BMS connected- which requires an Assistant- but no assistant found;10=VE.Bus Error 10: System time synchronisation problem occurred;14=VE.Bus Error 14: Device cannot transmit data;16=VE.Bus Error 16: Dongle missing;17=VE.Bus Error 17: One of the devices assumed master status because the original master failed;18=VE.Bus Error 18: AC Overvoltage on the output of a slave has occurred while already switched off;22=VE.Bus Error 22: This device cannot function as slave;24=VE.Bus Error 24: Switch-over system protection initiated;25=VE.Bus Error 25: Firmware incompatibility. The firmware of one of the connected device is not sufficiently up to date to operate in conjunction with this device;26=VE.Bus Error 26: Internal error,32,uint16,1,R -com.victronenergy.vebus,/Mode,u,1=Charger Only;2=Inverter Only;3=On;4=Off,33,uint16,1,W -com.victronenergy.vebus,/Alarms/HighTemperature,u,0=Ok;1=Warning;2=Alarm,34,uint16,1,R -com.victronenergy.vebus,/Alarms/LowBattery,u,0=Ok;1=Warning;2=Alarm,35,uint16,1,R -com.victronenergy.vebus,/Alarms/Overload,u,0=Ok;1=Warning;2=Alarm,36,uint16,1,R -com.victronenergy.vebus,/Alarms/TemperatureSensor,u,0=Ok;1=Warning;2=Alarm,42,uint16,1,R -com.victronenergy.vebus,/Alarms/VoltageSensor,u,0=Ok;1=Warning;2=Alarm,43,uint16,1,R -com.victronenergy.vebus,/Alarms/L1/HighTemperature,u,0=Ok;1=Warning;2=Alarm,44,uint16,1,R -com.victronenergy.vebus,/Alarms/L1/LowBattery,u,0=Ok;1=Warning;2=Alarm,45,uint16,1,R -com.victronenergy.vebus,/Alarms/L1/Overload,u,0=Ok;1=Warning;2=Alarm,46,uint16,1,R -com.victronenergy.vebus,/Alarms/L1/Ripple,u,0=Ok;1=Warning;2=Alarm,47,uint16,1,R -com.victronenergy.vebus,/Alarms/L2/HighTemperature,u,0=Ok;1=Warning;2=Alarm,48,uint16,1,R -com.victronenergy.vebus,/Alarms/L2/LowBattery,u,0=Ok;1=Warning;2=Alarm,49,uint16,1,R -com.victronenergy.vebus,/Alarms/L2/Overload,u,0=Ok;1=Warning;2=Alarm,50,uint16,1,R -com.victronenergy.vebus,/Alarms/L2/Ripple,u,0=Ok;1=Warning;2=Alarm,51,uint16,1,R -com.victronenergy.vebus,/Alarms/L3/HighTemperature,u,0=Ok;1=Warning;2=Alarm,52,uint16,1,R -com.victronenergy.vebus,/Alarms/L3/LowBattery,u,0=Ok;1=Warning;2=Alarm,53,uint16,1,R -com.victronenergy.vebus,/Alarms/L3/Overload,u,0=Ok;1=Warning;2=Alarm,54,uint16,1,R -com.victronenergy.vebus,/Alarms/L3/Ripple,u,0=Ok;1=Warning;2=Alarm,55,uint16,1,R -com.victronenergy.vebus,/Hub4/L1/AcPowerSetpoint,d,W,37,int16,1,W -com.victronenergy.vebus,/Hub4/DisableCharge,u,0=Charge allowed;1=Charge disabled,38,uint16,1,W -com.victronenergy.vebus,/Hub4/DisableFeedIn,u,0=Feed in allowed;1=Feed in disabled,39,uint16,1,W -com.victronenergy.vebus,/Hub4/L2/AcPowerSetpoint,d,W,40,int16,1,W -com.victronenergy.vebus,/Hub4/L3/AcPowerSetpoint,d,W,41,int16,1,W -com.victronenergy.vebus,/PvInverter/Disable,u,0=PV enabled;1=PV disabled,56,uint16,1,W -com.victronenergy.vebus,/Bms/AllowToCharge,u,0=No;1=Yes,57,uint16,1,R -com.victronenergy.vebus,/Bms/AllowToDischarge,u,0=No;1=Yes,58,uint16,1,R -com.victronenergy.vebus,/Bms/BmsExpected,u,0=No;1=Yes,59,uint16,1,R -com.victronenergy.vebus,/Bms/Error,u,0=No;1=Yes,60,uint16,1,R -com.victronenergy.vebus,/Dc/0/Temperature,d,Degrees celsius,61,int16,10,R -com.victronenergy.vebus,/SystemReset,y,0=No action;1=VE.Bus Reset,62,uint16,1,W -com.victronenergy.vebus,/Alarms/PhaseRotation,u,0=Ok;1=Warning,63,uint16,1,R -com.victronenergy.vebus,/Alarms/GridLost,i,0=No alarm;2=Alarm,64,uint16,1,R -com.victronenergy.vebus,/Hub4/DoNotFeedInOvervoltage,i,0=Feed in overvoltage;1=Don't feed in overvoltage,65,uint16,1,W -com.victronenergy.vebus,/Hub4/L1/MaxFeedInPower,d,W,66,uint16,0.01,W -com.victronenergy.vebus,/Hub4/L2/MaxFeedInPower,d,W,67,uint16,0.01,W -com.victronenergy.vebus,/Hub4/L3/MaxFeedInPower,d,W,68,uint16,0.01,W -com.victronenergy.vebus,/Ac/State/IgnoreAcIn1,i,0=AC input not ignored; 1=AC input ignored,69,uint16,1,R -com.victronenergy.vebus,/Ac/State/IgnoreAcIn2,i,0=AC input not ignored; 1=AC input ignored,70,uint16,1,R -com.victronenergy.vebus,/Hub4/TargetPowerIsMaxFeedIn,i,0=AcPowerSetpoint interpreted normally; 1=AcPowerSetpoint as OvervoltageFeedIn limit,71,uint16,,W -com.victronenergy.vebus,/Hub4/FixSolarOffsetTo100mV,i,0=OvervoltageFeedIn uses 1V offset; 1=OvervoltageFeedIn uses 0.1V offset,72,uint16,,W -com.victronenergy.vebus,/Hub4/Sustain,i,0=Sustain inactive; 1=Sustain active,73,uint16,1,R -com.victronenergy.vebus,/Energy/AcIn1ToAcOut,d,kWh,74,uint32,100,R -com.victronenergy.vebus,/Energy/AcIn1ToInverter,d,kWh,76,uint32,100,R -com.victronenergy.vebus,/Energy/AcIn2ToAcOut,d,kWh,78,uint32,100,R -com.victronenergy.vebus,/Energy/AcIn2ToInverter,d,kWh,80,uint32,100,R -com.victronenergy.vebus,/Energy/AcOutToAcIn1,d,kWh,82,uint32,100,R -com.victronenergy.vebus,/Energy/AcOutToAcIn2,d,kWh,84,uint32,100,R -com.victronenergy.vebus,/Energy/InverterToAcIn1,d,kWh,86,uint32,100,R -com.victronenergy.vebus,/Energy/InverterToAcIn2,d,kWh,88,uint32,100,R -com.victronenergy.vebus,/Energy/InverterToAcOut,d,kWh,90,uint32,100,R -com.victronenergy.vebus,/Energy/OutToInverter,d,kWh,92,uint32,100,R -com.victronenergy.battery,/Dc/0/Voltage,d,V DC,259,uint16,100,R -com.victronenergy.battery,/Dc/1/Voltage,d,V DC,260,uint16,100,R -com.victronenergy.battery,/Dc/0/Current,d,A DC,261,int16,10,R -com.victronenergy.battery,/Dc/0/Temperature,d,Degrees celsius,262,int16,10,R -com.victronenergy.battery,/Dc/0/MidVoltage,d,V DC,263,uint16,100,R -com.victronenergy.battery,/Dc/0/MidVoltageDeviation,d,%,264,uint16,100,R -com.victronenergy.battery,/ConsumedAmphours,d,Ah,265,uint16,-10,R -com.victronenergy.battery,/Soc,y,%,266,uint16,10,R -com.victronenergy.battery,/Soh,y,%,304,uint16,10,R -com.victronenergy.battery,/TimeToGo,q,seconds,303,uint16,0.01,R -com.victronenergy.battery,/Info/MaxChargeVoltage,u,V DC,305,uint16,10,R -com.victronenergy.battery,/Info/BatteryLowVoltage,u,V DC,306,uint16,10,R -com.victronenergy.battery,/Info/MaxChargeCurrent,u,A DC,307,uint16,10,R -com.victronenergy.battery,/Info/MaxDischargeCurrent,u,A DC,308,uint16,10,R -com.victronenergy.battery,/Alarms/Alarm,y,0=No alarm;2=Alarm,267,uint16,1,R -com.victronenergy.battery,/Alarms/LowVoltage,u,0=No alarm;2=Alarm,268,uint16,1,R -com.victronenergy.battery,/Alarms/HighVoltage,u,0=No alarm;2=Alarm,269,uint16,1,R -com.victronenergy.battery,/Alarms/LowStarterVoltage,u,0=No alarm;2=Alarm,270,uint16,1,R -com.victronenergy.battery,/Alarms/HighStarterVoltage,u,0=No alarm;2=Alarm,271,uint16,1,R -com.victronenergy.battery,/Alarms/LowSoc,u,0=No alarm;2=Alarm,272,uint16,1,R -com.victronenergy.battery,/Alarms/LowTemperature,u,0=No alarm;2=Alarm,273,uint16,1,R -com.victronenergy.battery,/Alarms/HighTemperature,u,0=No alarm;2=Alarm,274,uint16,1,R -com.victronenergy.battery,/Alarms/MidVoltage,u,0=No alarm;2=Alarm,275,uint16,1,R -com.victronenergy.battery,/Alarms/LowFusedVoltage,u,0=No alarm;2=Alarm,276,uint16,1,R -com.victronenergy.battery,/Alarms/HighFusedVoltage,u,0=No alarm;2=Alarm,277,uint16,1,R -com.victronenergy.battery,/Alarms/FuseBlown,u,0=No alarm;2=Alarm,278,uint16,1,R -com.victronenergy.battery,/Alarms/HighInternalTemperature,u,0=No alarm;2=Alarm,279,uint16,1,R -com.victronenergy.battery,/Alarms/HighChargeCurrent,u,0=No alarm;2=Alarm,320,uint16,1,R -com.victronenergy.battery,/Alarms/HighDischargeCurrent,u,0=No alarm;2=Alarm,321,uint16,1,R -com.victronenergy.battery,/Alarms/CellImbalance,u,0=No alarm;2=Alarm,322,uint16,1,R -com.victronenergy.battery,/Alarms/InternalFailure,u,0=No alarm;2=Alarm,323,uint16,1,R -com.victronenergy.battery,/Alarms/HighChargeTemperature,u,0=No alarm;2=Alarm,324,uint16,1,R -com.victronenergy.battery,/Alarms/LowChargeTemperature,u,0=No alarm;2=Alarm,325,uint16,1,R -com.victronenergy.battery,/Alarms/LowCellVoltage,u,0=No alarm;1=Almost discharged;2=Alarm,326,uint16,1,R -com.victronenergy.battery,/Relay/0/State,y,0=Open;1=Closed,280,uint16,1,W -com.victronenergy.battery,/History/DeepestDischarge,d,Ah,281,uint16,-10,R -com.victronenergy.battery,/History/LastDischarge,d,Ah,282,uint16,-10,R -com.victronenergy.battery,/History/AverageDischarge,d,Ah,283,uint16,-10,R -com.victronenergy.battery,/History/ChargeCycles,i,count,284,uint16,1,R -com.victronenergy.battery,/History/FullDischarges,i,count,285,uint16,1,R -com.victronenergy.battery,/History/TotalAhDrawn,d,Ah,286,uint16,-10,R -com.victronenergy.battery,/History/MinimumVoltage,d,V DC,287,uint16,100,R -com.victronenergy.battery,/History/MaximumVoltage,d,V DC,288,uint16,100,R -com.victronenergy.battery,/History/TimeSinceLastFullCharge,i,seconds,289,uint16,0.01,R -com.victronenergy.battery,/History/AutomaticSyncs,i,count,290,uint16,1,R -com.victronenergy.battery,/History/LowVoltageAlarms,i,count,291,uint16,1,R -com.victronenergy.battery,/History/HighVoltageAlarms,i,count,292,uint16,1,R -com.victronenergy.battery,/History/LowStarterVoltageAlarms,i,count,293,uint16,1,R -com.victronenergy.battery,/History/HighStarterVoltageAlarms,i,count,294,uint16,1,R -com.victronenergy.battery,/History/MinimumStarterVoltage,d,V DC,295,uint16,100,R -com.victronenergy.battery,/History/MaximumStarterVoltage,d,V DC,296,uint16,100,R -com.victronenergy.battery,/History/LowFusedVoltageAlarms,i,count,297,uint16,1,R -com.victronenergy.battery,/History/HighFusedVoltageAlarms,i,count,298,uint16,1,R -com.victronenergy.battery,/History/MinimumFusedVoltage,d,V DC,299,uint16,100,R -com.victronenergy.battery,/History/MaximumFusedVoltage,d,V DC,300,uint16,100,R -com.victronenergy.battery,/History/DischargedEnergy,d,kWh,301,uint16,10,R -com.victronenergy.battery,/History/ChargedEnergy,d,kWh,302,uint16,10,R -com.victronenergy.battery,/Capacity,d,Ah,309,uint16,10,R -com.victronenergy.battery,/State,y,0=Initializing (Wait start);1=Initializing (before boot);2=Initializing (Before boot delay);3=Initializing (Wait boot);4=Initializing;5=Initializing (Measure battery voltage);6=Initializing (Calculate battery voltage);7=Initializing (Wait bus voltage);8=Initializing (Wait for lynx shunt);9=Running;10=Error (10);11=Error (11),1282,uint16,1,R -com.victronenergy.battery,/ErrorCode,y,0=No error;1=Battery initialization error;2=No batteries connected;3=Unknown battery connected;4=Different battery type;5=Number of batteries incorrect;6=Lynx Shunt not found;7=Battery measure error;8=Internal calculation error;9=Batteries in series not ok;10=Number of batteries incorrect;11=Hardware error;12=Watchdog error;13=Over voltage;14=Under voltage;15=Over temperature;16=Under temperature;17=Hardware fault;18=Standby shutdown;19=Pre-charge charge error;20=Safety contactor check error;21=Pre-charge discharge error;22=ADC error;23=Slave error;24=Slave warning;25=Pre-charge error;26=Safety contactor error;27=Over current;28=Slave update failed;29=Slave update unavailable,1283,uint16,1,R -com.victronenergy.battery,/SystemSwitch,y,0=Disabled;1=Enabled,1284,uint16,1,R -com.victronenergy.battery,/Balancing,y,0=Inactive;1=Active,1285,uint16,1,R -com.victronenergy.battery,/System/NrOfBatteries,y,count,1286,uint16,1,R -com.victronenergy.battery,/System/BatteriesParallel,y,count,1287,uint16,1,R -com.victronenergy.battery,/System/BatteriesSeries,y,count,1288,uint16,1,R -com.victronenergy.battery,/System/NrOfCellsPerBattery,y,count,1289,uint16,1,R -com.victronenergy.battery,/System/MinCellVoltage,d,V DC,1290,uint16,100,R -com.victronenergy.battery,/System/MaxCellVoltage,d,V DC,1291,uint16,100,R -com.victronenergy.battery,/Diagnostics/ShutDownsDueError,q,count,1292,uint16,1,R -com.victronenergy.battery,/Diagnostics/LastErrors/1/Error,y,0=No error;1=Battery initialization error;2=No batteries connected;3=Unknown battery connected;4=Different battery type;5=Number of batteries incorrect;6=Lynx Shunt not found;7=Battery measure error;8=Internal calculation error;9=Batteries in series not ok;10=Number of batteries incorrect;11=Hardware error;12=Watchdog error;13=Over voltage;14=Under voltage;15=Over temperature;16=Under temperature;17=Hardware fault;18=Standby shutdown;19=Pre-charge charge error;20=Safety contactor check error;21=Pre-charge discharge error;22=ADC error;23=Slave error;24=Slave warning;25=Pre-charge error;26=Safety contactor error;27=Over current;28=Slave update failed;29=Slave update unavailable,1293,uint16,1,R -com.victronenergy.battery,/Diagnostics/LastErrors/2/Error,y,0=No error;1=Battery initialization error;2=No batteries connected;3=Unknown battery connected;4=Different battery type;5=Number of batteries incorrect;6=Lynx Shunt not found;7=Battery measure error;8=Internal calculation error;9=Batteries in series not ok;10=Number of batteries incorrect;11=Hardware error;12=Watchdog error;13=Over voltage;14=Under voltage;15=Over temperature;16=Under temperature;17=Hardware fault;18=Standby shutdown;19=Pre-charge charge error;20=Safety contactor check error;21=Pre-charge discharge error;22=ADC error;23=Slave error;24=Slave warning;25=Pre-charge error;26=Safety contactor error;27=Over current;28=Slave update failed;29=Slave update unavailable,1294,uint16,1,R -com.victronenergy.battery,/Diagnostics/LastErrors/3/Error,y,0=No error;1=Battery initialization error;2=No batteries connected;3=Unknown battery connected;4=Different battery type;5=Number of batteries incorrect;6=Lynx Shunt not found;7=Battery measure error;8=Internal calculation error;9=Batteries in series not ok;10=Number of batteries incorrect;11=Hardware error;12=Watchdog error;13=Over voltage;14=Under voltage;15=Over temperature;16=Under temperature;17=Hardware fault;18=Standby shutdown;19=Pre-charge charge error;20=Safety contactor check error;21=Pre-charge discharge error;22=ADC error;23=Slave error;24=Slave warning;25=Pre-charge error;26=Safety contactor error;27=Over current;28=Slave update failed;29=Slave update unavailable,1295,uint16,1,R -com.victronenergy.battery,/Diagnostics/LastErrors/4/Error,y,0=No error;1=Battery initialization error;2=No batteries connected;3=Unknown battery connected;4=Different battery type;5=Number of batteries incorrect;6=Lynx Shunt not found;7=Battery measure error;8=Internal calculation error;9=Batteries in series not ok;10=Number of batteries incorrect;11=Hardware error;12=Watchdog error;13=Over voltage;14=Under voltage;15=Over temperature;16=Under temperature;17=Hardware fault;18=Standby shutdown;19=Pre-charge charge error;20=Safety contactor check error;21=Pre-charge discharge error;22=ADC error;23=Slave error;24=Slave warning;25=Pre-charge error;26=Safety contactor error;27=Over current;28=Slave update failed;29=Slave update unavailable,1296,uint16,1,R -com.victronenergy.battery,/Diagnostics/LastErrors/1/Time,u,,310,int32,1,R -com.victronenergy.battery,/Diagnostics/LastErrors/2/Time,u,,312,int32,1,R -com.victronenergy.battery,/Diagnostics/LastErrors/3/Time,u,,314,int32,1,R -com.victronenergy.battery,/Diagnostics/LastErrors/4/Time,u,,316,int32,1,R -com.victronenergy.battery,/System/MinCellTemperature,d,Degrees celsius,318,int16,10,R -com.victronenergy.battery,/System/MaxCellTemperature,d,Degrees celsius,319,int16,10,R -com.victronenergy.battery,/Io/AllowToCharge,y,0=No;1=Yes,1297,uint16,1,R -com.victronenergy.battery,/Io/AllowToDischarge,y,0=No;1=Yes,1298,uint16,1,R -com.victronenergy.battery,/Io/ExternalRelay,y,0=Inactive;1=Active,1299,uint16,1,R -com.victronenergy.battery,/History/MinimumCellVoltage,d,V DC,1300,uint16,100,R -com.victronenergy.battery,/History/MaximumCellVoltage,d,V DC,1301,uint16,100,R -com.victronenergy.battery,/System/NrOfModulesOffline,u,,1302,uint16,1,R -com.victronenergy.battery,/System/NrOfModulesOnline,u,,1303,uint16,1,R -com.victronenergy.battery,/System/NrOfModulesBlockingCharge,u,,1304,uint16,1,R -com.victronenergy.battery,/System/NrOfModulesBlockingDischarge,u,,1305,uint16,1,R -com.victronenergy.battery,/System/MinVoltageCellId,s,,1306,string[4],1,R -com.victronenergy.battery,/System/MaxVoltageCellId,s,,1310,string[4],1,R -com.victronenergy.battery,/System/MinTemperatureCellId,s,,1314,string[4],1,R -com.victronenergy.battery,/System/MaxTemperatureCellId,s,,1318,string[4],1,R -com.victronenergy.solarcharger,/Dc/0/Voltage,d,V DC,771,uint16,100,R -com.victronenergy.solarcharger,/Dc/0/Current,d,A DC,772,int16,10,R -com.victronenergy.solarcharger,/Dc/0/Temperature,d,Degrees celsius,773,int16,10,R -com.victronenergy.solarcharger,/Mode,i,1=On;4=Off,774,uint16,1,W -com.victronenergy.solarcharger,/State,i,0=Off;2=Fault;3=Bulk;4=Absorption;5=Float;6=Storage;7=Equalize;11=Other (Hub-1);252=Hub-1,775,uint16,1,R -com.victronenergy.solarcharger,/Pv/V,d,V DC,776,uint16,100,R -com.victronenergy.solarcharger,/Pv/I,d,A DC,777,,10,R,HANDLED IN mappings.c -com.victronenergy.solarcharger,/Yield/Power,d,W,789,uint16,10,R -com.victronenergy.solarcharger,/Equalization/Pending,i,0=No;1=Yes;2=Error;3=Unavailable- Unknown,778,uint16,1,R -com.victronenergy.solarcharger,/Equalization/TimeRemaining,d,seconds,779,uint16,10,R -com.victronenergy.solarcharger,/Relay/0/State,i,0=Open;1=Closed,780,uint16,1,R -com.victronenergy.solarcharger,/Alarms/Alarm,i,0=No alarm;2=Alarm,781,uint16,1,R -com.victronenergy.solarcharger,/Alarms/LowVoltage,i,0=No alarm;2=Alarm,782,uint16,1,R -com.victronenergy.solarcharger,/Alarms/HighVoltage,i,0=No alarm;2=Alarm,783,uint16,1,R -com.victronenergy.solarcharger,/History/Daily/0/Yield,d,kWh,784,uint16,10,R -com.victronenergy.solarcharger,/History/Daily/0/MaxPower,d,W,785,uint16,1,R -com.victronenergy.solarcharger,/History/Daily/1/Yield,d,kWh,786,uint16,10,R -com.victronenergy.solarcharger,/History/Daily/1/MaxPower,d,W,787,uint16,1,R -com.victronenergy.solarcharger,/ErrorCode,i,0=No error;1=Battery temperature too high;2=Battery voltage too high;3=Battery temperature sensor miswired (+);4=Battery temperature sensor miswired (-);5=Battery temperature sensor disconnected;6=Battery voltage sense miswired (+);7=Battery voltage sense miswired (-);8=Battery voltage sense disconnected;9=Battery voltage wire losses too high;17=Charger temperature too high;18=Charger over-current;19=Charger current polarity reversed;20=Bulk time limit reached;22=Charger temperature sensor miswired;23=Charger temperature sensor disconnected;34=Input current too high,788,uint16,1,R -com.victronenergy.solarcharger,/Yield/User,d,kWh,790,uint16,10,R -com.victronenergy.solarcharger,/MppOperationMode,i,0=Off;1=Voltage/current limited;2=MPPT active;255=Not available,791,uint16,1,R -com.victronenergy.solarcharger,/Pv/0/V,d,V DC,3700,uint16,100,R -com.victronenergy.solarcharger,/Pv/1/V,d,V DC,3701,uint16,100,R -com.victronenergy.solarcharger,/Pv/2/V,d,V DC,3702,uint16,100,R -com.victronenergy.solarcharger,/Pv/3/V,d,V DC,3703,uint16,100,R -com.victronenergy.solarcharger,/History/Daily/0/Pv/0/Yield,d,kWh,3708,uint16,10,R -com.victronenergy.solarcharger,/History/Daily/0/Pv/1/Yield,d,kWh,3709,uint16,10,R -com.victronenergy.solarcharger,/History/Daily/0/Pv/2/Yield,d,kWh,3710,uint16,10,R -com.victronenergy.solarcharger,/History/Daily/0/Pv/3/Yield,d,kWh,3711,uint16,10,R -com.victronenergy.solarcharger,/History/Daily/1/Pv/0/Yield,d,kWh,3712,uint16,10,R -com.victronenergy.solarcharger,/History/Daily/1/Pv/1/Yield,d,kWh,3713,uint16,10,R -com.victronenergy.solarcharger,/History/Daily/1/Pv/2/Yield,d,kWh,3714,uint16,10,R -com.victronenergy.solarcharger,/History/Daily/1/Pv/3/Yield,d,kWh,3715,uint16,10,R -com.victronenergy.solarcharger,/History/Daily/0/Pv/0/MaxPower,d,W,3716,uint16,1,R -com.victronenergy.solarcharger,/History/Daily/0/Pv/1/MaxPower,d,W,3717,uint16,1,R -com.victronenergy.solarcharger,/History/Daily/0/Pv/2/MaxPower,d,W,3718,uint16,1,R -com.victronenergy.solarcharger,/History/Daily/0/Pv/3/MaxPower,d,W,3719,uint16,1,R -com.victronenergy.solarcharger,/History/Daily/1/Pv/0/MaxPower,d,W,3720,uint16,1,R -com.victronenergy.solarcharger,/History/Daily/1/Pv/1/MaxPower,d,W,3721,uint16,1,R -com.victronenergy.solarcharger,/History/Daily/1/Pv/2/MaxPower,d,W,3722,uint16,1,R -com.victronenergy.solarcharger,/History/Daily/1/Pv/3/MaxPower,d,W,3723,uint16,1,R -com.victronenergy.solarcharger,/Pv/0/P,d,W,3724,uint16,1,R -com.victronenergy.solarcharger,/Pv/1/P,d,W,3725,uint16,1,R -com.victronenergy.solarcharger,/Pv/2/P,d,W,3726,uint16,1,R -com.victronenergy.solarcharger,/Pv/3/P,d,W,3727,uint16,1,R -com.victronenergy.system,/Ac/PvOnOutput/L1/Power,d,W,808,uint16,1,R -com.victronenergy.system,/Ac/PvOnOutput/L2/Power,d,W,809,uint16,1,R -com.victronenergy.system,/Ac/PvOnOutput/L3/Power,d,W,810,uint16,1,R -com.victronenergy.system,/Ac/PvOnGrid/L1/Power,d,W,811,uint16,1,R -com.victronenergy.system,/Ac/PvOnGrid/L2/Power,d,W,812,uint16,1,R -com.victronenergy.system,/Ac/PvOnGrid/L3/Power,d,W,813,uint16,1,R -com.victronenergy.system,/Ac/PvOnGenset/L1/Power,d,W,814,uint16,1,R -com.victronenergy.system,/Ac/PvOnGenset/L2/Power,d,W,815,uint16,1,R -com.victronenergy.system,/Ac/PvOnGenset/L3/Power,d,W,816,uint16,1,R -com.victronenergy.system,/Dc/Pv/Power,d,W,850,uint16,1,R -com.victronenergy.system,/Dc/Pv/Current,d,A DC,851,int16,10,R -com.victronenergy.system,/Dc/Charger/Power,d,W,855,uint16,1,R -com.victronenergy.system,/Ac/Consumption/L1/Power,d,W,817,uint16,1,R -com.victronenergy.system,/Ac/Consumption/L2/Power,d,W,818,uint16,1,R -com.victronenergy.system,/Ac/Consumption/L3/Power,d,W,819,uint16,1,R -com.victronenergy.system,/Ac/Grid/L1/Power,d,W,820,int16,1,R -com.victronenergy.system,/Ac/Grid/L2/Power,d,W,821,int16,1,R -com.victronenergy.system,/Ac/Grid/L3/Power,d,W,822,int16,1,R -com.victronenergy.system,/Ac/Genset/L1/Power,d,W,823,int16,1,R -com.victronenergy.system,/Ac/Genset/L2/Power,d,W,824,int16,1,R -com.victronenergy.system,/Ac/Genset/L3/Power,d,W,825,int16,1,R -com.victronenergy.system,/Ac/ActiveIn/Source,u,0=Not available;1=Grid;2=Generator;3=Shore power;240=Not connected,826,int16,1,R -com.victronenergy.system,/Dc/System/Power,d,W,860,int16,1,R -com.victronenergy.system,/Dc/Battery/Voltage,d,V DC,840,uint16,10,R -com.victronenergy.system,/Dc/Battery/Current,d,A DC,841,int16,10,R -com.victronenergy.system,/Dc/Battery/Power,d,W,842,int16,1,R -com.victronenergy.system,/Dc/Vebus/Current,d,A DC,865,int16,10,R -com.victronenergy.system,/Dc/Vebus/Power,d,W,866,int16,1,R -com.victronenergy.system,/Dc/Battery/Soc,d,%,843,uint16,1,R -com.victronenergy.system,/Dc/Battery/State,i,0=idle;1=charging;2=discharging,844,uint16,1,R -com.victronenergy.system,/Dc/Battery/ConsumedAmphours,d,Ah,845,uint16,-10,R -com.victronenergy.system,/Dc/Battery/TimeToGo,d,s,846,uint16,0.01,R -com.victronenergy.system,/Relay/0/State,i,0=Open;1=Closed,806,uint16,1,W -com.victronenergy.system,/Relay/1/State,i,0=Open;1=Closed,807,uint16,1,W -com.victronenergy.system,/Serial,s,,800,string[6],1,R -com.victronenergy.pvinverter,/Position,i,0=AC input 1;1=AC output;2=AC input 2,1026,uint16,1,R -com.victronenergy.pvinverter,/Serial,s,,1039,string[7],1,R -com.victronenergy.pvinverter,/Ac/L1/Voltage,d,V AC,1027,uint16,10,R -com.victronenergy.pvinverter,/Ac/L1/Current,d,A AC,1028,int16,10,R -com.victronenergy.pvinverter,/Ac/L1/Power,i,W,1029,uint16,1,R -com.victronenergy.pvinverter,/Ac/L1/Energy/Forward,u,kWh,1030,uint16,100,R -com.victronenergy.pvinverter,/Ac/L2/Voltage,d,V AC,1031,uint16,10,R -com.victronenergy.pvinverter,/Ac/L2/Current,d,A AC,1032,int16,10,R -com.victronenergy.pvinverter,/Ac/L2/Power,i,W,1033,uint16,1,R -com.victronenergy.pvinverter,/Ac/L2/Energy/Forward,u,kWh,1034,uint16,100,R -com.victronenergy.pvinverter,/Ac/L3/Voltage,d,V AC,1035,uint16,10,R -com.victronenergy.pvinverter,/Ac/L3/Current,d,A AC,1036,int16,10,R -com.victronenergy.pvinverter,/Ac/L3/Power,i,W,1037,uint16,1,R -com.victronenergy.pvinverter,/Ac/L3/Energy/Forward,u,kWh,1038,uint16,100,R -com.victronenergy.pvinverter,/Ac/L1/Energy/Forward,u,kWh,1046,uint32,100,R -com.victronenergy.pvinverter,/Ac/L2/Energy/Forward,u,kWh,1048,uint32,100,R -com.victronenergy.pvinverter,/Ac/L3/Energy/Forward,u,kWh,1050,uint32,100,R -com.victronenergy.pvinverter,/Ac/Power,i,kW,1052,int32,1,W -com.victronenergy.pvinverter,/Ac/MaxPower,u,kW,1054,uint32,1,W -com.victronenergy.pvinverter,/Ac/PowerLimit,u,kW,1056,uint32,1,W -com.victronenergy.motordrive,/Motor/RPM,d,RPM,2048,int16,1,R -com.victronenergy.motordrive,/Motor/Temperature,d,Degrees celsius,2049,int16,10,R -com.victronenergy.motordrive,/Dc/0/Voltage,d,V DC,2050,uint16,100,R -com.victronenergy.motordrive,/Dc/0/Current,d,A DC,2051,int16,10,R -com.victronenergy.motordrive,/Dc/0/Power,d,W,2052,int16,10,R -com.victronenergy.motordrive,/Controller/Temperature,d,Degrees celsius,2053,int16,10,R -com.victronenergy.charger,/Dc/0/Voltage,d,V DC,2307,uint16,100,R -com.victronenergy.charger,/Dc/0/Current,d,A DC,2308,int16,10,R -com.victronenergy.charger,/Dc/0/Temperature,d,Degrees celsius,2309,int16,10,R -com.victronenergy.charger,/Dc/1/Voltage,d,V DC,2310,uint16,100,R -com.victronenergy.charger,/Dc/1/Current,d,A DC,2311,int16,10,R -com.victronenergy.charger,/Dc/2/Voltage,d,V DC,2312,uint16,100,R -com.victronenergy.charger,/Dc/2/Current,d,A DC,2313,int16,10,R -com.victronenergy.charger,/Ac/In/L1/I,d,A AC,2314,int16,10,R -com.victronenergy.charger,/Ac/In/L1/P,d,W DC,2315,uint16,1,R -com.victronenergy.charger,/Ac/In/CurrentLimit,d,A AC,2316,int16,10,W -com.victronenergy.charger,/Mode,i,0=Off;1=On;2=Error;3=Unavailable- Unknown,2317,uint16,1,W -com.victronenergy.charger,/State,i,0=Off;1=Low Power Mode;2=Fault;3=Bulk;4=Absorption;5=Float;6=Storage;7=Equalize;8=Passthru;9=Inverting;10=Power assist;11=Power supply mode;252=Bulk protection,2318,uint16,1,R -com.victronenergy.charger,/ErrorCode,i,0=No error;1=Battery temperature too high;2=Battery voltage too high;3=Battery temperature sensor miswired (+);4=Battery temperature sensor miswired (-);5=Battery temperature sensor disconnected;6=Battery voltage sense miswired (+);7=Battery voltage sense miswired (-);8=Battery voltage sense disconnected;9=Battery voltage wire losses too high;17=Charger temperature too high;18=Charger over-current;19=Charger current polarity reversed;20=Bulk time limit reached;22=Charger temperature sensor miswired;23=Charger temperature sensor disconnected;34=Input current too high,2319,uint16,1,R -com.victronenergy.charger,/Relay/0/State,i,0=Open;1=Closed,2320,uint16,1,R -com.victronenergy.charger,/Alarms/LowVoltage,i,0=No alarm;2=Alarm,2321,uint16,1,R -com.victronenergy.charger,/Alarms/HighVoltage,i,0=No alarm;2=Alarm,2322,uint16,1,R -com.victronenergy.grid,/Ac/L1/Power,d,W,2600,int16,1,R -com.victronenergy.grid,/Ac/L2/Power,d,W,2601,int16,1,R -com.victronenergy.grid,/Ac/L3/Power,d,W,2602,int16,1,R -com.victronenergy.grid,/Ac/L1/Energy/Forward,d,kWh,2603,uint16,100,R -com.victronenergy.grid,/Ac/L2/Energy/Forward,d,kWh,2604,uint16,100,R -com.victronenergy.grid,/Ac/L3/Energy/Forward,d,kWh,2605,uint16,100,R -com.victronenergy.grid,/Ac/L1/Energy/Reverse,d,kWh,2606,uint16,100,R -com.victronenergy.grid,/Ac/L2/Energy/Reverse,d,kWh,2607,uint16,100,R -com.victronenergy.grid,/Ac/L3/Energy/Reverse,d,kWh,2608,uint16,100,R -com.victronenergy.grid,/Serial,s,,2609,string[7],,R -com.victronenergy.grid,/Ac/L1/Voltage,d,V AC,2616,uint16,10,R -com.victronenergy.grid,/Ac/L1/Current,d,A AC,2617,int16,10,R -com.victronenergy.grid,/Ac/L2/Voltage,d,V AC,2618,uint16,10,R -com.victronenergy.grid,/Ac/L2/Current,d,A AC,2619,int16,10,R -com.victronenergy.grid,/Ac/L3/Voltage,d,V AC,2620,uint16,10,R -com.victronenergy.grid,/Ac/L3/Current,d,A AC,2621,int16,10,R -com.victronenergy.grid,/Ac/L1/Energy/Forward,d,kWh,2622,uint32,100,R -com.victronenergy.grid,/Ac/L2/Energy/Forward,d,kWh,2624,uint32,100,R -com.victronenergy.grid,/Ac/L3/Energy/Forward,d,kWh,2626,uint32,100,R -com.victronenergy.grid,/Ac/L1/Energy/Reverse,d,kWh,2628,uint32,100,R -com.victronenergy.grid,/Ac/L2/Energy/Reverse,d,kWh,2630,uint32,100,R -com.victronenergy.grid,/Ac/L3/Energy/Reverse,d,kWh,2632,uint32,100,R -com.victronenergy.grid,/Ac/Energy/Forward,d,kWh,2634,uint32,100,R -com.victronenergy.grid,/Ac/Energy/Reverse,d,kWh,2636,uint32,100,R -com.victronenergy.settings,/Settings/CGwacs/AcPowerSetPoint,d,W,2700,int16,1,W -com.victronenergy.settings,/Settings/CGwacs/AcPowerSetPoint,d,W,2703,int16,0.01,W -com.victronenergy.settings,/Settings/CGwacs/MaxChargePercentage,d,%,2701,uint16,1,W -com.victronenergy.settings,/Settings/CGwacs/MaxDischargePercentage,d,%,2702,uint16,1,W -com.victronenergy.settings,/Settings/CGwacs/MaxDischargePower,d,W,2704,uint16,0.1,W -com.victronenergy.settings,/Settings/SystemSetup/MaxChargeCurrent,d,A,2705,int16,1,W -com.victronenergy.settings,/Settings/CGwacs/MaxFeedInPower,d,W,2706,int16,0.01,W -com.victronenergy.settings,/Settings/CGwacs/OvervoltageFeedIn,i,0=Don't feed excess DC-coupled PV into grid;1=Feed excess DC-coupled PV into the grid,2707,uint16,1,W -com.victronenergy.settings,/Settings/CGwacs/PreventFeedback,i,0=Feed excess AC-coupled PV into grid;1=Don't feed excess AC-coupled PV into the grid,2708,uint16,1,W -com.victronenergy.settings,/Settings/SystemSetup/MaxChargeVoltage,d,V,2710,uint16,10,W -com.victronenergy.hub4,/PvPowerLimiterActive,i,0=Feed-in limiting inactive;1=Feed-in limiting active,2709,uint16,1,R -com.victronenergy.tank,/ProductId,u,,3000,uint16,1,R -com.victronenergy.tank,/Capacity,d,m3,3001,uint32,10000,R -com.victronenergy.tank,/FluidType,u,0=Fuel;1=Fresh water;2=Waste water;3=Live well;4=Oil;5=Black water (sewage);6=Gasoline;7=Diesel;8=LPG;9=LNG;10=Hydraulic oil;11=Raw water,3003,uint16,1,R -com.victronenergy.tank,/Level,d,%,3004,uint16,10,R -com.victronenergy.tank,/Remaining,d,m3,3005,uint32,10000,R -com.victronenergy.tank,/Status,u,0=OK;1=Disconnected;2=Short circuited;3=Reverse Polarity;4=Unknown,3007,uint16,1,R -com.victronenergy.inverter,/ProductId,u,,3127,uint16,1,R -com.victronenergy.inverter,/FirmwareVersion,u,,3125,uint16,1,R -com.victronenergy.inverter,/Ac/Out/L1/I,d,A AC,3100,int16,10,R -com.victronenergy.inverter,/Ac/Out/L1/V,d,V AC,3101,uint16,10,R -com.victronenergy.inverter,/Ac/Out/L1/P,d,W AC,3102,int16,0.1,R -com.victronenergy.inverter,/Alarms/HighTemperature,u,0=No alarm;1=Warning;2=Alarm,3110,uint16,1,R -com.victronenergy.inverter,/Alarms/HighVoltage,u,0=No alarm;1=Warning;2=Alarm,3111,uint16,1,R -com.victronenergy.inverter,/Alarms/HighVoltageAcOut,u,0=No alarm;1=Warning;2=Alarm,3112,uint16,1,R -com.victronenergy.inverter,/Alarms/LowTemperature,u,0=No alarm;1=Warning;2=Alarm,3113,uint16,1,R -com.victronenergy.inverter,/Alarms/LowVoltage,u,0=No alarm;1=Warning;2=Alarm,3114,uint16,1,R -com.victronenergy.inverter,/Alarms/LowVoltageAcOut,u,0=No alarm;1=Warning;2=Alarm,3115,uint16,1,R -com.victronenergy.inverter,/Alarms/Overload,u,0=No alarm;1=Warning;2=Alarm,3116,uint16,1,R -com.victronenergy.inverter,/Alarms/Ripple,u,0=No alarm;1=Warning;2=Alarm,3117,uint16,1,R -com.victronenergy.inverter,/Dc/0/Voltage,d,V DC,3105,uint16,100,R -com.victronenergy.inverter,/Dc/0/Current,d,A DC,3106,int16,10,R -com.victronenergy.inverter,/Mode,u,2=On;4=Off;5=Eco,3126,uint16,1,W -com.victronenergy.inverter,/State,u,0=Off;1=Low power mode (search mode);2=Fault;9=Inverting (on),3128,uint16,1,R -com.victronenergy.inverter,/Energy/InverterToAcOut,d,kWh,3130,uint32,100,R -com.victronenergy.inverter,/Energy/OutToInverter,d,kWh,3132,uint32,100,R -com.victronenergy.inverter,/Energy/SolarToAcOut,d,kWh,3134,uint32,100,R -com.victronenergy.inverter,/Energy/SolarToBattery,d,kWh,3136,uint32,100,R -com.victronenergy.inverter,/Pv/V,d,V DC,3138,uint16,10,R -com.victronenergy.inverter,/Pv/0/V,d,V DC,3140,uint16,10,R -com.victronenergy.inverter,/Pv/1/V,d,V DC,3141,uint16,10,R -com.victronenergy.inverter,/Pv/2/V,d,V DC,3142,uint16,10,R -com.victronenergy.inverter,/Pv/3/V,d,V DC,3143,uint16,10,R -com.victronenergy.inverter,/History/Daily/0/Pv/0/Yield,d,kWh,3148,uint16,10,R -com.victronenergy.inverter,/History/Daily/0/Pv/1/Yield,d,kWh,3149,uint16,10,R -com.victronenergy.inverter,/History/Daily/0/Pv/2/Yield,d,kWh,3150,uint16,10,R -com.victronenergy.inverter,/History/Daily/0/Pv/3/Yield,d,kWh,3151,uint16,10,R -com.victronenergy.inverter,/History/Daily/1/Pv/0/Yield,d,kWh,3152,uint16,10,R -com.victronenergy.inverter,/History/Daily/1/Pv/1/Yield,d,kWh,3153,uint16,10,R -com.victronenergy.inverter,/History/Daily/1/Pv/2/Yield,d,kWh,3154,uint16,10,R -com.victronenergy.inverter,/History/Daily/1/Pv/3/Yield,d,kWh,3155,uint16,10,R -com.victronenergy.inverter,/History/Daily/0/Pv/0/MaxPower,d,W,3156,uint16,1,R -com.victronenergy.inverter,/History/Daily/0/Pv/1/MaxPower,d,W,3157,uint16,1,R -com.victronenergy.inverter,/History/Daily/0/Pv/2/MaxPower,d,W,3158,uint16,1,R -com.victronenergy.inverter,/History/Daily/0/Pv/3/MaxPower,d,W,3159,uint16,1,R -com.victronenergy.inverter,/History/Daily/1/Pv/0/MaxPower,d,W,3160,uint16,1,R -com.victronenergy.inverter,/History/Daily/1/Pv/1/MaxPower,d,W,3161,uint16,1,R -com.victronenergy.inverter,/History/Daily/1/Pv/2/MaxPower,d,W,3162,uint16,1,R -com.victronenergy.inverter,/History/Daily/1/Pv/3/MaxPower,d,W,3163,uint16,1,R -com.victronenergy.inverter,/Pv/0/P,d,W,3164,uint16,1,R -com.victronenergy.inverter,/Pv/1/P,d,W,3165,uint16,1,R -com.victronenergy.inverter,/Pv/2/P,d,W,3166,uint16,1,R -com.victronenergy.inverter,/Pv/3/P,d,W,3167,uint16,1,R -com.victronenergy.inverter,/Alarms/LowSoc,u,,3168,uint16,1,R -com.victronenergy.genset,/ProductId,u,,3212,uint16,1,R -com.victronenergy.genset,/StatusCode,u,0=Standby;1=Startup 1;2=Startup 2;3=Startup 3;4=Startup 4;5=Startup 5;6=Startup 6;7=Startup 7;8=Running;9=Stopping;10=Error,3213,uint16,1,R -com.victronenergy.genset,/ErrorCode,u,0=No error;1=AC voltage L1 too low;2=AC frequency L1 too low;3=AC current too low;4=AC power too low;5=Emergency stop;6=Servo current too low;7=Oil pressure too low;8=Engine temperature too low;9=Winding temperature too low;10=Exhaust temperature too low;13=Starter current too low;14=Glow current too low;15=Glow current too low;16=Fuel holding magnet current too low;17=Stop solenoid hold coil current too low;18=Stop solenoid pull coil current too low;19=Optional DC out current too low;20=5V output voltage too low;21=Boost output current too low;22=Panel supply current too high;25=Starter battery voltage too low;26=Startup aborted (rotation too low);28=Rotation too low;29=Power contactor current too low;30=AC voltage L2 too low;31=AC frequency L2 too low;32=AC current L2 too low;33=AC power L2 too low;34=AC voltage L3 too low;35=AC frequency L3 too low;36=AC current L3 too low;37=AC power L3 too low;62=Fuel temperature too low;63=Fuel level too low;65=AC voltage L1 too high;66=AC frequency too high;67=AC current too high;68=AC power too high;70=Servo current too high;71=Oil pressure too high;72=Engine temperature too high;73=Winding temperature too high;74=Exhaust temperature too low;77=Starter current too low;78=Glow current too high;79=Glow current too high;80=Fuel holding magnet current too high;81=Stop solenoid hold coil current too high;82=Stop solenoid pull coil current too high;83=Optional DC out current too high;84=5V output voltage too high;85=Boost output current too high;89=Starter battery voltage too high;90=Startup aborted (rotation too high);92=Rotation too high;93=Power contactor current too high;94=AC voltage L2 too high;95=AC frequency L2 too high;96=AC current L2 too high;97=AC power L2 too high;98=AC voltage L3 too high;99=AC frequency L3 too high;100=AC current L3 too high;101=AC power L3 too high;126=Fuel temperature too high;127=Fuel level too high;130=Lost control unit;131=Lost panel;132=Service needed;133=Lost 3-phase module;134=Lost AGT module;135=Synchronization failure;137=Intake airfilter;139=Lost sync. module;140=Load-balance failed;141=Sync-mode deactivated;142=Engine controller;148=Rotating field wrong;149=Fuel level sensor lost;150=Init failed;151=Watchdog;152=Out: winding;153=Out: exhaust;154=Out: Cyl. head;155=Inverter over temperature;156=Inverter overload;157=Inverter communication lost;158=Inverter sync failed;159=CAN communication lost;160=L1 overload;161=L2 overload;162=L3 overload;163=DC overload;164=DC overvoltage;165=Emergency stop;166=No connection,3214,uint16,1,R -com.victronenergy.genset,/AutoStart,u,0=Disabled;1=Enabled,3215,uint16,1,R -com.victronenergy.genset,/Start,u,0=Stop;1=Start,3223,uint16,1,W -com.victronenergy.genset,/Engine/Load,d,%,3216,uint16,1,R -com.victronenergy.genset,/Engine/Speed,d,RPM,3217,uint16,1,R -com.victronenergy.genset,/Engine/OperatingHours,d,s,3218,uint16,0.01,R -com.victronenergy.genset,/Engine/CoolantTemperature,d,Degrees celsius,3219,int16,10,R -com.victronenergy.genset,/Engine/WindingTemperature,d,Degrees celsius,3220,int16,10,R -com.victronenergy.genset,/Engine/ExaustTemperature,d,Degrees celsius,3221,int16,10,R -com.victronenergy.genset,/StarterVoltage,d,V DC,3222,uint16,100,R -com.victronenergy.genset,/Ac/L1/Voltage,d,V AC,3200,uint16,10,R -com.victronenergy.genset,/Ac/L1/Current,d,A AC,3203,int16,10,R -com.victronenergy.genset,/Ac/L1/Power,d,W,3206,int16,1,R -com.victronenergy.genset,/Ac/L1/Frequency,d,Hz,3209,uint16,100,R -com.victronenergy.genset,/Ac/L2/Voltage,d,V AC,3201,uint16,10,R -com.victronenergy.genset,/Ac/L2/Current,d,A AC,3204,int16,10,R -com.victronenergy.genset,/Ac/L2/Power,d,W,3207,int16,1,R -com.victronenergy.genset,/Ac/L2/Frequency,d,Hz,3210,uint16,100,R -com.victronenergy.genset,/Ac/L3/Voltage,d,V AC,3202,uint16,10,R -com.victronenergy.genset,/Ac/L3/Current,d,A AC,3205,int16,10,R -com.victronenergy.genset,/Ac/L3/Power,d,W,3208,int16,1,R -com.victronenergy.genset,/Ac/L3/Frequency,d,Hz,3211,uint16,100,R -com.victronenergy.temperature,/ProductId,u,,3300,uint16,1,R -com.victronenergy.temperature,/Scale,d,,3301,uint16,100,R -com.victronenergy.temperature,/Offset,d,,3302,int16,100,R -com.victronenergy.temperature,/TemperatureType,u,0=Battery;1=Fridge;2=Generic,3303,uint16,1,R -com.victronenergy.temperature,/Temperature,d,Degrees celsius,3304,int16,100,R -com.victronenergy.temperature,/Status,u,0=OK;1=Disconnected;2=Short circuited;3=Reverse Polarity;4=Unknown,3305,uint16,1,R -com.victronenergy.temperature,/Humidity,u,%,3306,uint16,10,R -com.victronenergy.temperature,/BatteryVoltage,d,V,3307,uint16,100,R -com.victronenergy.temperature,/Pressure,u,hPa,3308,uint16,1,R -com.victronenergy.pulsemeter,/Aggregate,i,,3400,uint32,1,R -com.victronenergy.pulsemeter,/Count,i,,3402,uint32,1,R -com.victronenergy.digitalinput,/Count,i,,3420,uint32,1,R -com.victronenergy.digitalinput,/State,i,,3422,uint16,1,R -com.victronenergy.digitalinput,/Alarm,i,0=No alarm;2=Alarm,3423,uint16,1,R -com.victronenergy.digitalinput,/Type,i,2=Door;3=Bilge pump;4=Bilge alarm;5=Burglar alarm;6=Smoke alarm;7=Fire alarm;8=CO2 alarm,3424,uint16,1,R -com.victronenergy.generator,/ManualStart,i,0=Stop generator;1=Start generator,3500,uint16,1,W -com.victronenergy.generator,/RunningByConditionCode,i,0=Stopped;1=Manual;2=TestRun;3=LossOfComms;4=Soc;5=AcLoad;6=BatteryCurrent;7=BatteryVoltage;8=InverterTemperatur;9=InverterOverload;10=StopOnAc1,3501,uint16,1,R -com.victronenergy.generator,/Runtime,i,seconds,3502,uint16,1,R -com.victronenergy.generator,/QuietHours,i,0=Quiet hours inactive; 1=Quiet hours active,3503,uint16,1,R -com.victronenergy.generator,/Runtime,u,seconds,3504,uint32,1,R -com.victronenergy.generator,/State,i,0=Stopped;1=Running;10=Error,3506,uint16,1,R -com.victronenergy.generator,/Error,i,0=No Error;1=Remote disabled;2=Remote fault,3507,uint16,1,R -com.victronenergy.generator,/Alarms/NoGeneratorAtAcIn,i,0=No Alarm;2=Alarm,3508,uint16,1,R -com.victronenergy.generator,/AutoStartEnabled,i,0=Autostart disabled;1=Autostart enabled,3509,uint16,1,W -com.victronenergy.meteo,/Irradiance,d,W/m^2,3600,uint16,10,R -com.victronenergy.meteo,/WindSpeed,d,m/s,3601,uint16,10,R -com.victronenergy.meteo,/CellTemperature,d,Degrees celsius,3602,int16,10,R -com.victronenergy.meteo,/ExternalTemperature,d,Degrees celsius,3603,int16,10,R -com.victronenergy.evcharger,/ProductId,u,,3800,uint16,1,R -com.victronenergy.evcharger,/FirmwareVersion,u,,3802,uint32,1,R -com.victronenergy.evcharger,/Serial,s,,3804,string[6],,R -com.victronenergy.evcharger,/Model,s,,3810,string[4],,R -com.victronenergy.evcharger,/MaxCurrent,d,A,3814,uint16,1,W -com.victronenergy.evcharger,/Mode,u,0=Manual;1=Auto,3815,uint16,1,W -com.victronenergy.evcharger,/Ac/Energy/Forward,d,kWh,3816,uint32,100,R -com.victronenergy.evcharger,/Ac/L1/Power,d,W,3818,uint16,1,R -com.victronenergy.evcharger,/Ac/L2/Power,d,W,3819,uint16,1,R -com.victronenergy.evcharger,/Ac/L3/Power,d,W,3820,uint16,1,R -com.victronenergy.evcharger,/Ac/Power,d,W,3821,uint16,1,R -com.victronenergy.evcharger,/ChargingTime,d,s,3822,uint16,0.01,R -com.victronenergy.evcharger,/Current,d,A,3823,uint16,1,W -com.victronenergy.evcharger,/Status,u,0=Disconnected; 1=Connected; 2=Charging; 3=Charged; 4=Waiting for sun; 5=Waiting for RFID; 6=Waiting for start; 7=Low SOC; 8=Ground fault; 9=Welded contacts; 10=CP Input shorted; 11=Residual current detected; 12=Under voltage detected; 13=Overvoltage detected; 14=Overheating detected,3824,uint16,1,R -com.victronenergy.evcharger,/SetCurrent,d,A,3825,uint16,1,W -com.victronenergy.evcharger,/StartStop,u,0=Stop;1=Start,3826,uint16,1,W -com.victronenergy.evcharger,/Position,i,0=AC input 1;1=AC output;2=AC input 2,3827,uint16,1,W -com.victronenergy.acload,/Ac/L1/Power,d,W,3900,int16,1,R -com.victronenergy.acload,/Ac/L2/Power,d,W,3901,int16,1,R -com.victronenergy.acload,/Ac/L3/Power,d,W,3902,int16,1,R -com.victronenergy.acload,/Serial,s,,3903,string[7],,R -com.victronenergy.acload,/Ac/L1/Voltage,d,V AC,3910,uint16,10,R -com.victronenergy.acload,/Ac/L1/Current,d,A AC,3911,int16,10,R -com.victronenergy.acload,/Ac/L2/Voltage,d,V AC,3912,uint16,10,R -com.victronenergy.acload,/Ac/L2/Current,d,A AC,3913,int16,10,R -com.victronenergy.acload,/Ac/L3/Voltage,d,V AC,3914,uint16,10,R -com.victronenergy.acload,/Ac/L3/Current,d,A AC,3915,int16,10,R -com.victronenergy.acload,/Ac/L1/Energy/Forward,d,kWh,3916,uint32,100,R -com.victronenergy.acload,/Ac/L2/Energy/Forward,d,kWh,3918,uint32,100,R -com.victronenergy.acload,/Ac/L3/Energy/Forward,d,kWh,3920,uint32,100,R -com.victronenergy.fuelcell,/Dc/0/Voltage,d,V DC,4000,uint16,100,R -com.victronenergy.fuelcell,/Dc/0/Current,d,A DC,4001,int16,10,R -com.victronenergy.fuelcell,/Dc/1/Voltage,d,V DC,4002,int16,10,R -com.victronenergy.fuelcell,/Dc/0/Temperature,d,Degrees celsius,4003,int16,10,R -com.victronenergy.fuelcell,/History/EnergyOut,d,kWh,4004,uint32,100,R -com.victronenergy.fuelcell,/Alarms/LowVoltage,u,0=No alarm;2=Alarm,4006,uint16,1,R -com.victronenergy.fuelcell,/Alarms/HighVoltage,u,0=No alarm;2=Alarm,4007,uint16,1,R -com.victronenergy.fuelcell,/Alarms/LowStarterVoltage,u,0=No alarm;2=Alarm,4008,uint16,1,R -com.victronenergy.fuelcell,/Alarms/HighStarterVoltage,u,0=No alarm;2=Alarm,4009,uint16,1,R -com.victronenergy.fuelcell,/Alarms/LowTemperature,u,0=No alarm;2=Alarm,4010,uint16,1,R -com.victronenergy.fuelcell,/Alarms/HighTemperature,u,0=No alarm;2=Alarm,4011,uint16,1,R -com.victronenergy.alternator,/Dc/0/Voltage,d,V DC,4100,uint16,100,R -com.victronenergy.alternator,/Dc/0/Current,d,A DC,4101,int16,10,R -com.victronenergy.alternator,/Dc/1/Voltage,d,V DC,4102,int16,10,R -com.victronenergy.alternator,/Dc/0/Temperature,d,Degrees celsius,4103,int16,10,R -com.victronenergy.alternator,/History/EnergyOut,d,kWh,4104,uint32,100,R -com.victronenergy.alternator,/Alarms/LowVoltage,u,0=No alarm;2=Alarm,4106,uint16,1,R -com.victronenergy.alternator,/Alarms/HighVoltage,u,0=No alarm;2=Alarm,4107,uint16,1,R -com.victronenergy.alternator,/Alarms/LowStarterVoltage,u,0=No alarm;2=Alarm,4108,uint16,1,R -com.victronenergy.alternator,/Alarms/HighStarterVoltage,u,0=No alarm;2=Alarm,4109,uint16,1,R -com.victronenergy.alternator,/Alarms/LowTemperature,u,0=No alarm;2=Alarm,4110,uint16,1,R -com.victronenergy.alternator,/Alarms/HighTemperature,u,0=No alarm;2=Alarm,4111,uint16,1,R -com.victronenergy.alternator,/State,u,0=Off;2=Fault;3=Bulk;4=Absorption;5=Float;6=Storage;7=Equalize;252=External control,4112,uint16,1,R -com.victronenergy.alternator,/ErrorCode,u,12=High battery temperature;13=High battery voltage;14=Low battery voltage;15=VBat exceeds $CPB;21=High alternator temperature;22=Alternator overspeed;24=Internal error;41=High field FET temperature;42=Sensor missing;43=Low VAlt;44=High Voltage offset;45=VAlt exceeds $CPB;51-52=Battery disconnect request;53=Battery instance out of range;54=Too many BMSes;55=AEBus fault;56=Too many Victron devices;58-61=Battery requested disconnection;91=BMS lost;92=Forced idle;201=DCDC converter fail;201-207=DCDC error,4113,uint16,1,R -com.victronenergy.alternator,/Engine/Speed,d,RPM,4114,uint16,1,R -com.victronenergy.alternator,/Speed,d,RPM,4115,uint16,1,R -com.victronenergy.alternator,/FieldDrive,q,%,4116,uint16,1,R -com.victronenergy.dcsource,/Dc/0/Voltage,d,V DC,4200,uint16,100,R -com.victronenergy.dcsource,/Dc/0/Current,d,A DC,4201,int16,10,R -com.victronenergy.dcsource,/Dc/1/Voltage,d,V DC,4202,int16,10,R -com.victronenergy.dcsource,/Dc/0/Temperature,d,Degrees centigrade,4203,int16,10,R -com.victronenergy.dcsource,/History/EnergyOut,d,kWh,4204,uint32,100,R -com.victronenergy.dcsource,/Alarms/LowVoltage,u,0=No alarm;2=Alarm,4206,uint16,1,R -com.victronenergy.dcsource,/Alarms/HighVoltage,u,0=No alarm;2=Alarm,4207,uint16,1,R -com.victronenergy.dcsource,/Alarms/LowStarterVoltage,u,0=No alarm;2=Alarm,4208,uint16,1,R -com.victronenergy.dcsource,/Alarms/HighStarterVoltage,u,0=No alarm;2=Alarm,4209,uint16,1,R -com.victronenergy.dcsource,/Alarms/LowTemperature,u,0=No alarm;2=Alarm,4210,uint16,1,R -com.victronenergy.dcsource,/Alarms/HighTemperature,u,0=No alarm;2=Alarm,4211,uint16,1,R -com.victronenergy.dcload,/Dc/0/Voltage,d,V DC,4300,uint16,100,R -com.victronenergy.dcload,/Dc/0/Current,d,A DC,4301,int16,10,R -com.victronenergy.dcload,/Dc/1/Voltage,d,V DC,4302,int16,10,R -com.victronenergy.dcload,/Dc/0/Temperature,d,Degrees centigrade,4303,int16,10,R -com.victronenergy.dcload,/History/EnergyIn,d,kWh,4304,uint32,100,R -com.victronenergy.dcload,/Alarms/LowVoltage,u,0=No alarm;2=Alarm,4306,uint16,1,R -com.victronenergy.dcload,/Alarms/HighVoltage,u,0=No alarm;2=Alarm,4307,uint16,1,R -com.victronenergy.dcload,/Alarms/LowStarterVoltage,u,0=No alarm;2=Alarm,4308,uint16,1,R -com.victronenergy.dcload,/Alarms/HighStarterVoltage,u,0=No alarm;2=Alarm,4309,uint16,1,R -com.victronenergy.dcload,/Alarms/LowTemperature,u,0=No alarm;2=Alarm,4310,uint16,1,R -com.victronenergy.dcload,/Alarms/HighTemperature,u,0=No alarm;2=Alarm,4311,uint16,1,R -com.victronenergy.dcsystem,/Dc/0/Voltage,d,V DC,4400,uint16,100,R -com.victronenergy.dcsystem,/Dc/0/Current,d,A DC,4401,int16,10,R -com.victronenergy.dcsystem,/Dc/1/Voltage,d,V DC,4402,int16,10,R -com.victronenergy.dcsystem,/Dc/0/Temperature,d,Degrees centigrade,4403,int16,10,R -com.victronenergy.dcsystem,/History/EnergyOut,d,kWh,4404,uint32,100,R -com.victronenergy.dcsystem,/History/EnergyIn,d,kWh,4406,uint32,100,R -com.victronenergy.dcsystem,/Alarms/LowVoltage,u,0=No alarm;2=Alarm,4408,uint16,1,R -com.victronenergy.dcsystem,/Alarms/HighVoltage,u,0=No alarm;2=Alarm,4409,uint16,1,R -com.victronenergy.dcsystem,/Alarms/LowStarterVoltage,u,0=No alarm;2=Alarm,4410,uint16,1,R -com.victronenergy.dcsystem,/Alarms/HighStarterVoltage,u,0=No alarm;2=Alarm,4411,uint16,1,R -com.victronenergy.dcsystem,/Alarms/LowTemperature,u,0=No alarm;2=Alarm,4412,uint16,1,R -com.victronenergy.dcsystem,/Alarms/HighTemperature,u,0=No alarm;2=Alarm,4413,uint16,1,R -com.victronenergy.multi,/Ac/In/1/L1/V,d,V AC,4500,uint16,10,R -com.victronenergy.multi,/Ac/In/1/L2/V,d,V AC,4501,uint16,10,R -com.victronenergy.multi,/Ac/In/1/L3/V,d,V AC,4502,uint16,10,R -com.victronenergy.multi,/Ac/In/1/L1/I,d,A AC,4503,uint16,10,R -com.victronenergy.multi,/Ac/In/1/L2/I,d,A AC,4504,uint16,10,R -com.victronenergy.multi,/Ac/In/1/L3/I,d,A AC,4505,uint16,10,R -com.victronenergy.multi,/Ac/In/1/L1/P,d,W,4506,int16,0.1,R -com.victronenergy.multi,/Ac/In/1/L2/P,d,W,4507,int16,0.1,R -com.victronenergy.multi,/Ac/In/1/L3/P,d,W,4508,int16,0.1,R -com.victronenergy.multi,/Ac/In/1/L1/F,d,Hz,4509,uint16,100,R -com.victronenergy.multi,/Ac/Out/L1/V,d,V AC,4510,uint16,10,R -com.victronenergy.multi,/Ac/Out/L2/V,d,V AC,4511,uint16,10,R -com.victronenergy.multi,/Ac/Out/L3/V,d,V AC,4512,uint16,10,R -com.victronenergy.multi,/Ac/Out/L1/I,d,A AC,4513,uint16,10,R -com.victronenergy.multi,/Ac/Out/L2/I,d,A AC,4514,uint16,10,R -com.victronenergy.multi,/Ac/Out/L3/I,d,A AC,4515,uint16,10,R -com.victronenergy.multi,/Ac/Out/L1/P,d,W,4516,int16,0.1,R -com.victronenergy.multi,/Ac/Out/L2/P,d,W,4517,int16,0.1,R -com.victronenergy.multi,/Ac/Out/L3/P,d,W,4518,int16,0.1,R -com.victronenergy.multi,/Ac/Out/L1/F,d,Hz,4519,uint16,100,R -com.victronenergy.multi,/Ac/In/1/Type,u,0=Unused;1=Grid;2=Genset;3=Shore,4520,uint16,1,R -com.victronenergy.multi,/Ac/In/2/Type,u,0=Unused;1=Grid;2=Genset;3=Shore,4521,uint16,1,R -com.victronenergy.multi,/Ac/In/1/CurrentLimit,d,A,4522,uint16,10,W -com.victronenergy.multi,/Ac/In/2/CurrentLimit,d,A,4523,uint16,10,W -com.victronenergy.multi,/Ac/NumberOfPhases,u,count,4524,uint16,1,R -com.victronenergy.multi,/Ac/ActiveIn/ActiveInput,u,0=AC Input 1;1=AC Input 2;240=Disconnected,4525,uint16,1,R -com.victronenergy.multi,/Dc/0/Voltage,d,V DC,4526,uint16,100,R -com.victronenergy.multi,/Dc/0/Current,d,A DC,4527,int16,10,R -com.victronenergy.multi,/Dc/0/Temperature,d,Degrees celsius,4528,int16,10,R -com.victronenergy.multi,/Soc,d,%,4529,uint16,10,R -com.victronenergy.multi,/State,u,0=Off;1=Low Power;2=Fault;3=Bulk;4=Absorption;5=Float;6=Storage;7=Equalize;8=Passthru;9=Inverting;10=Power assist;11=Power supply;252=Bulk protection,4530,uint16,1,R -com.victronenergy.multi,/Mode,u,1=Charger Only;2=Inverter Only;3=On;4=Off,4531,uint16,1,W -com.victronenergy.multi,/Alarms/HighTemperature,u,0=Ok;1=Warning;2=Alarm,4532,uint16,1,R -com.victronenergy.multi,/Alarms/HighVoltage,u,0=Ok;1=Warning;2=Alarm,4533,uint16,1,R -com.victronenergy.multi,/Alarms/HighVoltageAcOut,u,0=Ok;1=Warning;2=Alarm,4534,uint16,1,R -com.victronenergy.multi,/Alarms/LowTemperature,u,0=Ok;1=Warning;2=Alarm,4535,uint16,1,R -com.victronenergy.multi,/Alarms/LowVoltage,u,0=Ok;1=Warning;2=Alarm,4536,uint16,1,R -com.victronenergy.multi,/Alarms/LowVoltageAcOut,u,0=Ok;1=Warning;2=Alarm,4537,uint16,1,R -com.victronenergy.multi,/Alarms/Overload,u,0=Ok;1=Warning;2=Alarm,4538,uint16,1,R -com.victronenergy.multi,/Alarms/Ripple,u,0=Ok;1=Warning;2=Alarm,4539,uint16,1,R -com.victronenergy.multi,/Yield/Power,d,W,4540,uint16,1,R -com.victronenergy.multi,/Yield/User,d,kWh,4541,uint16,10,R -com.victronenergy.multi,/Relay/0/State,i,0=Open;1=Closed,4542,uint16,1,R -com.victronenergy.multi,/MppOperationMode,i,0=Off;1=Voltage/current limited;2=MPPT active;255=Not available,4543,uint16,1,R -com.victronenergy.multi,/Pv/V,d,V DC,4544,uint16,10,R -com.victronenergy.multi,/ErrorCode,i,0=No error;1=Battery temperature too high;2=Battery voltage too high;3=Battery temperature sensor miswired (+);4=Battery temperature sensor miswired (-);5=Battery temperature sensor disconnected;6=Battery voltage sense miswired (+);7=Battery voltage sense miswired (-);8=Battery voltage sense disconnected;9=Battery voltage wire losses too high;17=Charger temperature too high;18=Charger over-current;19=Charger current polarity reversed;20=Bulk time limit reached;22=Charger temperature sensor miswired;23=Charger temperature sensor disconnected;34=Input current too high,4545,uint16,1,R -com.victronenergy.multi,/Energy/AcIn1ToAcOut,d,kWh,4546,uint32,100,R -com.victronenergy.multi,/Energy/AcIn1ToInverter,d,kWh,4548,uint32,100,R -com.victronenergy.multi,/Energy/AcIn2ToAcOut,d,kWh,4550,uint32,100,R -com.victronenergy.multi,/Energy/AcIn2ToInverter,d,kWh,4552,uint32,100,R -com.victronenergy.multi,/Energy/AcOutToAcIn1,d,kWh,4554,uint32,100,R -com.victronenergy.multi,/Energy/AcOutToAcIn2,d,kWh,4556,uint32,100,R -com.victronenergy.multi,/Energy/InverterToAcIn1,d,kWh,4558,uint32,100,R -com.victronenergy.multi,/Energy/InverterToAcIn2,d,kWh,4560,uint32,100,R -com.victronenergy.multi,/Energy/InverterToAcOut,d,kWh,4562,uint32,100,R -com.victronenergy.multi,/Energy/OutToInverter,d,kWh,4564,uint32,100,R -com.victronenergy.multi,/Energy/SolarToAcIn1,d,kWh,4566,uint32,100,R -com.victronenergy.multi,/Energy/SolarToAcIn2,d,kWh,4568,uint32,100,R -com.victronenergy.multi,/Energy/SolarToAcOut,d,kWh,4570,uint32,100,R -com.victronenergy.multi,/Energy/SolarToBattery,d,kWh,4572,uint32,100,R -com.victronenergy.multi,/History/Daily/0/Yield,d,kWh,4574,uint16,10,R -com.victronenergy.multi,/History/Daily/0/MaxPower,d,W,4575,uint16,1,R -com.victronenergy.multi,/History/Daily/1/Yield,d,kWh,4576,uint16,10,R -com.victronenergy.multi,/History/Daily/1/MaxPower,d,W,4577,uint16,1,R -com.victronenergy.multi,/History/Daily/0/Pv/0/Yield,d,kWh,4578,uint16,10,R -com.victronenergy.multi,/History/Daily/0/Pv/1/Yield,d,kWh,4579,uint16,10,R -com.victronenergy.multi,/History/Daily/0/Pv/2/Yield,d,kWh,4580,uint16,10,R -com.victronenergy.multi,/History/Daily/0/Pv/3/Yield,d,kWh,4581,uint16,10,R -com.victronenergy.multi,/History/Daily/1/Pv/0/Yield,d,kWh,4582,uint16,10,R -com.victronenergy.multi,/History/Daily/1/Pv/1/Yield,d,kWh,4583,uint16,10,R -com.victronenergy.multi,/History/Daily/1/Pv/2/Yield,d,kWh,4584,uint16,10,R -com.victronenergy.multi,/History/Daily/1/Pv/3/Yield,d,kWh,4585,uint16,10,R -com.victronenergy.multi,/History/Daily/0/Pv/0/MaxPower,d,W,4586,uint16,1,R -com.victronenergy.multi,/History/Daily/0/Pv/1/MaxPower,d,W,4587,uint16,1,R -com.victronenergy.multi,/History/Daily/0/Pv/2/MaxPower,d,W,4588,uint16,1,R -com.victronenergy.multi,/History/Daily/0/Pv/3/MaxPower,d,W,4589,uint16,1,R -com.victronenergy.multi,/History/Daily/1/Pv/0/MaxPower,d,W,4590,uint16,1,R -com.victronenergy.multi,/History/Daily/1/Pv/1/MaxPower,d,W,4591,uint16,1,R -com.victronenergy.multi,/History/Daily/1/Pv/2/MaxPower,d,W,4592,uint16,1,R -com.victronenergy.multi,/History/Daily/1/Pv/3/MaxPower,d,W,4593,uint16,1,R -com.victronenergy.multi,/Pv/0/V,d,V DC,4594,uint16,10,R -com.victronenergy.multi,/Pv/1/V,d,V DC,4595,uint16,10,R -com.victronenergy.multi,/Pv/2/V,d,V DC,4596,uint16,10,R -com.victronenergy.multi,/Pv/3/V,d,V DC,4597,uint16,10,R -com.victronenergy.multi,/Pv/0/P,d,W,4598,uint16,1,R -com.victronenergy.multi,/Pv/1/P,d,W,4599,uint16,1,R -com.victronenergy.multi,/Pv/2/P,d,W,4600,uint16,1,R -com.victronenergy.multi,/Pv/3/P,d,W,4601,uint16,1,R -com.victronenergy.multi,/Alarms/LowSoc,u,,4602,uint16,1,R diff --git a/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v2.73 b/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v2.73 deleted file mode 100755 index ce8e13b1..00000000 --- a/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v2.73 +++ /dev/null @@ -1,1127 +0,0 @@ -#!/usr/bin/python -u -# -*- coding: utf-8 -*- - -#### modified for GuiMods - -from dbus.mainloop.glib import DBusGMainLoop -import dbus -import gobject -import argparse -import sys -import os -import json -from itertools import chain - -# Victron packages -sys.path.insert(1, os.path.join(os.path.dirname(__file__), 'ext', 'velib_python')) -from vedbus import VeDbusService -from ve_utils import get_vrm_portal_id, exit_on_error -from dbusmonitor import DbusMonitor -from settingsdevice import SettingsDevice -from logger import setup_logging -import delegates -from sc_utils import safeadd as _safeadd, safemax as _safemax - -softwareVersion = '2.67' - -class SystemCalc: - STATE_IDLE = 0 - STATE_CHARGING = 1 - STATE_DISCHARGING = 2 - BATSERVICE_DEFAULT = 'default' - BATSERVICE_NOBATTERY = 'nobattery' - def __init__(self): - # Why this dummy? Because DbusMonitor expects these values to be there, even though we don't - # need them. So just add some dummy data. This can go away when DbusMonitor is more generic. - dummy = {'code': None, 'whenToLog': 'configChange', 'accessLevel': None} - dbus_tree = { - 'com.victronenergy.solarcharger': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Load/I': dummy, - '/FirmwareVersion': dummy}, - 'com.victronenergy.pvinverter': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, - '/Position': dummy, - '/ProductId': dummy}, - 'com.victronenergy.battery': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/DeviceInstance': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/1/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Soc': dummy, - '/Sense/Current': dummy, - '/TimeToGo': dummy, - '/ConsumedAmphours': dummy, - '/ProductId': dummy, - '/CustomName': dummy}, - 'com.victronenergy.vebus' : { - '/Ac/ActiveIn/ActiveInput': dummy, - '/Ac/ActiveIn/L1/P': dummy, - '/Ac/ActiveIn/L2/P': dummy, - '/Ac/ActiveIn/L3/P': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L2/P': dummy, - '/Ac/Out/L3/P': dummy, -#### add for GuiMods - '/Ac/Out/L1/I': dummy, - '/Ac/Out/L2/I': dummy, - '/Ac/Out/L3/I': dummy, - '/Ac/Out/L1/V': dummy, - '/Ac/Out/L2/V': dummy, - '/Ac/Out/L3/V': dummy, - '/Ac/Out/L1/F': dummy, - '/Ac/Out/L2/F': dummy, - '/Ac/Out/L3/F': dummy, - '/Ac/ActiveIn/L1/V': dummy, - '/Ac/ActiveIn/L2/V': dummy, - '/Ac/ActiveIn/L3/V': dummy, - '/Ac/ActiveIn/L1/F': dummy, - '/Ac/ActiveIn/L2/F': dummy, - '/Ac/ActiveIn/L3/F': dummy, - - '/Connected': dummy, - '/ProductId': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Mode': dummy, - '/State': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.charger': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/1/Voltage': dummy, - '/Dc/1/Current': dummy, - '/Dc/2/Voltage': dummy, - '/Dc/2/Current': dummy}, - 'com.victronenergy.grid' : { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/ProductId' : dummy, - '/DeviceType' : dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, -#### add for GuiMods - '/Ac/L1/Current': dummy, - '/Ac/L2/Current': dummy, - '/Ac/L3/Current': dummy, - '/Ac/L1/Voltage': dummy, - '/Ac/L2/Voltage': dummy, - '/Ac/L3/Voltage': dummy, - '/Ac/L1/Frequency': dummy, - '/Ac/L2/Frequency': dummy, - '/Ac/L3/Frequency': dummy}, - 'com.victronenergy.genset' : { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/ProductId' : dummy, - '/DeviceType' : dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, -#### add for GuiMods - '/Ac/L1/Current': dummy, - '/Ac/L2/Current': dummy, - '/Ac/L3/Current': dummy, - '/Ac/L1/Voltage': dummy, - '/Ac/L2/Voltage': dummy, - '/Ac/L3/Voltage': dummy, - '/Ac/L1/Frequency': dummy, - '/Ac/L2/Frequency': dummy, - '/Ac/L3/Frequency': dummy, - - '/StarterVoltage': dummy}, - 'com.victronenergy.settings' : { - '/Settings/SystemSetup/AcInput1' : dummy, - '/Settings/SystemSetup/AcInput2' : dummy, - '/Settings/CGwacs/RunWithoutGridMeter' : dummy, - '/Settings/System/TimeZone' : dummy}, - 'com.victronenergy.temperature': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy}, - 'com.victronenergy.inverter': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L1/V': dummy, - '/Ac/Out/L1/I': dummy, -#### add for GuiMods - '/Ac/Out/L1/V': dummy, - '/Ac/Out/L1/I': dummy, - '/Ac/Out/L1/F': dummy, - - '/Yield/Power': dummy, - '/Soc': dummy, - } - } - - self._modules = [ - delegates.HubTypeSelect(), - delegates.VebusSocWriter(), - delegates.ServiceMapper(), - delegates.RelayState(), - delegates.BuzzerControl(), - delegates.LgCircuitBreakerDetect(), - delegates.Dvcc(self), - delegates.BatterySense(self), - delegates.BatterySettings(self), - delegates.SystemState(), - delegates.BatteryLife(), - delegates.ScheduledCharging(), - delegates.SourceTimers(), - #delegates.BydCurrentSense(self), - delegates.BatteryData(), - delegates.Gps()] - - for m in self._modules: - for service, paths in m.get_input(): - s = dbus_tree.setdefault(service, {}) - for path in paths: - s[path] = dummy - - self._dbusmonitor = self._create_dbus_monitor(dbus_tree, valueChangedCallback=self._dbus_value_changed, - deviceAddedCallback=self._device_added, deviceRemovedCallback=self._device_removed) - - # Connect to localsettings - supported_settings = { - 'batteryservice': ['/Settings/SystemSetup/BatteryService', self.BATSERVICE_DEFAULT, 0, 0], - 'hasdcsystem': ['/Settings/SystemSetup/HasDcSystem', 0, 0, 1], - 'useacout': ['/Settings/SystemSetup/HasAcOutSystem', 1, 0, 1]} - - for m in self._modules: - for setting in m.get_settings(): - supported_settings[setting[0]] = list(setting[1:]) - - self._settings = self._create_settings(supported_settings, self._handlechangedsetting) - - self._dbusservice = self._create_dbus_service() - - for m in self._modules: - m.set_sources(self._dbusmonitor, self._settings, self._dbusservice) - - # This path does nothing except respond with a PropertiesChanged so - # that round-trip time can be measured. - self._dbusservice.add_path('/Ping', value=None, writeable=True) - - # At this moment, VRM portal ID is the MAC address of the CCGX. Anyhow, it should be string uniquely - # identifying the CCGX. - self._dbusservice.add_path('/Serial', value=get_vrm_portal_id()) - self._dbusservice.add_path( - '/AvailableBatteryServices', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/AvailableBatteryMeasurements', value=None) - self._dbusservice.add_path( - '/AutoSelectedBatteryService', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/AutoSelectedBatteryMeasurement', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/ActiveBatteryService', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/Dc/Battery/BatteryService', value=None) - self._dbusservice.add_path( - '/PvInvertersProductIds', value=None) - self._summeditems = { - '/Ac/Grid/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Grid/ProductId': {'gettext': '%s'}, - '/Ac/Grid/DeviceType': {'gettext': '%s'}, - '/Ac/Genset/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Genset/ProductId': {'gettext': '%s'}, - '/Ac/Genset/DeviceType': {'gettext': '%s'}, - '/Ac/ConsumptionOnOutput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Consumption/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/L1/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/L2/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/L3/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/L1/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/L2/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/L3/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/NumberOfPhases': {'gettext': '%d'}, - '/Dc/Pv/Power': {'gettext': '%.0F W'}, - '/Dc/Pv/Current': {'gettext': '%.1F A'}, - '/Dc/Battery/Voltage': {'gettext': '%.2F V'}, - '/Dc/Battery/VoltageService': {'gettext': '%s'}, - '/Dc/Battery/Current': {'gettext': '%.1F A'}, - '/Dc/Battery/Power': {'gettext': '%.0F W'}, - '/Dc/Battery/Soc': {'gettext': '%.0F %%'}, - '/Dc/Battery/State': {'gettext': '%s'}, - '/Dc/Battery/TimeToGo': {'gettext': '%.0F s'}, - '/Dc/Battery/ConsumedAmphours': {'gettext': '%.1F Ah'}, - '/Dc/Battery/ProductId': {'gettext': '0x%x'}, - '/Dc/Charger/Power': {'gettext': '%.0F %%'}, - '/Dc/Vebus/Current': {'gettext': '%.1F A'}, - '/Dc/Vebus/Power': {'gettext': '%.0F W'}, - '/Dc/System/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/Source': {'gettext': '%s'}, - '/VebusService': {'gettext': '%s'}, -#### added for GuiMods - '/Ac/Grid/L1/Current': {'gettext': '%.1F A'}, - '/Ac/Grid/L2/Current': {'gettext': '%.1F A'}, - '/Ac/Grid/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Grid/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/Grid/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/Grid/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/Grid/Frequency': {'gettext': '%.1F Hz'}, - '/Ac/Genset/L1/Current': {'gettext': '%.1F A'}, - '/Ac/Genset/L2/Current': {'gettext': '%.1F A'}, - '/Ac/Genset/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Genset/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/Genset/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/Genset/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/Genset/Frequency': {'gettext': '%.1F Hz'}, - '/Ac/ConsumptionOnOutput/L1/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnOutput/L2/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnOutput/L3/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnOutput/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnOutput/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnOutput/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnOutput/Frequency': {'gettext': '%.1F Hz'}, - '/Ac/ConsumptionOnInput/L1/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnInput/L2/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnInput/L3/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnInput/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnInput/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnInput/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnInput/Frequency': {'gettext': '%.1F Hz'}, - '/Ac/Consumption/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/Consumption/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/Consumption/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/Consumption/L1/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/L2/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/Frequency': {'gettext': '%.1F Hz'}, - '/Ac/ActiveIn/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/ActiveIn/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/ActiveIn/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/ActiveIn/L1/Current': {'gettext': '%.1F A'}, - '/Ac/ActiveIn/L2/Current': {'gettext': '%.1F A'}, - '/Ac/ActiveIn/L3/Current': {'gettext': '%.1F A'}, - '/Ac/ActiveIn/Frequency': {'gettext': '%.1F Hz'}, - } - - for m in self._modules: - self._summeditems.update(m.get_output()) - - for path in self._summeditems.keys(): - self._dbusservice.add_path(path, value=None, gettextcallback=self._gettext) - - self._batteryservice = None - self._determinebatteryservice() - - if self._batteryservice is None: - logger.info("Battery service initialized to None (setting == %s)" % - self._settings['batteryservice']) - - self._changed = True - for service, instance in self._dbusmonitor.get_service_list().items(): - self._device_added(service, instance, do_service_change=False) - - self._handleservicechange() - self._updatevalues() - - gobject.timeout_add(1000, exit_on_error, self._handletimertick) - - 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): - raise Exception("This function should be overridden") - - def _handlechangedsetting(self, setting, oldvalue, newvalue): - self._determinebatteryservice() - self._changed = True - - # Give our delegates a chance to react on a settings change - for m in self._modules: - m.settings_changed(setting, oldvalue, newvalue) - - def _find_device_instance(self, serviceclass, instance): - """ Gets a mapping of services vs DeviceInstance using - get_service_list. Then searches for the specified DeviceInstance - and returns the service name. """ - services = self._dbusmonitor.get_service_list(classfilter=serviceclass) - - # According to https://www.python.org/dev/peps/pep-3106/, dict.keys() - # and dict.values() always have the same order. This is also much - # faster than you would expect. - try: - return services.keys()[services.values().index(instance)] - except ValueError: # If instance not in values - return None - - def _determinebatteryservice(self): - auto_battery_service = self._autoselect_battery_service() - auto_battery_measurement = None - if auto_battery_service is not None: - services = self._dbusmonitor.get_service_list() - if auto_battery_service in services: - auto_battery_measurement = \ - self._get_instance_service_name(auto_battery_service, services[auto_battery_service]) - auto_battery_measurement = auto_battery_measurement.replace('.', '_').replace('/', '_') + '/Dc/0' - self._dbusservice['/AutoSelectedBatteryMeasurement'] = auto_battery_measurement - - if self._settings['batteryservice'] == self.BATSERVICE_DEFAULT: - newbatteryservice = auto_battery_service - self._dbusservice['/AutoSelectedBatteryService'] = ( - 'No battery monitor found' if newbatteryservice is None else - self._get_readable_service_name(newbatteryservice)) - - elif self._settings['batteryservice'] == self.BATSERVICE_NOBATTERY: - self._dbusservice['/AutoSelectedBatteryService'] = None - newbatteryservice = None - - else: - self._dbusservice['/AutoSelectedBatteryService'] = None - - s = self._settings['batteryservice'].split('/') - if len(s) != 2: - logger.error("The battery setting (%s) is invalid!" % self._settings['batteryservice']) - serviceclass = s[0] - instance = int(s[1]) if len(s) == 2 else None - - # newbatteryservice might turn into None if a chosen battery - # monitor no longer exists. Don't auto change the setting (it might - # come back) and don't autoselect another. - newbatteryservice = self._find_device_instance(serviceclass, instance) - - if newbatteryservice != self._batteryservice: - services = self._dbusmonitor.get_service_list() - instance = services.get(newbatteryservice, None) - if instance is None: - battery_service = None - else: - battery_service = self._get_instance_service_name(newbatteryservice, instance) - self._dbusservice['/ActiveBatteryService'] = battery_service - logger.info("Battery service, setting == %s, changed from %s to %s (%s)" % - (self._settings['batteryservice'], self._batteryservice, newbatteryservice, instance)) - - # Battery service has changed. Notify delegates. - for m in self._modules: - m.battery_service_changed(self._batteryservice, newbatteryservice) - self._dbusservice['/Dc/Battery/BatteryService'] = self._batteryservice = newbatteryservice - - def _autoselect_battery_service(self): - # Default setting business logic: - # first try to use a battery service (BMV or Lynx Shunt VE.Can). If there - # is more than one battery service, just use a random one. If no battery service is - # available, check if there are not Solar chargers and no normal chargers. If they are not - # there, assume this is a hub-2, hub-3 or hub-4 system and use VE.Bus SOC. - batteries = self._get_connected_service_list('com.victronenergy.battery') - - # Pick the first battery service - if len(batteries) > 0: - return sorted(batteries)[0] - - # No battery services, and there is a charger in the system. Abandon - # hope. - if self._get_first_connected_service('com.victronenergy.charger') is not None: - return None - - # Also no Multi, then give up. - vebus_service = self._get_service_having_lowest_instance('com.victronenergy.vebus') - if vebus_service is None: - # No VE.Bus, but maybe there is an inverter with built-in SOC - # tracking, eg RS Smart. - inverter = self._get_service_having_lowest_instance('com.victronenergy.inverter') - if inverter and self._dbusmonitor.get_value(inverter[0], '/Soc') is not None: - return inverter[0] - - return None - - # There is a Multi, and it supports tracking external charge current - # from solarchargers. Then use it. - if self._dbusmonitor.get_value(vebus_service[0], '/ExtraBatteryCurrent') is not None and self._settings['hasdcsystem'] == 0: - return vebus_service[0] - - # Multi does not support tracking solarcharger current, and we have - # solar chargers. Then we cannot use it. - if self._get_first_connected_service('com.victronenergy.solarcharger') is not None: - return None - - # Only a Multi, no other chargers. Then we can use it. - return vebus_service[0] - - # Called on a one second timer - def _handletimertick(self): - if self._changed: - self._updatevalues() - self._changed = False - - return True # keep timer running - - def _updatepvinverterspidlist(self): - # Create list of connected pv inverters id's - pvinverters = self._dbusmonitor.get_service_list('com.victronenergy.pvinverter') - productids = [] - - for pvinverter in pvinverters: - pid = self._dbusmonitor.get_value(pvinverter, '/ProductId') - if pid is not None and pid not in productids: - productids.append(pid) - self._dbusservice['/PvInvertersProductIds'] = productids - - def _updatevalues(self): - # ==== PREPARATIONS ==== - newvalues = {} - - # Set the user timezone - if 'TZ' not in os.environ: - tz = self._dbusmonitor.get_value('com.victronenergy.settings', '/Settings/System/TimeZone') - if tz is not None: - os.environ['TZ'] = tz - - # Determine values used in logic below - vebusses = self._dbusmonitor.get_service_list('com.victronenergy.vebus') - vebuspower = 0 - for vebus in vebusses: - v = self._dbusmonitor.get_value(vebus, '/Dc/0/Voltage') - i = self._dbusmonitor.get_value(vebus, '/Dc/0/Current') - if v is not None and i is not None: - vebuspower += v * i - - # ==== PVINVERTERS ==== - pvinverters = self._dbusmonitor.get_service_list('com.victronenergy.pvinverter') - pos = {0: '/Ac/PvOnGrid', 1: '/Ac/PvOnOutput', 2: '/Ac/PvOnGenset'} - for pvinverter in pvinverters: - # Position will be None if PV inverter service has just been removed (after retrieving the - # service list). - position = pos.get(self._dbusmonitor.get_value(pvinverter, '/Position')) - if position is not None: - for phase in range(1, 4): - power = self._dbusmonitor.get_value(pvinverter, '/Ac/L%s/Power' % phase) - if power is not None: - path = '%s/L%s/Power' % (position, phase) - newvalues[path] = _safeadd(newvalues.get(path), power) - - for path in pos.values(): - self._compute_number_of_phases(path, newvalues) - - # ==== SOLARCHARGERS ==== - solarchargers = self._dbusmonitor.get_service_list('com.victronenergy.solarcharger') - solarcharger_batteryvoltage = None - solarcharger_batteryvoltage_service = None - solarchargers_charge_power = 0 - solarchargers_loadoutput_power = None - - for solarcharger in solarchargers: - v = self._dbusmonitor.get_value(solarcharger, '/Dc/0/Voltage') - if v is None: - continue - i = self._dbusmonitor.get_value(solarcharger, '/Dc/0/Current') - if i is None: - continue - l = self._dbusmonitor.get_value(solarcharger, '/Load/I', 0) - - if l is not None: - if solarchargers_loadoutput_power is None: - solarchargers_loadoutput_power = l * v - else: - solarchargers_loadoutput_power += l * v - - solarchargers_charge_power += v * i - - # Note that this path is not in the _summeditems{}, making for it to not be - # published on D-Bus. Which fine. The only one needing it is the vebussocwriter- - # delegate. - if '/Dc/Pv/ChargeCurrent' not in newvalues: - newvalues['/Dc/Pv/ChargeCurrent'] = i - else: - newvalues['/Dc/Pv/ChargeCurrent'] += i - - if '/Dc/Pv/Power' not in newvalues: - newvalues['/Dc/Pv/Power'] = v * _safeadd(i, l) - newvalues['/Dc/Pv/Current'] = _safeadd(i, l) - solarcharger_batteryvoltage = v - solarcharger_batteryvoltage_service = solarcharger - else: - newvalues['/Dc/Pv/Power'] += v * _safeadd(i, l) - newvalues['/Dc/Pv/Current'] += _safeadd(i, l) - - # ==== CHARGERS ==== - chargers = self._dbusmonitor.get_service_list('com.victronenergy.charger') - charger_batteryvoltage = None - charger_batteryvoltage_service = None - for charger in chargers: - # Assume the battery connected to output 0 is the main battery - v = self._dbusmonitor.get_value(charger, '/Dc/0/Voltage') - if v is None: - continue - - charger_batteryvoltage = v - charger_batteryvoltage_service = charger - - i = self._dbusmonitor.get_value(charger, '/Dc/0/Current') - if i is None: - continue - - if '/Dc/Charger/Power' not in newvalues: - newvalues['/Dc/Charger/Power'] = v * i - else: - newvalues['/Dc/Charger/Power'] += v * i - - # ==== VE.Direct Inverters ==== - _vedirect_inverters = sorted((di, s) for s, di in self._dbusmonitor.get_service_list('com.victronenergy.inverter').items()) - vedirect_inverters = [x[1] for x in _vedirect_inverters] - vedirect_inverter = None - if vedirect_inverters: - vedirect_inverter = vedirect_inverters[0] - - # For RS Smart inverters, add PV to the yield - for i in vedirect_inverters: - pv_yield = self._dbusmonitor.get_value(i, "/Yield/Power") - if pv_yield is not None: - newvalues['/Dc/Pv/Power'] = newvalues.get('/Dc/Pv/Power', 0) + pv_yield - - - # ==== BATTERY ==== - if self._batteryservice is not None: - batteryservicetype = self._batteryservice.split('.')[2] - assert batteryservicetype in ('battery', 'vebus', 'inverter') - - newvalues['/Dc/Battery/Soc'] = self._dbusmonitor.get_value(self._batteryservice,'/Soc') - newvalues['/Dc/Battery/TimeToGo'] = self._dbusmonitor.get_value(self._batteryservice,'/TimeToGo') - newvalues['/Dc/Battery/ConsumedAmphours'] = self._dbusmonitor.get_value(self._batteryservice,'/ConsumedAmphours') - newvalues['/Dc/Battery/ProductId'] = self._dbusmonitor.get_value(self._batteryservice, '/ProductId') - - if batteryservicetype in ('battery', 'inverter'): - newvalues['/Dc/Battery/Voltage'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Voltage') - newvalues['/Dc/Battery/VoltageService'] = self._batteryservice - newvalues['/Dc/Battery/Current'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Current') - newvalues['/Dc/Battery/Power'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Power') - - elif batteryservicetype == 'vebus': - vebus_voltage = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Voltage') - vebus_current = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Current') - vebus_power = None if vebus_voltage is None or vebus_current is None else vebus_current * vebus_voltage - newvalues['/Dc/Battery/Voltage'] = vebus_voltage - newvalues['/Dc/Battery/VoltageService'] = self._batteryservice - if self._settings['hasdcsystem'] == 1: - # hasdcsystem will normally disqualify the multi from being - # auto-selected as battery monitor, so the only way we're - # here is if the user explicitly selected the multi as the - # battery service - newvalues['/Dc/Battery/Current'] = vebus_current - if vebus_power is not None: - newvalues['/Dc/Battery/Power'] = vebus_power - else: - battery_power = _safeadd(solarchargers_charge_power, vebus_power) - newvalues['/Dc/Battery/Current'] = battery_power / vebus_voltage if vebus_voltage > 0 else None - newvalues['/Dc/Battery/Power'] = battery_power - - - p = newvalues.get('/Dc/Battery/Power', None) - if p is not None: - if p > 30: - newvalues['/Dc/Battery/State'] = self.STATE_CHARGING - elif p < -30: - newvalues['/Dc/Battery/State'] = self.STATE_DISCHARGING - else: - newvalues['/Dc/Battery/State'] = self.STATE_IDLE - - else: - # The battery service is not a BMS/BMV or a suitable vebus. A - # suitable vebus is defined as one explicitly selected by the user, - # or one that was automatically selected for SOC tracking. We may - # however still have a VE.Bus, just not one that can accurately - # track SOC. If we have one, use it as voltage source. Otherwise - # try a solar charger, a charger, or a vedirect inverter as - # fallbacks. - batteryservicetype = None - vebusses = self._dbusmonitor.get_service_list('com.victronenergy.vebus') - for vebus in vebusses: - v = self._dbusmonitor.get_value(vebus, '/Dc/0/Voltage') - s = self._dbusmonitor.get_value(vebus, '/State') - if v is not None and s not in (0, None): - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = vebus - break # Skip the else below - else: - # No suitable vebus voltage, try other devices - if solarcharger_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = solarcharger_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = solarcharger_batteryvoltage_service - elif charger_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = charger_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = charger_batteryvoltage_service - elif vedirect_inverter is not None: - v = self._dbusmonitor.get_value(vedirect_inverter, '/Dc/0/Voltage') - if v is not None: - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = vedirect_inverter - - if self._settings['hasdcsystem'] == 0 and '/Dc/Battery/Voltage' in newvalues: - # No unmonitored DC loads or chargers, and also no battery monitor: derive battery watts - # and amps from vebus, solarchargers and chargers. - assert '/Dc/Battery/Power' not in newvalues - assert '/Dc/Battery/Current' not in newvalues - p = solarchargers_charge_power + newvalues.get('/Dc/Charger/Power', 0) + vebuspower - voltage = newvalues['/Dc/Battery/Voltage'] - newvalues['/Dc/Battery/Current'] = p / voltage if voltage > 0 else None - newvalues['/Dc/Battery/Power'] = p - - - # ==== SYSTEM POWER ==== - if self._settings['hasdcsystem'] == 1 and batteryservicetype == 'battery': - # Calculate power being generated/consumed by not measured devices in the network. - # For MPPTs, take all the power, including power going out of the load output. - # /Dc/System: positive: consuming power - # VE.Bus: Positive: current flowing from the Multi to the dc system or battery - # Solarcharger & other chargers: positive: charging - # battery: Positive: charging battery. - # battery = solarcharger + charger + ve.bus - system - - battery_power = newvalues.get('/Dc/Battery/Power') - if battery_power is not None: - dc_pv_power = newvalues.get('/Dc/Pv/Power', 0) - charger_power = newvalues.get('/Dc/Charger/Power', 0) - - # If there are VE.Direct inverters, remove their power from the - # DC estimate. This is done using the AC value when the DC - # power values are not available. - inverter_power = 0 - for i in vedirect_inverters: - inverter_current = self._dbusmonitor.get_value(i, '/Dc/0/Current') - if inverter_current is not None: - inverter_power += self._dbusmonitor.get_value( - i, '/Dc/0/Voltage', 0) * inverter_current - else: - inverter_power += self._dbusmonitor.get_value( - i, '/Ac/Out/L1/V', 0) * self._dbusmonitor.get_value( - i, '/Ac/Out/L1/I', 0) - newvalues['/Dc/System/Power'] = dc_pv_power + charger_power + vebuspower - inverter_power - battery_power - - elif self._settings['hasdcsystem'] == 1 and solarchargers_loadoutput_power is not None: - newvalues['/Dc/System/Power'] = solarchargers_loadoutput_power - - # ==== Vebus ==== - multi = self._get_service_having_lowest_instance('com.victronenergy.vebus') - multi_path = None - if multi is not None: - multi_path = multi[0] - dc_current = self._dbusmonitor.get_value(multi_path, '/Dc/0/Current') - newvalues['/Dc/Vebus/Current'] = dc_current - dc_power = self._dbusmonitor.get_value(multi_path, '/Dc/0/Power') - # Just in case /Dc/0/Power is not available - if dc_power == None and dc_current is not None: - dc_voltage = self._dbusmonitor.get_value(multi_path, '/Dc/0/Voltage') - if dc_voltage is not None: - dc_power = dc_voltage * dc_current - # Note that there is also vebuspower, which is the total DC power summed over all multis. - # However, this value cannot be combined with /Dc/Multi/Current, because it does not make sense - # to add the Dc currents of all multis if they do not share the same DC voltage. - newvalues['/Dc/Vebus/Power'] = dc_power - - newvalues['/VebusService'] = multi_path - - # ===== AC IN SOURCE ===== - ac_in_source = None - if multi_path is None: - # Check if we have an non-VE.Bus inverter. If yes, then ActiveInput - # is disconnected. - if vedirect_inverter is not None: - ac_in_source = 240 - else: - active_input = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/ActiveInput') - if active_input == 0xF0: - # Not connected - ac_in_source = 240 - elif active_input is not None: - settings_path = '/Settings/SystemSetup/AcInput%s' % (active_input + 1) - ac_in_source = self._dbusmonitor.get_value('com.victronenergy.settings', settings_path) - newvalues['/Ac/ActiveIn/Source'] = ac_in_source - - # ===== GRID METERS & CONSUMPTION ==== - consumption = { "L1" : None, "L2" : None, "L3" : None } -#### added for GuiMods - currentconsumption = { "L1" : None, "L2" : None, "L3" : None } - voltageIn = { "L1" : None, "L2" : None, "L3" : None } - voltageOut = { "L1" : None, "L2" : None, "L3" : None } - frequencyIn = None - frequencyOut = None - - for device_type in ['Grid', 'Genset']: - servicename = 'com.victronenergy.%s' % device_type.lower() - energy_meter = self._get_first_connected_service(servicename) - em_service = None if energy_meter is None else energy_meter[0] - uses_active_input = False - if multi_path is not None: - # If a grid meter is present we use values from it. If not, we look at the multi. If it has - # AcIn1 or AcIn2 connected to the grid, we use those values. - # com.victronenergy.grid.??? indicates presence of an energy meter used as grid meter. - # com.victronenergy.vebus.???/Ac/ActiveIn/ActiveInput: decides which whether we look at AcIn1 - # or AcIn2 as possible grid connection. - if ac_in_source is not None: - uses_active_input = ac_in_source > 0 and (ac_in_source == 2) == (device_type == 'Genset') - for phase in consumption: - p = None - pvpower = newvalues.get('/Ac/PvOn%s/%s/Power' % (device_type, phase)) -#### added for GuiMods - mc = None - pvcurrent = newvalues.get('/Ac/PvOn%s/%s/Current' % (device_type, phase)) - if em_service is not None: - p = self._dbusmonitor.get_value(em_service, '/Ac/%s/Power' % phase) -#### added for GuiMods - mc = self._dbusmonitor.get_value(em.service, '/Ac/%s/Current' % phase) - if voltageIn[phase] == None: - voltageIn[phase] = self._dbusmonitor.get_value(em.service, '/Ac/%s/Voltage' % phase) - if frequencyIn == None: - frequencyIn = self._dbusmonitor.get_value(em.service, '/Ac/%s/Frequency' % phase) - - # Compute consumption between energy meter and multi (meter power - multi AC in) and - # add an optional PV inverter on input to the mix. - c = None -#### added for GuiMods - cc = None - if uses_active_input: - ac_in = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/P' % phase) - if ac_in is not None: - try: - c = _safeadd(c, -ac_in) -#### added for GuiMods - cc = _safeadd(cc, -self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/I' % phase)) - if voltageIn[phase] == None: - voltageIn[phase] = self._dbusmonitor.get_value(em.service, '/Ac/ActiveIn/%s/V' % phase) - if frequencyIn == None: - frequencyIn = self._dbusmonitor.get_value(em.service, '/Ac/ActiveIn/%s/F' % phase) - except TypeError: - pass - - # If there's any power coming from a PV inverter in the inactive AC in (which is unlikely), - # it will still be used, because there may also be a load in the same ACIn consuming - # power, or the power could be fed back to the net. - c = _safeadd(c, p, pvpower) - consumption[phase] = _safeadd(consumption[phase], _safemax(0, c)) -#### added for GuiMods - cc = _safeadd(cc, mc, pvcurrent) - currentconsumption[phase] = _safeadd(currentconsumption[phase], _safemax(0, cc)) - else: - if uses_active_input: - p = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/P' % phase) - if p is not None: - consumption[phase] = _safeadd(0, consumption[phase]) -#### added for GuiMods - currentconsumption[phase] = _safeadd(0, currentconsumption[phase]) - mc = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/I' % phase) - if voltageIn[phase] == None: - voltageIn[phase] = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/V' % phase) - if frequencyIn == None: - freq = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/F' % phase) - if freq != None: - frequencyIn = freq - - # No relevant energy meter present. Assume there is no load between the grid and the multi. - # There may be a PV inverter present though (Hub-3 setup). - if pvpower != None: - p = _safeadd(p, -pvpower) -#### added for GuiMods - mc = _safeadd(mc, -pvcurrent) - newvalues['/Ac/%s/%s/Power' % (device_type, phase)] = p -#### added for GuiMods - newvalues['/Ac/%s/%s/Current' % (device_type, phase)] = mc - if p != None: - newvalues['/Ac/%s/%s/Voltage' % (device_type, phase)] = voltageIn[phase] - newvalues['/Ac/%s/Frequency' % (device_type)] = frequencyIn - - self._compute_number_of_phases('/Ac/%s' % device_type, newvalues) - product_id = None - device_type_id = None - if em_service is not None: - product_id = self._dbusmonitor.get_value(em_service, '/ProductId') - device_type_id = self._dbusmonitor.get_value(em_service, '/DeviceType') - if product_id is None and uses_active_input: - product_id = self._dbusmonitor.get_value(multi_path, '/ProductId') - newvalues['/Ac/%s/ProductId' % device_type] = product_id - newvalues['/Ac/%s/DeviceType' % device_type] = device_type_id - - # If we have an ESS system and RunWithoutGridMeter is set, there cannot be load on the AC-In, so it - # must be on AC-Out. Hence we do calculate AC-Out consumption even if 'useacout' is disabled. - # Similarly all load are by definition on the output if this is not an ESS system. - use_ac_out = \ - self._settings['useacout'] == 1 or \ - (multi_path is not None and self._dbusmonitor.get_value(multi_path, '/Hub4/AssistantId') not in (4, 5)) or \ - self._dbusmonitor.get_value('com.victronenergy.settings', '/Settings/CGwacs/RunWithoutGridMeter') == 1 - for phase in consumption: - c = None -#### added for GuiMods - a = None - if use_ac_out: - c = newvalues.get('/Ac/PvOnOutput/%s/Power' % phase) -#### added for GuiMods - a = newvalues.get('/Ac/PvOnOutput/%s/Current' % phase) - if voltageOut[phase] == None: - voltageOut[phase] = newvalues.get('/Ac/PvOnOutput/%s/Voltage' % phase) - if frequencyOut == None: - frequencyOut = newvalues.get('/Ac/PvOnOutput/%s/Frequency' % phase) - - if multi_path is None: - for inv in vedirect_inverters: - ac_out = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/P' % phase) -#### added for GuiMods - i = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/I' % phase) - if voltageOut[phase] == None: - voltageOut[phase] = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/V' % phase) - if frequencyOut == None: - frequencyOut = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/F' % phase) - - # Some models don't show power, calculate it - if ac_out is None: -#### modified for GuiMods - # u = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/V' % phase) - if None not in (i, voltageOut[phase]): - ac_out = i * voltageOut[phase] - c = _safeadd(c, ac_out) -#### modified for GuiMods - a = _safeadd(a, i) - else: - ac_out = self._dbusmonitor.get_value(multi_path, '/Ac/Out/%s/P' % phase) - c = _safeadd(c, ac_out) - c = _safemax(0, c) -#### added for GuiMods - a = _safemax(0, a) - newvalues['/Ac/ConsumptionOnOutput/%s/Power' % phase] = c - newvalues['/Ac/ConsumptionOnInput/%s/Power' % phase] = consumption[phase] - newvalues['/Ac/Consumption/%s/Power' % phase] = _safeadd(consumption[phase], c) -#### added for GuiMods - newvalues['/Ac/Consumption/%s/Current' % phase] = _safeadd(currentconsumption[phase], a) - newvalues['/Ac/ConsumptionOnOutput/%s/Voltage' % phase] = voltageOut[phase] - newvalues['/Ac/ConsumptionOnInput/%s/Voltage' % phase] = voltageIn[phase] - if voltageOut[phase] != None: - newvalues['/Ac/Consumption/%s/Voltage' % phase] = voltageOut[phase] - elif voltageIn[phase] != None: - newvalues['/Ac/Consumption/%s/Voltage' % phase] = voltageIn[phase] - if frequencyIn != None: - newvalues['/Ac/ConsumptionOnInput/Frequency'] = frequencyIn - if frequencyOut != None: - newvalues['/Ac/ConsumptionOnOutput/Frequency'] = frequencyOut - if frequencyOut != None: - newvalues['/Ac/Consumption/Frequency'] = frequencyOut - elif frequencyIn != None: - newvalues['/Ac/Consumption/Frequency'] = frequencyIn - - self._compute_number_of_phases('/Ac/Consumption', newvalues) - self._compute_number_of_phases('/Ac/ConsumptionOnOutput', newvalues) - self._compute_number_of_phases('/Ac/ConsumptionOnInput', newvalues) - - for m in self._modules: - m.update_values(newvalues) - - # ==== UPDATE DBUS ITEMS ==== - for path in self._summeditems.keys(): - # Why the None? Because we want to invalidate things we don't have anymore. - self._dbusservice[path] = newvalues.get(path, None) - - def _handleservicechange(self): - # Update the available battery monitor services, used to populate the dropdown in the settings. - # Below code makes a dictionary. The key is [dbuserviceclass]/[deviceinstance]. For example - # "battery/245". The value is the name to show to the user in the dropdown. The full dbus- - # servicename, ie 'com.victronenergy.vebus.ttyO1' is not used, since the last part of that is not - # fixed. dbus-serviceclass name and the device instance are already fixed, so best to use those. - - services = self._get_connected_service_list('com.victronenergy.vebus') - services.update(self._get_connected_service_list('com.victronenergy.battery')) - services.update({k: v for k, v in self._get_connected_service_list( - 'com.victronenergy.inverter').items() if self._dbusmonitor.get_value(k, '/Soc') is not None}) - - ul = {self.BATSERVICE_DEFAULT: 'Automatic', self.BATSERVICE_NOBATTERY: 'No battery monitor'} - for servicename, instance in services.items(): - key = self._get_instance_service_name(servicename, instance) - ul[key] = self._get_readable_service_name(servicename) - self._dbusservice['/AvailableBatteryServices'] = json.dumps(ul) - - ul = {self.BATSERVICE_DEFAULT: 'Automatic', self.BATSERVICE_NOBATTERY: 'No battery monitor'} - # For later: for device supporting multiple Dc measurement we should add entries for /Dc/1 etc as - # well. - for servicename, instance in services.items(): - key = self._get_instance_service_name(servicename, instance).replace('.', '_').replace('/', '_') + '/Dc/0' - ul[key] = self._get_readable_service_name(servicename) - self._dbusservice['/AvailableBatteryMeasurements'] = ul - - self._determinebatteryservice() - self._updatepvinverterspidlist() - - self._changed = True - - def _get_readable_service_name(self, servicename): - return '%s on %s' % ( - self._dbusmonitor.get_value(servicename, '/ProductName'), - self._dbusmonitor.get_value(servicename, '/Mgmt/Connection')) - - def _get_instance_service_name(self, service, instance): - return '%s/%s' % ('.'.join(service.split('.')[0:3]), instance) - - def _remove_unconnected_services(self, services): - # Workaround: because com.victronenergy.vebus is available even when there is no vebus product - # connected. Remove any that is not connected. For this, we use /State since mandatory path - # /Connected is not implemented in mk2dbus. - for servicename in services.keys(): - if ((servicename.split('.')[2] == 'vebus' and self._dbusmonitor.get_value(servicename, '/State') is None) - or self._dbusmonitor.get_value(servicename, '/Connected') != 1 - or self._dbusmonitor.get_value(servicename, '/ProductName') is None - or self._dbusmonitor.get_value(servicename, '/Mgmt/Connection') is None): - del services[servicename] - - def _dbus_value_changed(self, dbusServiceName, dbusPath, dict, changes, deviceInstance): - self._changed = True - - # Workaround because com.victronenergy.vebus is available even when there is no vebus product - # connected. - if (dbusPath in ['/Connected', '/ProductName', '/Mgmt/Connection'] or - (dbusPath == '/State' and dbusServiceName.split('.')[0:3] == ['com', 'victronenergy', 'vebus'])): - self._handleservicechange() - - # Track the timezone changes - if dbusPath == '/Settings/System/TimeZone': - tz = changes.get('Value') - if tz is not None: - os.environ['TZ'] = tz - - def _device_added(self, service, instance, do_service_change=True): - if do_service_change: - self._handleservicechange() - - for m in self._modules: - m.device_added(service, instance, do_service_change) - - def _device_removed(self, service, instance): - self._handleservicechange() - - for m in self._modules: - m.device_removed(service, instance) - - def _gettext(self, path, value): - if path == '/Dc/Battery/State': - state = {self.STATE_IDLE: 'Idle', self.STATE_CHARGING: 'Charging', - self.STATE_DISCHARGING: 'Discharging'} - return state[value] - item = self._summeditems.get(path) - if item is not None: - return item['gettext'] % value - return str(value) - - def _compute_number_of_phases(self, path, newvalues): - number_of_phases = None - for phase in range(1, 4): - p = newvalues.get('%s/L%s/Power' % (path, phase)) - if p is not None: - number_of_phases = phase - newvalues[path + '/NumberOfPhases'] = number_of_phases - - def _get_connected_service_list(self, classfilter=None): - services = self._dbusmonitor.get_service_list(classfilter=classfilter) - self._remove_unconnected_services(services) - return services - - # returns a tuple (servicename, instance) - def _get_first_connected_service(self, classfilter=None): - services = self._get_connected_service_list(classfilter=classfilter) - if len(services) == 0: - return None - return services.items()[0] - - # returns a tuple (servicename, instance) - def _get_service_having_lowest_instance(self, classfilter=None): - services = self._get_connected_service_list(classfilter=classfilter) - if len(services) == 0: - return None - - # sort the dict by value; returns list of tuples: (value, key) - s = sorted((value, key) for (key, value) in services.items()) - return (s[0][1], s[0][0]) - - -class DbusSystemCalc(SystemCalc): - def _create_dbus_monitor(self, *args, **kwargs): - return DbusMonitor(*args, **kwargs) - - def _create_settings(self, *args, **kwargs): - bus = dbus.SessionBus() if 'DBUS_SESSION_BUS_ADDRESS' in os.environ else dbus.SystemBus() - return SettingsDevice(bus, *args, timeout=10, **kwargs) - - def _create_dbus_service(self): - dbusservice = VeDbusService('com.victronenergy.system') - dbusservice.add_mandatory_paths( - processname=__file__, - processversion=softwareVersion, - connection='data from other dbus processes', - deviceinstance=0, - productid=None, - productname=None, - firmwareversion=None, - hardwareversion=None, - connected=1) - return dbusservice - - -if __name__ == "__main__": - # Argument parsing - parser = argparse.ArgumentParser( - description='Converts readings from AC-Sensors connected to a VE.Bus device in a pvinverter ' + - 'D-Bus service.' - ) - - parser.add_argument("-d", "--debug", help="set logging level to debug", - action="store_true") - - args = parser.parse_args() - - print("-------- dbus_systemcalc, v" + softwareVersion + " is starting up --------") - logger = setup_logging(args.debug) - - # Have a mainloop, so we can send/receive asynchronous calls to and from dbus - DBusGMainLoop(set_as_default=True) - - systemcalc = DbusSystemCalc() - - # Start and run the mainloop - logger.info("Starting mainloop, responding only on events") - mainloop = gobject.MainLoop() - mainloop.run() diff --git a/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v2.73.orig b/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v2.73.orig deleted file mode 100755 index b95765ca..00000000 --- a/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v2.73.orig +++ /dev/null @@ -1,952 +0,0 @@ -#!/usr/bin/python -u -# -*- coding: utf-8 -*- - -from dbus.mainloop.glib import DBusGMainLoop -import dbus -import gobject -import argparse -import sys -import os -import json -from itertools import chain - -# Victron packages -sys.path.insert(1, os.path.join(os.path.dirname(__file__), 'ext', 'velib_python')) -from vedbus import VeDbusService -from ve_utils import get_vrm_portal_id, exit_on_error -from dbusmonitor import DbusMonitor -from settingsdevice import SettingsDevice -from logger import setup_logging -import delegates -from sc_utils import safeadd as _safeadd, safemax as _safemax - -softwareVersion = '2.67' - -class SystemCalc: - STATE_IDLE = 0 - STATE_CHARGING = 1 - STATE_DISCHARGING = 2 - BATSERVICE_DEFAULT = 'default' - BATSERVICE_NOBATTERY = 'nobattery' - def __init__(self): - # Why this dummy? Because DbusMonitor expects these values to be there, even though we don't - # need them. So just add some dummy data. This can go away when DbusMonitor is more generic. - dummy = {'code': None, 'whenToLog': 'configChange', 'accessLevel': None} - dbus_tree = { - 'com.victronenergy.solarcharger': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Load/I': dummy, - '/FirmwareVersion': dummy}, - 'com.victronenergy.pvinverter': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, - '/Position': dummy, - '/ProductId': dummy}, - 'com.victronenergy.battery': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/DeviceInstance': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/1/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Soc': dummy, - '/Sense/Current': dummy, - '/TimeToGo': dummy, - '/ConsumedAmphours': dummy, - '/ProductId': dummy, - '/CustomName': dummy}, - 'com.victronenergy.vebus' : { - '/Ac/ActiveIn/ActiveInput': dummy, - '/Ac/ActiveIn/L1/P': dummy, - '/Ac/ActiveIn/L2/P': dummy, - '/Ac/ActiveIn/L3/P': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L2/P': dummy, - '/Ac/Out/L3/P': dummy, - '/Connected': dummy, - '/ProductId': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Mode': dummy, - '/State': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.charger': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/1/Voltage': dummy, - '/Dc/1/Current': dummy, - '/Dc/2/Voltage': dummy, - '/Dc/2/Current': dummy}, - 'com.victronenergy.grid' : { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/ProductId' : dummy, - '/DeviceType' : dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy}, - 'com.victronenergy.genset' : { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/ProductId' : dummy, - '/DeviceType' : dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, - '/StarterVoltage': dummy}, - 'com.victronenergy.settings' : { - '/Settings/SystemSetup/AcInput1' : dummy, - '/Settings/SystemSetup/AcInput2' : dummy, - '/Settings/CGwacs/RunWithoutGridMeter' : dummy, - '/Settings/System/TimeZone' : dummy}, - 'com.victronenergy.temperature': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy}, - 'com.victronenergy.inverter': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L1/V': dummy, - '/Ac/Out/L1/I': dummy, - '/Yield/Power': dummy, - '/Soc': dummy, - } - } - - self._modules = [ - delegates.HubTypeSelect(), - delegates.VebusSocWriter(), - delegates.ServiceMapper(), - delegates.RelayState(), - delegates.BuzzerControl(), - delegates.LgCircuitBreakerDetect(), - delegates.Dvcc(self), - delegates.BatterySense(self), - delegates.BatterySettings(self), - delegates.SystemState(), - delegates.BatteryLife(), - delegates.ScheduledCharging(), - delegates.SourceTimers(), - #delegates.BydCurrentSense(self), - delegates.BatteryData(), - delegates.Gps()] - - for m in self._modules: - for service, paths in m.get_input(): - s = dbus_tree.setdefault(service, {}) - for path in paths: - s[path] = dummy - - self._dbusmonitor = self._create_dbus_monitor(dbus_tree, valueChangedCallback=self._dbus_value_changed, - deviceAddedCallback=self._device_added, deviceRemovedCallback=self._device_removed) - - # Connect to localsettings - supported_settings = { - 'batteryservice': ['/Settings/SystemSetup/BatteryService', self.BATSERVICE_DEFAULT, 0, 0], - 'hasdcsystem': ['/Settings/SystemSetup/HasDcSystem', 0, 0, 1], - 'useacout': ['/Settings/SystemSetup/HasAcOutSystem', 1, 0, 1]} - - for m in self._modules: - for setting in m.get_settings(): - supported_settings[setting[0]] = list(setting[1:]) - - self._settings = self._create_settings(supported_settings, self._handlechangedsetting) - - self._dbusservice = self._create_dbus_service() - - for m in self._modules: - m.set_sources(self._dbusmonitor, self._settings, self._dbusservice) - - # This path does nothing except respond with a PropertiesChanged so - # that round-trip time can be measured. - self._dbusservice.add_path('/Ping', value=None, writeable=True) - - # At this moment, VRM portal ID is the MAC address of the CCGX. Anyhow, it should be string uniquely - # identifying the CCGX. - self._dbusservice.add_path('/Serial', value=get_vrm_portal_id()) - self._dbusservice.add_path( - '/AvailableBatteryServices', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/AvailableBatteryMeasurements', value=None) - self._dbusservice.add_path( - '/AutoSelectedBatteryService', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/AutoSelectedBatteryMeasurement', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/ActiveBatteryService', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/Dc/Battery/BatteryService', value=None) - self._dbusservice.add_path( - '/PvInvertersProductIds', value=None) - self._summeditems = { - '/Ac/Grid/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Grid/ProductId': {'gettext': '%s'}, - '/Ac/Grid/DeviceType': {'gettext': '%s'}, - '/Ac/Genset/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Genset/ProductId': {'gettext': '%s'}, - '/Ac/Genset/DeviceType': {'gettext': '%s'}, - '/Ac/ConsumptionOnOutput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Consumption/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/L1/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/L2/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/L3/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/L1/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/L2/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/L3/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/NumberOfPhases': {'gettext': '%d'}, - '/Dc/Pv/Power': {'gettext': '%.0F W'}, - '/Dc/Pv/Current': {'gettext': '%.1F A'}, - '/Dc/Battery/Voltage': {'gettext': '%.2F V'}, - '/Dc/Battery/VoltageService': {'gettext': '%s'}, - '/Dc/Battery/Current': {'gettext': '%.1F A'}, - '/Dc/Battery/Power': {'gettext': '%.0F W'}, - '/Dc/Battery/Soc': {'gettext': '%.0F %%'}, - '/Dc/Battery/State': {'gettext': '%s'}, - '/Dc/Battery/TimeToGo': {'gettext': '%.0F s'}, - '/Dc/Battery/ConsumedAmphours': {'gettext': '%.1F Ah'}, - '/Dc/Battery/ProductId': {'gettext': '0x%x'}, - '/Dc/Charger/Power': {'gettext': '%.0F %%'}, - '/Dc/Vebus/Current': {'gettext': '%.1F A'}, - '/Dc/Vebus/Power': {'gettext': '%.0F W'}, - '/Dc/System/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/Source': {'gettext': '%s'}, - '/VebusService': {'gettext': '%s'} - } - - for m in self._modules: - self._summeditems.update(m.get_output()) - - for path in self._summeditems.keys(): - self._dbusservice.add_path(path, value=None, gettextcallback=self._gettext) - - self._batteryservice = None - self._determinebatteryservice() - - if self._batteryservice is None: - logger.info("Battery service initialized to None (setting == %s)" % - self._settings['batteryservice']) - - self._changed = True - for service, instance in self._dbusmonitor.get_service_list().items(): - self._device_added(service, instance, do_service_change=False) - - self._handleservicechange() - self._updatevalues() - - gobject.timeout_add(1000, exit_on_error, self._handletimertick) - - 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): - raise Exception("This function should be overridden") - - def _handlechangedsetting(self, setting, oldvalue, newvalue): - self._determinebatteryservice() - self._changed = True - - # Give our delegates a chance to react on a settings change - for m in self._modules: - m.settings_changed(setting, oldvalue, newvalue) - - def _find_device_instance(self, serviceclass, instance): - """ Gets a mapping of services vs DeviceInstance using - get_service_list. Then searches for the specified DeviceInstance - and returns the service name. """ - services = self._dbusmonitor.get_service_list(classfilter=serviceclass) - - # According to https://www.python.org/dev/peps/pep-3106/, dict.keys() - # and dict.values() always have the same order. This is also much - # faster than you would expect. - try: - return services.keys()[services.values().index(instance)] - except ValueError: # If instance not in values - return None - - def _determinebatteryservice(self): - auto_battery_service = self._autoselect_battery_service() - auto_battery_measurement = None - if auto_battery_service is not None: - services = self._dbusmonitor.get_service_list() - if auto_battery_service in services: - auto_battery_measurement = \ - self._get_instance_service_name(auto_battery_service, services[auto_battery_service]) - auto_battery_measurement = auto_battery_measurement.replace('.', '_').replace('/', '_') + '/Dc/0' - self._dbusservice['/AutoSelectedBatteryMeasurement'] = auto_battery_measurement - - if self._settings['batteryservice'] == self.BATSERVICE_DEFAULT: - newbatteryservice = auto_battery_service - self._dbusservice['/AutoSelectedBatteryService'] = ( - 'No battery monitor found' if newbatteryservice is None else - self._get_readable_service_name(newbatteryservice)) - - elif self._settings['batteryservice'] == self.BATSERVICE_NOBATTERY: - self._dbusservice['/AutoSelectedBatteryService'] = None - newbatteryservice = None - - else: - self._dbusservice['/AutoSelectedBatteryService'] = None - - s = self._settings['batteryservice'].split('/') - if len(s) != 2: - logger.error("The battery setting (%s) is invalid!" % self._settings['batteryservice']) - serviceclass = s[0] - instance = int(s[1]) if len(s) == 2 else None - - # newbatteryservice might turn into None if a chosen battery - # monitor no longer exists. Don't auto change the setting (it might - # come back) and don't autoselect another. - newbatteryservice = self._find_device_instance(serviceclass, instance) - - if newbatteryservice != self._batteryservice: - services = self._dbusmonitor.get_service_list() - instance = services.get(newbatteryservice, None) - if instance is None: - battery_service = None - else: - battery_service = self._get_instance_service_name(newbatteryservice, instance) - self._dbusservice['/ActiveBatteryService'] = battery_service - logger.info("Battery service, setting == %s, changed from %s to %s (%s)" % - (self._settings['batteryservice'], self._batteryservice, newbatteryservice, instance)) - - # Battery service has changed. Notify delegates. - for m in self._modules: - m.battery_service_changed(self._batteryservice, newbatteryservice) - self._dbusservice['/Dc/Battery/BatteryService'] = self._batteryservice = newbatteryservice - - def _autoselect_battery_service(self): - # Default setting business logic: - # first try to use a battery service (BMV or Lynx Shunt VE.Can). If there - # is more than one battery service, just use a random one. If no battery service is - # available, check if there are not Solar chargers and no normal chargers. If they are not - # there, assume this is a hub-2, hub-3 or hub-4 system and use VE.Bus SOC. - batteries = self._get_connected_service_list('com.victronenergy.battery') - - # Pick the first battery service - if len(batteries) > 0: - return sorted(batteries)[0] - - # No battery services, and there is a charger in the system. Abandon - # hope. - if self._get_first_connected_service('com.victronenergy.charger') is not None: - return None - - # Also no Multi, then give up. - vebus_service = self._get_service_having_lowest_instance('com.victronenergy.vebus') - if vebus_service is None: - # No VE.Bus, but maybe there is an inverter with built-in SOC - # tracking, eg RS Smart. - inverter = self._get_service_having_lowest_instance('com.victronenergy.inverter') - if inverter and self._dbusmonitor.get_value(inverter[0], '/Soc') is not None: - return inverter[0] - - return None - - # There is a Multi, and it supports tracking external charge current - # from solarchargers. Then use it. - if self._dbusmonitor.get_value(vebus_service[0], '/ExtraBatteryCurrent') is not None and self._settings['hasdcsystem'] == 0: - return vebus_service[0] - - # Multi does not support tracking solarcharger current, and we have - # solar chargers. Then we cannot use it. - if self._get_first_connected_service('com.victronenergy.solarcharger') is not None: - return None - - # Only a Multi, no other chargers. Then we can use it. - return vebus_service[0] - - # Called on a one second timer - def _handletimertick(self): - if self._changed: - self._updatevalues() - self._changed = False - - return True # keep timer running - - def _updatepvinverterspidlist(self): - # Create list of connected pv inverters id's - pvinverters = self._dbusmonitor.get_service_list('com.victronenergy.pvinverter') - productids = [] - - for pvinverter in pvinverters: - pid = self._dbusmonitor.get_value(pvinverter, '/ProductId') - if pid is not None and pid not in productids: - productids.append(pid) - self._dbusservice['/PvInvertersProductIds'] = productids - - def _updatevalues(self): - # ==== PREPARATIONS ==== - newvalues = {} - - # Set the user timezone - if 'TZ' not in os.environ: - tz = self._dbusmonitor.get_value('com.victronenergy.settings', '/Settings/System/TimeZone') - if tz is not None: - os.environ['TZ'] = tz - - # Determine values used in logic below - vebusses = self._dbusmonitor.get_service_list('com.victronenergy.vebus') - vebuspower = 0 - for vebus in vebusses: - v = self._dbusmonitor.get_value(vebus, '/Dc/0/Voltage') - i = self._dbusmonitor.get_value(vebus, '/Dc/0/Current') - if v is not None and i is not None: - vebuspower += v * i - - # ==== PVINVERTERS ==== - pvinverters = self._dbusmonitor.get_service_list('com.victronenergy.pvinverter') - pos = {0: '/Ac/PvOnGrid', 1: '/Ac/PvOnOutput', 2: '/Ac/PvOnGenset'} - for pvinverter in pvinverters: - # Position will be None if PV inverter service has just been removed (after retrieving the - # service list). - position = pos.get(self._dbusmonitor.get_value(pvinverter, '/Position')) - if position is not None: - for phase in range(1, 4): - power = self._dbusmonitor.get_value(pvinverter, '/Ac/L%s/Power' % phase) - if power is not None: - path = '%s/L%s/Power' % (position, phase) - newvalues[path] = _safeadd(newvalues.get(path), power) - - for path in pos.values(): - self._compute_number_of_phases(path, newvalues) - - # ==== SOLARCHARGERS ==== - solarchargers = self._dbusmonitor.get_service_list('com.victronenergy.solarcharger') - solarcharger_batteryvoltage = None - solarcharger_batteryvoltage_service = None - solarchargers_charge_power = 0 - solarchargers_loadoutput_power = None - - for solarcharger in solarchargers: - v = self._dbusmonitor.get_value(solarcharger, '/Dc/0/Voltage') - if v is None: - continue - i = self._dbusmonitor.get_value(solarcharger, '/Dc/0/Current') - if i is None: - continue - l = self._dbusmonitor.get_value(solarcharger, '/Load/I', 0) - - if l is not None: - if solarchargers_loadoutput_power is None: - solarchargers_loadoutput_power = l * v - else: - solarchargers_loadoutput_power += l * v - - solarchargers_charge_power += v * i - - # Note that this path is not in the _summeditems{}, making for it to not be - # published on D-Bus. Which fine. The only one needing it is the vebussocwriter- - # delegate. - if '/Dc/Pv/ChargeCurrent' not in newvalues: - newvalues['/Dc/Pv/ChargeCurrent'] = i - else: - newvalues['/Dc/Pv/ChargeCurrent'] += i - - if '/Dc/Pv/Power' not in newvalues: - newvalues['/Dc/Pv/Power'] = v * _safeadd(i, l) - newvalues['/Dc/Pv/Current'] = _safeadd(i, l) - solarcharger_batteryvoltage = v - solarcharger_batteryvoltage_service = solarcharger - else: - newvalues['/Dc/Pv/Power'] += v * _safeadd(i, l) - newvalues['/Dc/Pv/Current'] += _safeadd(i, l) - - # ==== CHARGERS ==== - chargers = self._dbusmonitor.get_service_list('com.victronenergy.charger') - charger_batteryvoltage = None - charger_batteryvoltage_service = None - for charger in chargers: - # Assume the battery connected to output 0 is the main battery - v = self._dbusmonitor.get_value(charger, '/Dc/0/Voltage') - if v is None: - continue - - charger_batteryvoltage = v - charger_batteryvoltage_service = charger - - i = self._dbusmonitor.get_value(charger, '/Dc/0/Current') - if i is None: - continue - - if '/Dc/Charger/Power' not in newvalues: - newvalues['/Dc/Charger/Power'] = v * i - else: - newvalues['/Dc/Charger/Power'] += v * i - - # ==== VE.Direct Inverters ==== - _vedirect_inverters = sorted((di, s) for s, di in self._dbusmonitor.get_service_list('com.victronenergy.inverter').items()) - vedirect_inverters = [x[1] for x in _vedirect_inverters] - vedirect_inverter = None - if vedirect_inverters: - vedirect_inverter = vedirect_inverters[0] - - # For RS Smart inverters, add PV to the yield - for i in vedirect_inverters: - pv_yield = self._dbusmonitor.get_value(i, "/Yield/Power") - if pv_yield is not None: - newvalues['/Dc/Pv/Power'] = newvalues.get('/Dc/Pv/Power', 0) + pv_yield - - - # ==== BATTERY ==== - if self._batteryservice is not None: - batteryservicetype = self._batteryservice.split('.')[2] - assert batteryservicetype in ('battery', 'vebus', 'inverter') - - newvalues['/Dc/Battery/Soc'] = self._dbusmonitor.get_value(self._batteryservice,'/Soc') - newvalues['/Dc/Battery/TimeToGo'] = self._dbusmonitor.get_value(self._batteryservice,'/TimeToGo') - newvalues['/Dc/Battery/ConsumedAmphours'] = self._dbusmonitor.get_value(self._batteryservice,'/ConsumedAmphours') - newvalues['/Dc/Battery/ProductId'] = self._dbusmonitor.get_value(self._batteryservice, '/ProductId') - - if batteryservicetype in ('battery', 'inverter'): - newvalues['/Dc/Battery/Voltage'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Voltage') - newvalues['/Dc/Battery/VoltageService'] = self._batteryservice - newvalues['/Dc/Battery/Current'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Current') - newvalues['/Dc/Battery/Power'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Power') - - elif batteryservicetype == 'vebus': - vebus_voltage = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Voltage') - vebus_current = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Current') - vebus_power = None if vebus_voltage is None or vebus_current is None else vebus_current * vebus_voltage - newvalues['/Dc/Battery/Voltage'] = vebus_voltage - newvalues['/Dc/Battery/VoltageService'] = self._batteryservice - if self._settings['hasdcsystem'] == 1: - # hasdcsystem will normally disqualify the multi from being - # auto-selected as battery monitor, so the only way we're - # here is if the user explicitly selected the multi as the - # battery service - newvalues['/Dc/Battery/Current'] = vebus_current - if vebus_power is not None: - newvalues['/Dc/Battery/Power'] = vebus_power - else: - battery_power = _safeadd(solarchargers_charge_power, vebus_power) - newvalues['/Dc/Battery/Current'] = battery_power / vebus_voltage if vebus_voltage > 0 else None - newvalues['/Dc/Battery/Power'] = battery_power - - - p = newvalues.get('/Dc/Battery/Power', None) - if p is not None: - if p > 30: - newvalues['/Dc/Battery/State'] = self.STATE_CHARGING - elif p < -30: - newvalues['/Dc/Battery/State'] = self.STATE_DISCHARGING - else: - newvalues['/Dc/Battery/State'] = self.STATE_IDLE - - else: - # The battery service is not a BMS/BMV or a suitable vebus. A - # suitable vebus is defined as one explicitly selected by the user, - # or one that was automatically selected for SOC tracking. We may - # however still have a VE.Bus, just not one that can accurately - # track SOC. If we have one, use it as voltage source. Otherwise - # try a solar charger, a charger, or a vedirect inverter as - # fallbacks. - batteryservicetype = None - vebusses = self._dbusmonitor.get_service_list('com.victronenergy.vebus') - for vebus in vebusses: - v = self._dbusmonitor.get_value(vebus, '/Dc/0/Voltage') - s = self._dbusmonitor.get_value(vebus, '/State') - if v is not None and s not in (0, None): - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = vebus - break # Skip the else below - else: - # No suitable vebus voltage, try other devices - if solarcharger_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = solarcharger_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = solarcharger_batteryvoltage_service - elif charger_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = charger_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = charger_batteryvoltage_service - elif vedirect_inverter is not None: - v = self._dbusmonitor.get_value(vedirect_inverter, '/Dc/0/Voltage') - if v is not None: - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = vedirect_inverter - - if self._settings['hasdcsystem'] == 0 and '/Dc/Battery/Voltage' in newvalues: - # No unmonitored DC loads or chargers, and also no battery monitor: derive battery watts - # and amps from vebus, solarchargers and chargers. - assert '/Dc/Battery/Power' not in newvalues - assert '/Dc/Battery/Current' not in newvalues - p = solarchargers_charge_power + newvalues.get('/Dc/Charger/Power', 0) + vebuspower - voltage = newvalues['/Dc/Battery/Voltage'] - newvalues['/Dc/Battery/Current'] = p / voltage if voltage > 0 else None - newvalues['/Dc/Battery/Power'] = p - - - # ==== SYSTEM POWER ==== - if self._settings['hasdcsystem'] == 1 and batteryservicetype == 'battery': - # Calculate power being generated/consumed by not measured devices in the network. - # For MPPTs, take all the power, including power going out of the load output. - # /Dc/System: positive: consuming power - # VE.Bus: Positive: current flowing from the Multi to the dc system or battery - # Solarcharger & other chargers: positive: charging - # battery: Positive: charging battery. - # battery = solarcharger + charger + ve.bus - system - - battery_power = newvalues.get('/Dc/Battery/Power') - if battery_power is not None: - dc_pv_power = newvalues.get('/Dc/Pv/Power', 0) - charger_power = newvalues.get('/Dc/Charger/Power', 0) - - # If there are VE.Direct inverters, remove their power from the - # DC estimate. This is done using the AC value when the DC - # power values are not available. - inverter_power = 0 - for i in vedirect_inverters: - inverter_current = self._dbusmonitor.get_value(i, '/Dc/0/Current') - if inverter_current is not None: - inverter_power += self._dbusmonitor.get_value( - i, '/Dc/0/Voltage', 0) * inverter_current - else: - inverter_power += self._dbusmonitor.get_value( - i, '/Ac/Out/L1/V', 0) * self._dbusmonitor.get_value( - i, '/Ac/Out/L1/I', 0) - newvalues['/Dc/System/Power'] = dc_pv_power + charger_power + vebuspower - inverter_power - battery_power - - elif self._settings['hasdcsystem'] == 1 and solarchargers_loadoutput_power is not None: - newvalues['/Dc/System/Power'] = solarchargers_loadoutput_power - - # ==== Vebus ==== - multi = self._get_service_having_lowest_instance('com.victronenergy.vebus') - multi_path = None - if multi is not None: - multi_path = multi[0] - dc_current = self._dbusmonitor.get_value(multi_path, '/Dc/0/Current') - newvalues['/Dc/Vebus/Current'] = dc_current - dc_power = self._dbusmonitor.get_value(multi_path, '/Dc/0/Power') - # Just in case /Dc/0/Power is not available - if dc_power == None and dc_current is not None: - dc_voltage = self._dbusmonitor.get_value(multi_path, '/Dc/0/Voltage') - if dc_voltage is not None: - dc_power = dc_voltage * dc_current - # Note that there is also vebuspower, which is the total DC power summed over all multis. - # However, this value cannot be combined with /Dc/Multi/Current, because it does not make sense - # to add the Dc currents of all multis if they do not share the same DC voltage. - newvalues['/Dc/Vebus/Power'] = dc_power - - newvalues['/VebusService'] = multi_path - - # ===== AC IN SOURCE ===== - ac_in_source = None - if multi_path is None: - # Check if we have an non-VE.Bus inverter. If yes, then ActiveInput - # is disconnected. - if vedirect_inverter is not None: - ac_in_source = 240 - else: - active_input = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/ActiveInput') - if active_input == 0xF0: - # Not connected - ac_in_source = 240 - elif active_input is not None: - settings_path = '/Settings/SystemSetup/AcInput%s' % (active_input + 1) - ac_in_source = self._dbusmonitor.get_value('com.victronenergy.settings', settings_path) - newvalues['/Ac/ActiveIn/Source'] = ac_in_source - - # ===== GRID METERS & CONSUMPTION ==== - consumption = { "L1" : None, "L2" : None, "L3" : None } - for device_type in ['Grid', 'Genset']: - servicename = 'com.victronenergy.%s' % device_type.lower() - energy_meter = self._get_first_connected_service(servicename) - em_service = None if energy_meter is None else energy_meter[0] - uses_active_input = False - if multi_path is not None: - # If a grid meter is present we use values from it. If not, we look at the multi. If it has - # AcIn1 or AcIn2 connected to the grid, we use those values. - # com.victronenergy.grid.??? indicates presence of an energy meter used as grid meter. - # com.victronenergy.vebus.???/Ac/ActiveIn/ActiveInput: decides which whether we look at AcIn1 - # or AcIn2 as possible grid connection. - if ac_in_source is not None: - uses_active_input = ac_in_source > 0 and (ac_in_source == 2) == (device_type == 'Genset') - for phase in consumption: - p = None - pvpower = newvalues.get('/Ac/PvOn%s/%s/Power' % (device_type, phase)) - if em_service is not None: - p = self._dbusmonitor.get_value(em_service, '/Ac/%s/Power' % phase) - # Compute consumption between energy meter and multi (meter power - multi AC in) and - # add an optional PV inverter on input to the mix. - c = None - if uses_active_input: - ac_in = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/P' % phase) - if ac_in is not None: - c = _safeadd(c, -ac_in) - # If there's any power coming from a PV inverter in the inactive AC in (which is unlikely), - # it will still be used, because there may also be a load in the same ACIn consuming - # power, or the power could be fed back to the net. - c = _safeadd(c, p, pvpower) - consumption[phase] = _safeadd(consumption[phase], _safemax(0, c)) - else: - if uses_active_input: - p = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/P' % phase) - if p is not None: - consumption[phase] = _safeadd(0, consumption[phase]) - # No relevant energy meter present. Assume there is no load between the grid and the multi. - # There may be a PV inverter present though (Hub-3 setup). - if pvpower != None: - p = _safeadd(p, -pvpower) - newvalues['/Ac/%s/%s/Power' % (device_type, phase)] = p - self._compute_number_of_phases('/Ac/%s' % device_type, newvalues) - product_id = None - device_type_id = None - if em_service is not None: - product_id = self._dbusmonitor.get_value(em_service, '/ProductId') - device_type_id = self._dbusmonitor.get_value(em_service, '/DeviceType') - if product_id is None and uses_active_input: - product_id = self._dbusmonitor.get_value(multi_path, '/ProductId') - newvalues['/Ac/%s/ProductId' % device_type] = product_id - newvalues['/Ac/%s/DeviceType' % device_type] = device_type_id - # If we have an ESS system and RunWithoutGridMeter is set, there cannot be load on the AC-In, so it - # must be on AC-Out. Hence we do calculate AC-Out consumption even if 'useacout' is disabled. - # Similarly all load are by definition on the output if this is not an ESS system. - use_ac_out = \ - self._settings['useacout'] == 1 or \ - (multi_path is not None and self._dbusmonitor.get_value(multi_path, '/Hub4/AssistantId') not in (4, 5)) or \ - self._dbusmonitor.get_value('com.victronenergy.settings', '/Settings/CGwacs/RunWithoutGridMeter') == 1 - for phase in consumption: - c = None - if use_ac_out: - c = newvalues.get('/Ac/PvOnOutput/%s/Power' % phase) - if multi_path is None: - for inv in vedirect_inverters: - ac_out = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/P' % phase) - - # Some models don't show power, calculate it - if ac_out is None: - i = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/I' % phase) - u = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/V' % phase) - if None not in (i, u): - ac_out = i * u - c = _safeadd(c, ac_out) - else: - ac_out = self._dbusmonitor.get_value(multi_path, '/Ac/Out/%s/P' % phase) - c = _safeadd(c, ac_out) - c = _safemax(0, c) - newvalues['/Ac/ConsumptionOnOutput/%s/Power' % phase] = c - newvalues['/Ac/ConsumptionOnInput/%s/Power' % phase] = consumption[phase] - newvalues['/Ac/Consumption/%s/Power' % phase] = _safeadd(consumption[phase], c) - self._compute_number_of_phases('/Ac/Consumption', newvalues) - self._compute_number_of_phases('/Ac/ConsumptionOnOutput', newvalues) - self._compute_number_of_phases('/Ac/ConsumptionOnInput', newvalues) - - for m in self._modules: - m.update_values(newvalues) - - # ==== UPDATE DBUS ITEMS ==== - for path in self._summeditems.keys(): - # Why the None? Because we want to invalidate things we don't have anymore. - self._dbusservice[path] = newvalues.get(path, None) - - def _handleservicechange(self): - # Update the available battery monitor services, used to populate the dropdown in the settings. - # Below code makes a dictionary. The key is [dbuserviceclass]/[deviceinstance]. For example - # "battery/245". The value is the name to show to the user in the dropdown. The full dbus- - # servicename, ie 'com.victronenergy.vebus.ttyO1' is not used, since the last part of that is not - # fixed. dbus-serviceclass name and the device instance are already fixed, so best to use those. - - services = self._get_connected_service_list('com.victronenergy.vebus') - services.update(self._get_connected_service_list('com.victronenergy.battery')) - services.update({k: v for k, v in self._get_connected_service_list( - 'com.victronenergy.inverter').items() if self._dbusmonitor.get_value(k, '/Soc') is not None}) - - ul = {self.BATSERVICE_DEFAULT: 'Automatic', self.BATSERVICE_NOBATTERY: 'No battery monitor'} - for servicename, instance in services.items(): - key = self._get_instance_service_name(servicename, instance) - ul[key] = self._get_readable_service_name(servicename) - self._dbusservice['/AvailableBatteryServices'] = json.dumps(ul) - - ul = {self.BATSERVICE_DEFAULT: 'Automatic', self.BATSERVICE_NOBATTERY: 'No battery monitor'} - # For later: for device supporting multiple Dc measurement we should add entries for /Dc/1 etc as - # well. - for servicename, instance in services.items(): - key = self._get_instance_service_name(servicename, instance).replace('.', '_').replace('/', '_') + '/Dc/0' - ul[key] = self._get_readable_service_name(servicename) - self._dbusservice['/AvailableBatteryMeasurements'] = ul - - self._determinebatteryservice() - self._updatepvinverterspidlist() - - self._changed = True - - def _get_readable_service_name(self, servicename): - return '%s on %s' % ( - self._dbusmonitor.get_value(servicename, '/ProductName'), - self._dbusmonitor.get_value(servicename, '/Mgmt/Connection')) - - def _get_instance_service_name(self, service, instance): - return '%s/%s' % ('.'.join(service.split('.')[0:3]), instance) - - def _remove_unconnected_services(self, services): - # Workaround: because com.victronenergy.vebus is available even when there is no vebus product - # connected. Remove any that is not connected. For this, we use /State since mandatory path - # /Connected is not implemented in mk2dbus. - for servicename in services.keys(): - if ((servicename.split('.')[2] == 'vebus' and self._dbusmonitor.get_value(servicename, '/State') is None) - or self._dbusmonitor.get_value(servicename, '/Connected') != 1 - or self._dbusmonitor.get_value(servicename, '/ProductName') is None - or self._dbusmonitor.get_value(servicename, '/Mgmt/Connection') is None): - del services[servicename] - - def _dbus_value_changed(self, dbusServiceName, dbusPath, dict, changes, deviceInstance): - self._changed = True - - # Workaround because com.victronenergy.vebus is available even when there is no vebus product - # connected. - if (dbusPath in ['/Connected', '/ProductName', '/Mgmt/Connection'] or - (dbusPath == '/State' and dbusServiceName.split('.')[0:3] == ['com', 'victronenergy', 'vebus'])): - self._handleservicechange() - - # Track the timezone changes - if dbusPath == '/Settings/System/TimeZone': - tz = changes.get('Value') - if tz is not None: - os.environ['TZ'] = tz - - def _device_added(self, service, instance, do_service_change=True): - if do_service_change: - self._handleservicechange() - - for m in self._modules: - m.device_added(service, instance, do_service_change) - - def _device_removed(self, service, instance): - self._handleservicechange() - - for m in self._modules: - m.device_removed(service, instance) - - def _gettext(self, path, value): - if path == '/Dc/Battery/State': - state = {self.STATE_IDLE: 'Idle', self.STATE_CHARGING: 'Charging', - self.STATE_DISCHARGING: 'Discharging'} - return state[value] - item = self._summeditems.get(path) - if item is not None: - return item['gettext'] % value - return str(value) - - def _compute_number_of_phases(self, path, newvalues): - number_of_phases = None - for phase in range(1, 4): - p = newvalues.get('%s/L%s/Power' % (path, phase)) - if p is not None: - number_of_phases = phase - newvalues[path + '/NumberOfPhases'] = number_of_phases - - def _get_connected_service_list(self, classfilter=None): - services = self._dbusmonitor.get_service_list(classfilter=classfilter) - self._remove_unconnected_services(services) - return services - - # returns a tuple (servicename, instance) - def _get_first_connected_service(self, classfilter=None): - services = self._get_connected_service_list(classfilter=classfilter) - if len(services) == 0: - return None - return services.items()[0] - - # returns a tuple (servicename, instance) - def _get_service_having_lowest_instance(self, classfilter=None): - services = self._get_connected_service_list(classfilter=classfilter) - if len(services) == 0: - return None - - # sort the dict by value; returns list of tuples: (value, key) - s = sorted((value, key) for (key, value) in services.items()) - return (s[0][1], s[0][0]) - - -class DbusSystemCalc(SystemCalc): - def _create_dbus_monitor(self, *args, **kwargs): - return DbusMonitor(*args, **kwargs) - - def _create_settings(self, *args, **kwargs): - bus = dbus.SessionBus() if 'DBUS_SESSION_BUS_ADDRESS' in os.environ else dbus.SystemBus() - return SettingsDevice(bus, *args, timeout=10, **kwargs) - - def _create_dbus_service(self): - dbusservice = VeDbusService('com.victronenergy.system') - dbusservice.add_mandatory_paths( - processname=__file__, - processversion=softwareVersion, - connection='data from other dbus processes', - deviceinstance=0, - productid=None, - productname=None, - firmwareversion=None, - hardwareversion=None, - connected=1) - return dbusservice - - -if __name__ == "__main__": - # Argument parsing - parser = argparse.ArgumentParser( - description='Converts readings from AC-Sensors connected to a VE.Bus device in a pvinverter ' + - 'D-Bus service.' - ) - - parser.add_argument("-d", "--debug", help="set logging level to debug", - action="store_true") - - args = parser.parse_args() - - print("-------- dbus_systemcalc, v" + softwareVersion + " is starting up --------") - logger = setup_logging(args.debug) - - # Have a mainloop, so we can send/receive asynchronous calls to and from dbus - DBusGMainLoop(set_as_default=True) - - systemcalc = DbusSystemCalc() - - # Start and run the mainloop - logger.info("Starting mainloop, responding only on events") - mainloop = gobject.MainLoop() - mainloop.run() diff --git a/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v2.84 b/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v2.84 deleted file mode 100755 index 0eee8449..00000000 --- a/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v2.84 +++ /dev/null @@ -1,1224 +0,0 @@ -#!/usr/bin/python3 -u -# -*- coding: utf-8 -*- - -#### modified for GuiMods - -from dbus.mainloop.glib import DBusGMainLoop -import dbus -import argparse -import sys -import os -import json -import time -import re -from gi.repository import GLib - -# Victron packages -sys.path.insert(1, os.path.join(os.path.dirname(__file__), 'ext', 'velib_python')) -from vedbus import VeDbusService -from ve_utils import get_vrm_portal_id, exit_on_error -from dbusmonitor import DbusMonitor -from settingsdevice import SettingsDevice -from logger import setup_logging -import delegates -from sc_utils import safeadd as _safeadd, safemax as _safemax - -softwareVersion = '2.92' - -class SystemCalc: - STATE_IDLE = 0 - STATE_CHARGING = 1 - STATE_DISCHARGING = 2 - BATSERVICE_DEFAULT = 'default' - BATSERVICE_NOBATTERY = 'nobattery' - def __init__(self): - # Why this dummy? Because DbusMonitor expects these values to be there, even though we don't - # need them. So just add some dummy data. This can go away when DbusMonitor is more generic. - dummy = {'code': None, 'whenToLog': 'configChange', 'accessLevel': None} - dbus_tree = { - 'com.victronenergy.solarcharger': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Load/I': dummy, - '/FirmwareVersion': dummy}, - 'com.victronenergy.pvinverter': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, - '/Position': dummy, - '/ProductId': dummy}, - 'com.victronenergy.battery': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/DeviceInstance': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/1/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Soc': dummy, - '/Sense/Current': dummy, - '/TimeToGo': dummy, - '/ConsumedAmphours': dummy, - '/ProductId': dummy, - '/CustomName': dummy}, - 'com.victronenergy.vebus' : { - '/Ac/ActiveIn/ActiveInput': dummy, - '/Ac/ActiveIn/L1/P': dummy, - '/Ac/ActiveIn/L2/P': dummy, - '/Ac/ActiveIn/L3/P': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L2/P': dummy, - '/Ac/Out/L3/P': dummy, -#### add for GuiMods - '/Ac/Out/L1/I': dummy, - '/Ac/Out/L2/I': dummy, - '/Ac/Out/L3/I': dummy, - '/Ac/Out/L1/V': dummy, - '/Ac/Out/L2/V': dummy, - '/Ac/Out/L3/V': dummy, - '/Ac/Out/L1/F': dummy, - '/Ac/Out/L2/F': dummy, - '/Ac/Out/L3/F': dummy, - '/Ac/ActiveIn/L1/V': dummy, - '/Ac/ActiveIn/L2/V': dummy, - '/Ac/ActiveIn/L3/V': dummy, - '/Ac/ActiveIn/L1/F': dummy, - '/Ac/ActiveIn/L2/F': dummy, - '/Ac/ActiveIn/L3/F': dummy, - - '/Connected': dummy, - '/ProductId': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Mode': dummy, - '/State': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.fuelcell': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy}, - 'com.victronenergy.charger': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/1/Voltage': dummy, - '/Dc/1/Current': dummy, - '/Dc/2/Voltage': dummy, - '/Dc/2/Current': dummy}, - 'com.victronenergy.grid' : { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/ProductId' : dummy, - '/DeviceType' : dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, -#### add for GuiMods - '/Ac/L1/Current': dummy, - '/Ac/L2/Current': dummy, - '/Ac/L3/Current': dummy, - '/Ac/L1/Voltage': dummy, - '/Ac/L2/Voltage': dummy, - '/Ac/L3/Voltage': dummy, - '/Ac/L1/Frequency': dummy, - '/Ac/L2/Frequency': dummy, - '/Ac/L3/Frequency': dummy}, - 'com.victronenergy.genset' : { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/ProductId' : dummy, - '/DeviceType' : dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, -#### add for GuiMods - '/Ac/L1/Current': dummy, - '/Ac/L2/Current': dummy, - '/Ac/L3/Current': dummy, - '/Ac/L1/Voltage': dummy, - '/Ac/L2/Voltage': dummy, - '/Ac/L3/Voltage': dummy, - '/Ac/L1/Frequency': dummy, - '/Ac/L2/Frequency': dummy, - '/Ac/L3/Frequency': dummy, - - '/StarterVoltage': dummy}, - 'com.victronenergy.settings' : { - '/Settings/SystemSetup/AcInput1' : dummy, - '/Settings/SystemSetup/AcInput2' : dummy, - '/Settings/CGwacs/RunWithoutGridMeter' : dummy, - '/Settings/System/TimeZone' : dummy}, - 'com.victronenergy.temperature': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy}, - 'com.victronenergy.inverter': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L1/V': dummy, - '/Ac/Out/L1/I': dummy, -#### add for GuiMods - '/Ac/Out/L1/V': dummy, - '/Ac/Out/L1/I': dummy, - '/Ac/Out/L1/F': dummy, - - '/Yield/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.dcsystem': { - '/Dc/0/Voltage': dummy, - '/Dc/0/Power': dummy - } - } - - self._modules = [ - delegates.Multi(), - delegates.HubTypeSelect(), - delegates.VebusSocWriter(), - delegates.ServiceMapper(), - delegates.RelayState(), - delegates.BuzzerControl(), - delegates.LgCircuitBreakerDetect(), - delegates.Dvcc(self), - delegates.BatterySense(self), - delegates.BatterySettings(self), - delegates.SystemState(self), - delegates.BatteryLife(), - delegates.ScheduledCharging(), - delegates.SourceTimers(), - #delegates.BydCurrentSense(self), - delegates.BatteryData(), - delegates.Gps(), - delegates.AcInputs(), - delegates.GensetStartStop(), - delegates.SocSync(self)] - - for m in self._modules: - for service, paths in m.get_input(): - s = dbus_tree.setdefault(service, {}) - for path in paths: - s[path] = dummy - - self._dbusmonitor = self._create_dbus_monitor(dbus_tree, valueChangedCallback=self._dbus_value_changed, - deviceAddedCallback=self._device_added, deviceRemovedCallback=self._device_removed) - - # Connect to localsettings - supported_settings = { - 'batteryservice': ['/Settings/SystemSetup/BatteryService', self.BATSERVICE_DEFAULT, 0, 0], - 'hasdcsystem': ['/Settings/SystemSetup/HasDcSystem', 0, 0, 1], - 'useacout': ['/Settings/SystemSetup/HasAcOutSystem', 1, 0, 1]} - - for m in self._modules: - for setting in m.get_settings(): - supported_settings[setting[0]] = list(setting[1:]) - - self._settings = self._create_settings(supported_settings, self._handlechangedsetting) - - self._dbusservice = self._create_dbus_service() - - for m in self._modules: - m.set_sources(self._dbusmonitor, self._settings, self._dbusservice) - - # At this moment, VRM portal ID is the MAC address of the CCGX. Anyhow, it should be string uniquely - # identifying the CCGX. - self._dbusservice.add_path('/Serial', value=get_vrm_portal_id()) - self._dbusservice.add_path( - '/AvailableBatteryServices', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/AvailableBatteryMeasurements', value=None) - self._dbusservice.add_path( - '/AutoSelectedBatteryService', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/AutoSelectedBatteryMeasurement', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/ActiveBatteryService', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/Dc/Battery/BatteryService', value=None) - self._dbusservice.add_path( - '/PvInvertersProductIds', value=None) - self._summeditems = { - '/Ac/Grid/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Grid/ProductId': {'gettext': '%s'}, - '/Ac/Grid/DeviceType': {'gettext': '%s'}, - '/Ac/Genset/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Genset/ProductId': {'gettext': '%s'}, - '/Ac/Genset/DeviceType': {'gettext': '%s'}, - '/Ac/ConsumptionOnOutput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Consumption/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/L1/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/L2/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/L3/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/L1/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/L2/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/L3/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/NumberOfPhases': {'gettext': '%d'}, - '/Dc/Pv/Power': {'gettext': '%.0F W'}, - '/Dc/Pv/Current': {'gettext': '%.1F A'}, - '/Dc/Battery/Voltage': {'gettext': '%.2F V'}, - '/Dc/Battery/VoltageService': {'gettext': '%s'}, - '/Dc/Battery/Current': {'gettext': '%.1F A'}, - '/Dc/Battery/Power': {'gettext': '%.0F W'}, - '/Dc/Battery/Soc': {'gettext': '%.0F %%'}, - '/Dc/Battery/State': {'gettext': '%s'}, - '/Dc/Battery/TimeToGo': {'gettext': '%.0F s'}, - '/Dc/Battery/ConsumedAmphours': {'gettext': '%.1F Ah'}, - '/Dc/Battery/ProductId': {'gettext': '0x%x'}, - '/Dc/Charger/Power': {'gettext': '%.0F %%'}, - '/Dc/FuelCell/Power': {'gettext': '%.0F %%'}, - '/Dc/Vebus/Current': {'gettext': '%.1F A'}, - '/Dc/Vebus/Power': {'gettext': '%.0F W'}, - '/Dc/System/Power': {'gettext': '%.0F W'}, - '/Dc/System/MeasurementType': {'gettext': '%d'}, - '/Ac/ActiveIn/Source': {'gettext': '%s'}, - '/Ac/ActiveIn/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/L3/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/NumberOfPhases': {'gettext': '%d'}, -#### added for GuiMods - '/Ac/Grid/L1/Current': {'gettext': '%.1F A'}, - '/Ac/Grid/L2/Current': {'gettext': '%.1F A'}, - '/Ac/Grid/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Grid/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/Grid/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/Grid/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/Grid/Frequency': {'gettext': '%.1F Hz'}, - '/Ac/Genset/L1/Current': {'gettext': '%.1F A'}, - '/Ac/Genset/L2/Current': {'gettext': '%.1F A'}, - '/Ac/Genset/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Genset/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/Genset/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/Genset/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/Genset/Frequency': {'gettext': '%.1F Hz'}, - '/Ac/ConsumptionOnOutput/L1/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnOutput/L2/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnOutput/L3/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnOutput/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnOutput/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnOutput/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnOutput/Frequency': {'gettext': '%.1F Hz'}, - '/Ac/ConsumptionOnInput/L1/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnInput/L2/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnInput/L3/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnInput/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnInput/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnInput/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnInput/Frequency': {'gettext': '%.1F Hz'}, - '/Ac/Consumption/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/Consumption/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/Consumption/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/Consumption/L1/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/L2/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/Frequency': {'gettext': '%.1F Hz'}, - '/Ac/ActiveIn/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/ActiveIn/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/ActiveIn/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/ActiveIn/L1/Current': {'gettext': '%.1F A'}, - '/Ac/ActiveIn/L2/Current': {'gettext': '%.1F A'}, - '/Ac/ActiveIn/L3/Current': {'gettext': '%.1F A'}, - '/Ac/ActiveIn/Frequency': {'gettext': '%.1F Hz'}, - } - - for m in self._modules: - self._summeditems.update(m.get_output()) - - for path in self._summeditems.keys(): - self._dbusservice.add_path(path, value=None, gettextcallback=self._gettext) - - self._batteryservice = None - self._determinebatteryservice() - - if self._batteryservice is None: - logger.info("Battery service initialized to None (setting == %s)" % - self._settings['batteryservice']) - - self._changed = True - for service, instance in self._dbusmonitor.get_service_list().items(): - self._device_added(service, instance, do_service_change=False) - - self._handleservicechange() - self._updatevalues() - - GLib.timeout_add(1000, exit_on_error, self._handletimertick) - - 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): - raise Exception("This function should be overridden") - - def _handlechangedsetting(self, setting, oldvalue, newvalue): - self._determinebatteryservice() - self._changed = True - - # Give our delegates a chance to react on a settings change - for m in self._modules: - m.settings_changed(setting, oldvalue, newvalue) - - def _find_device_instance(self, serviceclass, instance): - """ Gets a mapping of services vs DeviceInstance using - get_service_list. Then searches for the specified DeviceInstance - and returns the service name. """ - services = self._dbusmonitor.get_service_list(classfilter=serviceclass) - - for k, v in services.items(): - if v == instance: - return k - return None - - def _determinebatteryservice(self): - auto_battery_service = self._autoselect_battery_service() - auto_battery_measurement = None - auto_selected = False - if auto_battery_service is not None: - services = self._dbusmonitor.get_service_list() - if auto_battery_service in services: - auto_battery_measurement = \ - self._get_instance_service_name(auto_battery_service, services[auto_battery_service]) - auto_battery_measurement = auto_battery_measurement.replace('.', '_').replace('/', '_') + '/Dc/0' - self._dbusservice['/AutoSelectedBatteryMeasurement'] = auto_battery_measurement - - if self._settings['batteryservice'] == self.BATSERVICE_DEFAULT: - auto_selected = True - newbatteryservice = auto_battery_service - self._dbusservice['/AutoSelectedBatteryService'] = ( - 'No battery monitor found' if newbatteryservice is None else - self._get_readable_service_name(newbatteryservice)) - - elif self._settings['batteryservice'] == self.BATSERVICE_NOBATTERY: - self._dbusservice['/AutoSelectedBatteryService'] = None - newbatteryservice = None - - else: - self._dbusservice['/AutoSelectedBatteryService'] = None - - s = self._settings['batteryservice'].split('/') - if len(s) != 2: - logger.error("The battery setting (%s) is invalid!" % self._settings['batteryservice']) - serviceclass = s[0] - instance = int(s[1]) if len(s) == 2 else None - - # newbatteryservice might turn into None if a chosen battery - # monitor no longer exists. Don't auto change the setting (it might - # come back) and don't autoselect another. - newbatteryservice = self._find_device_instance(serviceclass, instance) - - if newbatteryservice != self._batteryservice: - services = self._dbusmonitor.get_service_list() - instance = services.get(newbatteryservice, None) - if instance is None: - battery_service = None - else: - battery_service = self._get_instance_service_name(newbatteryservice, instance) - self._dbusservice['/ActiveBatteryService'] = battery_service - logger.info("Battery service, setting == %s, changed from %s to %s (%s)" % - (self._settings['batteryservice'], self._batteryservice, newbatteryservice, instance)) - - # Battery service has changed. Notify delegates. - for m in self._modules: - m.battery_service_changed(auto_selected, self._batteryservice, newbatteryservice) - self._dbusservice['/Dc/Battery/BatteryService'] = self._batteryservice = newbatteryservice - - def _autoselect_battery_service(self): - # Default setting business logic: - # first try to use a battery service (BMV or Lynx Shunt VE.Can). If there - # is more than one battery service, just use a random one. If no battery service is - # available, check if there are not Solar chargers and no normal chargers. If they are not - # there, assume this is a hub-2, hub-3 or hub-4 system and use VE.Bus SOC. - batteries = self._get_connected_service_list('com.victronenergy.battery') - - # Pick the first battery service - if len(batteries) > 0: - return sorted(batteries)[0] - - # No battery services, and there is a charger in the system. Abandon - # hope. - if self._get_first_connected_service('com.victronenergy.charger') is not None: - return None - - # Also no Multi, then give up. - vebus_service = self._get_service_having_lowest_instance('com.victronenergy.vebus') - if vebus_service is None: - # No VE.Bus, but maybe there is an inverter with built-in SOC - # tracking, eg RS Smart. - inverter = self._get_service_having_lowest_instance('com.victronenergy.inverter') - if inverter and self._dbusmonitor.get_value(inverter[0], '/Soc') is not None: - return inverter[0] - - return None - - # There is a Multi, it supports tracking external charge current from - # solarchargers, and there are no DC loads. Then use it. - if self._dbusmonitor.get_value( - vebus_service[0], '/ExtraBatteryCurrent') is not None \ - and self._get_first_connected_service('com.victronenergy.dcsystem') is None \ - and self._settings['hasdcsystem'] == 0: - return vebus_service[0] - - # Multi does not support tracking solarcharger current, and we have - # solar chargers. Then we cannot use it. - if self._get_first_connected_service('com.victronenergy.solarcharger') is not None: - return None - - # Only a Multi, no other chargers. Then we can use it. - return vebus_service[0] - - @property - def batteryservice(self): - return self._batteryservice - - # Called on a one second timer - def _handletimertick(self): - if self._changed: - self._updatevalues() - self._changed = False - - return True # keep timer running - - def _updatepvinverterspidlist(self): - # Create list of connected pv inverters id's - pvinverters = self._dbusmonitor.get_service_list('com.victronenergy.pvinverter') - productids = [] - - for pvinverter in pvinverters: - pid = self._dbusmonitor.get_value(pvinverter, '/ProductId') - if pid is not None and pid not in productids: - productids.append(pid) - self._dbusservice['/PvInvertersProductIds'] = productids - - def _updatevalues(self): - # ==== PREPARATIONS ==== - newvalues = {} - - # Set the user timezone - if 'TZ' not in os.environ: - tz = self._dbusmonitor.get_value('com.victronenergy.settings', '/Settings/System/TimeZone') - if tz is not None: - os.environ['TZ'] = tz - time.tzset() - - # Determine values used in logic below - vebusses = self._dbusmonitor.get_service_list('com.victronenergy.vebus') - vebuspower = 0 - for vebus in vebusses: - v = self._dbusmonitor.get_value(vebus, '/Dc/0/Voltage') - i = self._dbusmonitor.get_value(vebus, '/Dc/0/Current') - if v is not None and i is not None: - vebuspower += v * i - - # ==== PVINVERTERS ==== - pvinverters = self._dbusmonitor.get_service_list('com.victronenergy.pvinverter') - pos = {0: '/Ac/PvOnGrid', 1: '/Ac/PvOnOutput', 2: '/Ac/PvOnGenset'} - for pvinverter in pvinverters: - # Position will be None if PV inverter service has just been removed (after retrieving the - # service list). - position = pos.get(self._dbusmonitor.get_value(pvinverter, '/Position')) - if position is not None: - for phase in range(1, 4): - power = self._dbusmonitor.get_value(pvinverter, '/Ac/L%s/Power' % phase) - if power is not None: - path = '%s/L%s/Power' % (position, phase) - newvalues[path] = _safeadd(newvalues.get(path), power) - - for path in pos.values(): - self._compute_number_of_phases(path, newvalues) - - # ==== SOLARCHARGERS ==== - solarchargers = self._dbusmonitor.get_service_list('com.victronenergy.solarcharger') - solarcharger_batteryvoltage = None - solarcharger_batteryvoltage_service = None - solarchargers_charge_power = 0 - solarchargers_loadoutput_power = None - - for solarcharger in solarchargers: - v = self._dbusmonitor.get_value(solarcharger, '/Dc/0/Voltage') - if v is None: - continue - i = self._dbusmonitor.get_value(solarcharger, '/Dc/0/Current') - if i is None: - continue - l = self._dbusmonitor.get_value(solarcharger, '/Load/I', 0) - - if l is not None: - if solarchargers_loadoutput_power is None: - solarchargers_loadoutput_power = l * v - else: - solarchargers_loadoutput_power += l * v - - solarchargers_charge_power += v * i - - # Note that this path is not in the _summeditems{}, making for it to not be - # published on D-Bus. Which fine. The only one needing it is the vebussocwriter- - # delegate. - if '/Dc/Pv/ChargeCurrent' not in newvalues: - newvalues['/Dc/Pv/ChargeCurrent'] = i - else: - newvalues['/Dc/Pv/ChargeCurrent'] += i - - if '/Dc/Pv/Power' not in newvalues: - newvalues['/Dc/Pv/Power'] = v * _safeadd(i, l) - newvalues['/Dc/Pv/Current'] = _safeadd(i, l) - solarcharger_batteryvoltage = v - solarcharger_batteryvoltage_service = solarcharger - else: - newvalues['/Dc/Pv/Power'] += v * _safeadd(i, l) - newvalues['/Dc/Pv/Current'] += _safeadd(i, l) - - # ==== FUELCELLS ==== - fuelcells = self._dbusmonitor.get_service_list('com.victronenergy.fuelcell') - fuelcell_batteryvoltage = None - fuelcell_batteryvoltage_service = None - for fuelcell in fuelcells: - # Assume the battery connected to output 0 is the main battery - v = self._dbusmonitor.get_value(fuelcell, '/Dc/0/Voltage') - if v is None: - continue - - fuelcell_batteryvoltage = v - fuelcell_batteryvoltage_service = fuelcell - - i = self._dbusmonitor.get_value(fuelcell, '/Dc/0/Current') - if i is None: - continue - - if '/Dc/FuelCell/Power' not in newvalues: - newvalues['/Dc/FuelCell/Power'] = v * i - else: - newvalues['/Dc/FuelCell/Power'] += v * i - - # ==== CHARGERS ==== - chargers = self._dbusmonitor.get_service_list('com.victronenergy.charger') - charger_batteryvoltage = None - charger_batteryvoltage_service = None - for charger in chargers: - # Assume the battery connected to output 0 is the main battery - v = self._dbusmonitor.get_value(charger, '/Dc/0/Voltage') - if v is None: - continue - - charger_batteryvoltage = v - charger_batteryvoltage_service = charger - - i = self._dbusmonitor.get_value(charger, '/Dc/0/Current') - if i is None: - continue - - if '/Dc/Charger/Power' not in newvalues: - newvalues['/Dc/Charger/Power'] = v * i - else: - newvalues['/Dc/Charger/Power'] += v * i - - # ==== VE.Direct Inverters ==== - _vedirect_inverters = sorted((di, s) for s, di in self._dbusmonitor.get_service_list('com.victronenergy.inverter').items()) - vedirect_inverters = [x[1] for x in _vedirect_inverters] - vedirect_inverter = None - if vedirect_inverters: - vedirect_inverter = vedirect_inverters[0] - - # For RS Smart inverters, add PV to the yield - for i in vedirect_inverters: - pv_yield = self._dbusmonitor.get_value(i, "/Yield/Power") - if pv_yield is not None: - newvalues['/Dc/Pv/Power'] = newvalues.get('/Dc/Pv/Power', 0) + pv_yield - - - # Used lower down, possibly needed for battery values as well - dcsystems = self._dbusmonitor.get_service_list('com.victronenergy.dcsystem') - - # ==== BATTERY ==== - if self._batteryservice is not None: - batteryservicetype = self._batteryservice.split('.')[2] - assert batteryservicetype in ('battery', 'vebus', 'inverter') - - newvalues['/Dc/Battery/Soc'] = self._dbusmonitor.get_value(self._batteryservice,'/Soc') - newvalues['/Dc/Battery/TimeToGo'] = self._dbusmonitor.get_value(self._batteryservice,'/TimeToGo') - newvalues['/Dc/Battery/ConsumedAmphours'] = self._dbusmonitor.get_value(self._batteryservice,'/ConsumedAmphours') - newvalues['/Dc/Battery/ProductId'] = self._dbusmonitor.get_value(self._batteryservice, '/ProductId') - - if batteryservicetype in ('battery', 'inverter'): - newvalues['/Dc/Battery/Voltage'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Voltage') - newvalues['/Dc/Battery/VoltageService'] = self._batteryservice - newvalues['/Dc/Battery/Current'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Current') - newvalues['/Dc/Battery/Power'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Power') - - elif batteryservicetype == 'vebus': - vebus_voltage = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Voltage') - vebus_current = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Current') - vebus_power = None if vebus_voltage is None or vebus_current is None else vebus_current * vebus_voltage - newvalues['/Dc/Battery/Voltage'] = vebus_voltage - newvalues['/Dc/Battery/VoltageService'] = self._batteryservice - if self._settings['hasdcsystem'] == 1 or dcsystems: - # hasdcsystem will normally disqualify the multi from being - # auto-selected as battery monitor, so the only way we're - # here is if the user explicitly selected the multi as the - # battery service - newvalues['/Dc/Battery/Current'] = vebus_current - if vebus_power is not None: - newvalues['/Dc/Battery/Power'] = vebus_power - else: - battery_power = _safeadd(solarchargers_charge_power, vebus_power) - newvalues['/Dc/Battery/Current'] = battery_power / vebus_voltage if vebus_voltage is not None and vebus_voltage > 0 else None - newvalues['/Dc/Battery/Power'] = battery_power - - - p = newvalues.get('/Dc/Battery/Power', None) - if p is not None: - if p > 30: - newvalues['/Dc/Battery/State'] = self.STATE_CHARGING - elif p < -30: - newvalues['/Dc/Battery/State'] = self.STATE_DISCHARGING - else: - newvalues['/Dc/Battery/State'] = self.STATE_IDLE - - else: - # The battery service is not a BMS/BMV or a suitable vebus. A - # suitable vebus is defined as one explicitly selected by the user, - # or one that was automatically selected for SOC tracking. We may - # however still have a VE.Bus, just not one that can accurately - # track SOC. If we have one, use it as voltage source. Otherwise - # try a solar charger, a charger, a vedirect inverter or a dcsource - # as fallbacks. - batteryservicetype = None - vebusses = self._dbusmonitor.get_service_list('com.victronenergy.vebus') - for vebus in vebusses: - v = self._dbusmonitor.get_value(vebus, '/Dc/0/Voltage') - s = self._dbusmonitor.get_value(vebus, '/State') - if v is not None and s not in (0, None): - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = vebus - break # Skip the else below - else: - # No suitable vebus voltage, try other devices - if solarcharger_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = solarcharger_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = solarcharger_batteryvoltage_service - elif charger_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = charger_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = charger_batteryvoltage_service - elif fuelcell_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = fuelcell_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = fuelcell_batteryvoltage_service - elif vedirect_inverter is not None: - v = self._dbusmonitor.get_value(vedirect_inverter, '/Dc/0/Voltage') - if v is not None: - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = vedirect_inverter - elif dcsystems: - # Get voltage from first dcsystem - s = next(iter(dcsystems.keys())) - v = self._dbusmonitor.get_value(s, '/Dc/0/Voltage') - if v is not None: - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = s - - # We have no suitable battery monitor, so power and current data - # is not available. We can however calculate it from other values, - # if we have at least a battery voltage. - if '/Dc/Battery/Voltage' in newvalues: - dcsystempower = _safeadd(0, *(self._dbusmonitor.get_value(s, - '/Dc/0/Power', 0) for s in dcsystems)) - if dcsystems or self._settings['hasdcsystem'] == 0: - # Either DC loads are monitored, or there are no - # unmonitored DC loads or chargers: derive battery watts - # and amps from vebus, solarchargers, chargers and measured - # loads. - p = solarchargers_charge_power + newvalues.get('/Dc/Charger/Power', 0) + vebuspower - dcsystempower - voltage = newvalues['/Dc/Battery/Voltage'] - newvalues['/Dc/Battery/Current'] = p / voltage if voltage > 0 else None - newvalues['/Dc/Battery/Power'] = p - - # ==== SYSTEM POWER ==== - # Look for dcsytem devices, add them together. Otherwise, if enabled, - # calculate it - if dcsystems: - newvalues['/Dc/System/MeasurementType'] = 1 # measured - newvalues['/Dc/System/Power'] = 0 - for meter in dcsystems: - newvalues['/Dc/System/Power'] = _safeadd(newvalues['/Dc/System/Power'], - self._dbusmonitor.get_value(meter, '/Dc/0/Power')) - elif self._settings['hasdcsystem'] == 1 and batteryservicetype == 'battery': - # Calculate power being generated/consumed by not measured devices in the network. - # For MPPTs, take all the power, including power going out of the load output. - # /Dc/System: positive: consuming power - # VE.Bus: Positive: current flowing from the Multi to the dc system or battery - # Solarcharger & other chargers: positive: charging - # battery: Positive: charging battery. - # battery = solarcharger + charger + ve.bus - system - - battery_power = newvalues.get('/Dc/Battery/Power') - if battery_power is not None: - dc_pv_power = newvalues.get('/Dc/Pv/Power', 0) - charger_power = newvalues.get('/Dc/Charger/Power', 0) - fuelcell_power = newvalues.get('/Dc/FuelCell/Power', 0) - - # If there are VE.Direct inverters, remove their power from the - # DC estimate. This is done using the AC value when the DC - # power values are not available. - inverter_power = 0 - for i in vedirect_inverters: - inverter_current = self._dbusmonitor.get_value(i, '/Dc/0/Current') - if inverter_current is not None: - inverter_power += self._dbusmonitor.get_value( - i, '/Dc/0/Voltage', 0) * inverter_current - else: - inverter_power += self._dbusmonitor.get_value( - i, '/Ac/Out/L1/V', 0) * self._dbusmonitor.get_value( - i, '/Ac/Out/L1/I', 0) - newvalues['/Dc/System/MeasurementType'] = 0 # estimated - newvalues['/Dc/System/Power'] = dc_pv_power + charger_power + fuelcell_power + vebuspower - inverter_power - battery_power - - elif self._settings['hasdcsystem'] == 1 and solarchargers_loadoutput_power is not None: - newvalues['/Dc/System/MeasurementType'] = 0 # estimated - newvalues['/Dc/System/Power'] = solarchargers_loadoutput_power - - # ==== Vebus ==== - multi_path = getattr(delegates.Multi.instance.multi, 'service', None) - if multi_path is not None: - dc_current = self._dbusmonitor.get_value(multi_path, '/Dc/0/Current') - newvalues['/Dc/Vebus/Current'] = dc_current - dc_power = self._dbusmonitor.get_value(multi_path, '/Dc/0/Power') - # Just in case /Dc/0/Power is not available - if dc_power == None and dc_current is not None: - dc_voltage = self._dbusmonitor.get_value(multi_path, '/Dc/0/Voltage') - if dc_voltage is not None: - dc_power = dc_voltage * dc_current - # Note that there is also vebuspower, which is the total DC power summed over all multis. - # However, this value cannot be combined with /Dc/Multi/Current, because it does not make sense - # to add the Dc currents of all multis if they do not share the same DC voltage. - newvalues['/Dc/Vebus/Power'] = dc_power - - # ===== AC IN SOURCE ===== - ac_in_source = None - if multi_path is None: - # Check if we have an non-VE.Bus inverter. If yes, then ActiveInput - # is disconnected. - if vedirect_inverter is not None: - ac_in_source = 240 - else: - active_input = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/ActiveInput') - if active_input == 0xF0: - # Not connected - ac_in_source = 240 - elif active_input is not None: - settings_path = '/Settings/SystemSetup/AcInput%s' % (active_input + 1) - ac_in_source = self._dbusmonitor.get_value('com.victronenergy.settings', settings_path) - newvalues['/Ac/ActiveIn/Source'] = ac_in_source - - # ===== GRID METERS & CONSUMPTION ==== - grid_meter = delegates.AcInputs.instance.gridmeter - genset_meter = delegates.AcInputs.instance.gensetmeter - - # Make an educated guess as to what is being consumed from an AC source. If ac_in_source - # indicates grid, genset or shore, we use that. If the Multi is off, or disconnected through - # a relay assistant or otherwise, then assume the presence of a .grid or .genset service indicates - # presence of that AC source. If both are available, then give up. This decision making is here - # so the GUI has something to present even if the Multi is off. - ac_in_guess = ac_in_source - if ac_in_guess in (None, 0xF0): - if genset_meter is None and grid_meter is not None: - ac_in_guess = 1 - elif grid_meter is None and genset_meter is not None: - ac_in_guess = 2 - - consumption = { "L1" : None, "L2" : None, "L3" : None } -#### added for GuiMods - currentconsumption = { "L1" : None, "L2" : None, "L3" : None } - voltageIn = { "L1" : None, "L2" : None, "L3" : None } - voltageOut = { "L1" : None, "L2" : None, "L3" : None } - frequencyIn = None - frequencyOut = None - - for device_type, em, _types in (('Grid', grid_meter, (1, 3)), ('Genset', genset_meter, (2,))): - # If a grid meter is present we use values from it. If not, we look at the multi. If it has - # AcIn1 or AcIn2 connected to the grid, we use those values. - # com.victronenergy.grid.??? indicates presence of an energy meter used as grid meter. - # com.victronenergy.vebus.???/Ac/ActiveIn/ActiveInput: decides which whether we look at AcIn1 - # or AcIn2 as possible grid connection. - uses_active_input = ac_in_source in _types - for phase in consumption: - p = None - pvpower = newvalues.get('/Ac/PvOn%s/%s/Power' % (device_type, phase)) -#### added for GuiMods - mc = None - pvcurrent = newvalues.get('/Ac/PvOn%s/%s/Current' % (device_type, phase)) - if em is not None: - p = self._dbusmonitor.get_value(em.service, '/Ac/%s/Power' % phase) -#### added for GuiMods - mc = self._dbusmonitor.get_value(em.service, '/Ac/%s/Current' % phase) - if voltageIn[phase] == None: - voltageIn[phase] = self._dbusmonitor.get_value(em.service, '/Ac/%s/Voltage' % phase) - if frequencyIn == None: - frequencyIn = self._dbusmonitor.get_value(em.service, '/Ac/%s/Frequency' % phase) - - # Compute consumption between energy meter and multi (meter power - multi AC in) and - # add an optional PV inverter on input to the mix. - c = None -#### added for GuiMods - cc = None - if uses_active_input: - ac_in = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/P' % phase) - if ac_in is not None: - try: - c = _safeadd(c, -ac_in) -#### added for GuiMods - cc = _safeadd(cc, -self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/I' % phase)) - if voltageIn[phase] == None: - voltageIn[phase] = self._dbusmonitor.get_value(em.service, '/Ac/ActiveIn/%s/V' % phase) - if frequencyIn == None: - frequencyIn = self._dbusmonitor.get_value(em.service, '/Ac/ActiveIn/%s/F' % phase) - except TypeError: - pass - - # If there's any power coming from a PV inverter in the inactive AC in (which is unlikely), - # it will still be used, because there may also be a load in the same ACIn consuming - # power, or the power could be fed back to the net. - c = _safeadd(c, p, pvpower) - consumption[phase] = _safeadd(consumption[phase], _safemax(0, c)) -#### added for GuiMods - cc = _safeadd(cc, mc, pvcurrent) - currentconsumption[phase] = _safeadd(currentconsumption[phase], _safemax(0, cc)) - else: - if uses_active_input: - p = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/P' % phase) - if p is not None: - consumption[phase] = _safeadd(0, consumption[phase]) -#### added for GuiMods - currentconsumption[phase] = _safeadd(0, currentconsumption[phase]) - mc = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/I' % phase) - if voltageIn[phase] == None: - voltageIn[phase] = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/V' % phase) - if frequencyIn == None: - freq = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/F' % phase) - if freq != None: - frequencyIn = freq - - # No relevant energy meter present. Assume there is no load between the grid and the multi. - # There may be a PV inverter present though (Hub-3 setup). - if pvpower != None: - p = _safeadd(p, -pvpower) -#### added for GuiMods - mc = _safeadd(mc, -pvcurrent) - newvalues['/Ac/%s/%s/Power' % (device_type, phase)] = p - if ac_in_guess in _types: - newvalues['/Ac/ActiveIn/%s/Power' % (phase,)] = p -#### added for GuiMods - newvalues['/Ac/%s/%s/Current' % (device_type, phase)] = mc - if p != None: - newvalues['/Ac/%s/%s/Voltage' % (device_type, phase)] = voltageIn[phase] - newvalues['/Ac/%s/Frequency' % (device_type)] = frequencyIn - - self._compute_number_of_phases('/Ac/%s' % device_type, newvalues) - self._compute_number_of_phases('/Ac/ActiveIn', newvalues) - - product_id = None - device_type_id = None - if em is not None: - product_id = em.product_id - device_type_id = em.device_type - if product_id is None and uses_active_input: - product_id = self._dbusmonitor.get_value(multi_path, '/ProductId') - newvalues['/Ac/%s/ProductId' % device_type] = product_id - newvalues['/Ac/%s/DeviceType' % device_type] = device_type_id - - # If we have an ESS system and RunWithoutGridMeter is set, there cannot be load on the AC-In, so it - # must be on AC-Out. Hence we do calculate AC-Out consumption even if 'useacout' is disabled. - # Similarly all load are by definition on the output if this is not an ESS system. - use_ac_out = \ - self._settings['useacout'] == 1 or \ - (multi_path is not None and self._dbusmonitor.get_value(multi_path, '/Hub4/AssistantId') not in (4, 5)) or \ - self._dbusmonitor.get_value('com.victronenergy.settings', '/Settings/CGwacs/RunWithoutGridMeter') == 1 - for phase in consumption: - c = None -#### added for GuiMods - a = None - if use_ac_out: - c = newvalues.get('/Ac/PvOnOutput/%s/Power' % phase) -#### added for GuiMods - a = newvalues.get('/Ac/PvOnOutput/%s/Current' % phase) - if voltageOut[phase] == None: - voltageOut[phase] = newvalues.get('/Ac/PvOnOutput/%s/Voltage' % phase) - if frequencyOut == None: - frequencyOut = newvalues.get('/Ac/PvOnOutput/%s/Frequency' % phase) - - if multi_path is None: - for inv in vedirect_inverters: - ac_out = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/P' % phase) -#### added for GuiMods - i = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/I' % phase) - if voltageOut[phase] == None: - voltageOut[phase] = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/V' % phase) - if frequencyOut == None: - frequencyOut = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/F' % phase) - - # Some models don't show power, calculate it - if ac_out is None: - i = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/I' % phase) - u = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/V' % phase) - if None not in (i, u): - ac_out = i * u -#### modified for GuiMods - # u = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/V' % phase) - if None not in (i, voltageOut[phase]): - ac_out = i * voltageOut[phase] - c = _safeadd(c, ac_out) - else: - ac_out = self._dbusmonitor.get_value(multi_path, '/Ac/Out/%s/P' % phase) - c = _safeadd(c, ac_out) - c = _safemax(0, c) -#### added for GuiMods - a = _safemax(0, a) - newvalues['/Ac/ConsumptionOnOutput/%s/Power' % phase] = c - newvalues['/Ac/ConsumptionOnInput/%s/Power' % phase] = consumption[phase] - newvalues['/Ac/Consumption/%s/Power' % phase] = _safeadd(consumption[phase], c) -#### added for GuiMods - newvalues['/Ac/Consumption/%s/Current' % phase] = _safeadd(currentconsumption[phase], a) - newvalues['/Ac/ConsumptionOnOutput/%s/Voltage' % phase] = voltageOut[phase] - newvalues['/Ac/ConsumptionOnInput/%s/Voltage' % phase] = voltageIn[phase] - if voltageOut[phase] != None: - newvalues['/Ac/Consumption/%s/Voltage' % phase] = voltageOut[phase] - elif voltageIn[phase] != None: - newvalues['/Ac/Consumption/%s/Voltage' % phase] = voltageIn[phase] - if frequencyIn != None: - newvalues['/Ac/ConsumptionOnInput/Frequency'] = frequencyIn - if frequencyOut != None: - newvalues['/Ac/ConsumptionOnOutput/Frequency'] = frequencyOut - if frequencyOut != None: - newvalues['/Ac/Consumption/Frequency'] = frequencyOut - elif frequencyIn != None: - newvalues['/Ac/Consumption/Frequency'] = frequencyIn - - self._compute_number_of_phases('/Ac/Consumption', newvalues) - self._compute_number_of_phases('/Ac/ConsumptionOnOutput', newvalues) - self._compute_number_of_phases('/Ac/ConsumptionOnInput', newvalues) - - for m in self._modules: - m.update_values(newvalues) - - # ==== UPDATE DBUS ITEMS ==== - with self._dbusservice as sss: - for path in self._summeditems.keys(): - # Why the None? Because we want to invalidate things we don't have anymore. - sss[path] = newvalues.get(path, None) - - def _handleservicechange(self): - # Update the available battery monitor services, used to populate the dropdown in the settings. - # Below code makes a dictionary. The key is [dbuserviceclass]/[deviceinstance]. For example - # "battery/245". The value is the name to show to the user in the dropdown. The full dbus- - # servicename, ie 'com.victronenergy.vebus.ttyO1' is not used, since the last part of that is not - # fixed. dbus-serviceclass name and the device instance are already fixed, so best to use those. - - services = self._get_connected_service_list('com.victronenergy.vebus') - services.update(self._get_connected_service_list('com.victronenergy.battery')) - services.update({k: v for k, v in self._get_connected_service_list( - 'com.victronenergy.inverter').items() if self._dbusmonitor.get_value(k, '/Soc') is not None}) - - ul = {self.BATSERVICE_DEFAULT: 'Automatic', self.BATSERVICE_NOBATTERY: 'No battery monitor'} - for servicename, instance in services.items(): - key = self._get_instance_service_name(servicename, instance) - ul[key] = self._get_readable_service_name(servicename) - self._dbusservice['/AvailableBatteryServices'] = json.dumps(ul) - - ul = {self.BATSERVICE_DEFAULT: 'Automatic', self.BATSERVICE_NOBATTERY: 'No battery monitor'} - # For later: for device supporting multiple Dc measurement we should add entries for /Dc/1 etc as - # well. - for servicename, instance in services.items(): - key = self._get_instance_service_name(servicename, instance).replace('.', '_').replace('/', '_') + '/Dc/0' - ul[key] = self._get_readable_service_name(servicename) - self._dbusservice['/AvailableBatteryMeasurements'] = ul - - self._determinebatteryservice() - self._updatepvinverterspidlist() - - self._changed = True - - def _get_readable_service_name(self, servicename): - return '%s on %s' % ( - self._dbusmonitor.get_value(servicename, '/ProductName'), - self._dbusmonitor.get_value(servicename, '/Mgmt/Connection')) - - def _get_instance_service_name(self, service, instance): - return '%s/%s' % ('.'.join(service.split('.')[0:3]), instance) - - def _remove_unconnected_services(self, services): - # Workaround: because com.victronenergy.vebus is available even when there is no vebus product - # connected, remove any service that is not connected. Previously we used - # /State since mandatory path /Connected is not implemented in mk2dbus, - # but this has since been resolved. - for servicename in list(services.keys()): - if (self._dbusmonitor.get_value(servicename, '/Connected') != 1 - or self._dbusmonitor.get_value(servicename, '/ProductName') is None - or self._dbusmonitor.get_value(servicename, '/Mgmt/Connection') is None): - del services[servicename] - - def _dbus_value_changed(self, dbusServiceName, dbusPath, dict, changes, deviceInstance): - self._changed = True - - # Workaround because com.victronenergy.vebus is available even when there is no vebus product - # connected. - if (dbusPath in ['/Connected', '/ProductName', '/Mgmt/Connection'] or - (dbusPath == '/State' and dbusServiceName.split('.')[0:3] == ['com', 'victronenergy', 'vebus'])): - self._handleservicechange() - - # Track the timezone changes - if dbusPath == '/Settings/System/TimeZone': - tz = changes.get('Value') - if tz is not None: - os.environ['TZ'] = tz - - def _device_added(self, service, instance, do_service_change=True): - if do_service_change: - self._handleservicechange() - - for m in self._modules: - m.device_added(service, instance, do_service_change) - - def _device_removed(self, service, instance): - self._handleservicechange() - - for m in self._modules: - m.device_removed(service, instance) - - def _gettext(self, path, value): - if path == '/Dc/Battery/State': - state = {self.STATE_IDLE: 'Idle', self.STATE_CHARGING: 'Charging', - self.STATE_DISCHARGING: 'Discharging'} - return state[value] - item = self._summeditems.get(path) - if item is not None: - return item['gettext'] % value - return str(value) - - def _compute_number_of_phases(self, path, newvalues): - number_of_phases = None - for phase in range(1, 4): - p = newvalues.get('%s/L%s/Power' % (path, phase)) - if p is not None: - number_of_phases = phase - newvalues[path + '/NumberOfPhases'] = number_of_phases - - def _get_connected_service_list(self, classfilter=None): - services = self._dbusmonitor.get_service_list(classfilter=classfilter) - self._remove_unconnected_services(services) - return services - - # returns a servicename string - def _get_first_connected_service(self, classfilter): - services = self._get_connected_service_list(classfilter=classfilter) - if len(services) == 0: - return None - return next(iter(services.items()), (None,))[0] - - # returns a tuple (servicename, instance) - def _get_service_having_lowest_instance(self, classfilter=None): - services = self._get_connected_service_list(classfilter=classfilter) - if len(services) == 0: - return None - - # sort the dict by value; returns list of tuples: (value, key) - s = sorted((value, key) for (key, value) in services.items()) - return (s[0][1], s[0][0]) - - -class DbusSystemCalc(SystemCalc): - def _create_dbus_monitor(self, *args, **kwargs): - return DbusMonitor(*args, **kwargs) - - def _create_settings(self, *args, **kwargs): - bus = dbus.SessionBus() if 'DBUS_SESSION_BUS_ADDRESS' in os.environ else dbus.SystemBus() - return SettingsDevice(bus, *args, timeout=10, **kwargs) - - def _create_dbus_service(self): - venusversion, venusbuildtime = self._get_venus_versioninfo() - - dbusservice = VeDbusService('com.victronenergy.system') - dbusservice.add_mandatory_paths( - processname=__file__, - processversion=softwareVersion, - connection='data from other dbus processes', - deviceinstance=0, - productid=None, - productname=None, - firmwareversion=venusversion, - hardwareversion=None, - connected=1) - dbusservice.add_path('/FirmwareBuild', value=venusbuildtime) - return dbusservice - - def _get_venus_versioninfo(self): - try: - with open("/opt/victronenergy/version", "r") as fp: - version, software, buildtime = fp.read().split('\n')[:3] - major, minor, _, rev = re.compile('v([0-9]*)\.([0-9]*)(~([0-9]*))?').match(version).groups() - return (int(major, 16)<<16)+(int(minor, 16)<<8)+(0 if rev is None else int(rev, 16)), buildtime - except Exception: - pass - return 0, '0' - -if __name__ == "__main__": - # Argument parsing - parser = argparse.ArgumentParser( - description='Converts readings from AC-Sensors connected to a VE.Bus device in a pvinverter ' + - 'D-Bus service.' - ) - - parser.add_argument("-d", "--debug", help="set logging level to debug", - action="store_true") - - args = parser.parse_args() - - print("-------- dbus_systemcalc, v" + softwareVersion + " is starting up --------") - logger = setup_logging(args.debug) - - # Have a mainloop, so we can send/receive asynchronous calls to and from dbus - DBusGMainLoop(set_as_default=True) - - systemcalc = DbusSystemCalc() - - # Start and run the mainloop - logger.info("Starting mainloop, responding only on events") - mainloop = GLib.MainLoop() - mainloop.run() diff --git a/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v2.84.orig b/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v2.84.orig deleted file mode 100755 index 64076df5..00000000 --- a/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v2.84.orig +++ /dev/null @@ -1,1049 +0,0 @@ -#!/usr/bin/python3 -u -# -*- coding: utf-8 -*- - -from dbus.mainloop.glib import DBusGMainLoop -import dbus -import argparse -import sys -import os -import json -import time -import re -from gi.repository import GLib - -# Victron packages -sys.path.insert(1, os.path.join(os.path.dirname(__file__), 'ext', 'velib_python')) -from vedbus import VeDbusService -from ve_utils import get_vrm_portal_id, exit_on_error -from dbusmonitor import DbusMonitor -from settingsdevice import SettingsDevice -from logger import setup_logging -import delegates -from sc_utils import safeadd as _safeadd, safemax as _safemax - -softwareVersion = '2.92' - -class SystemCalc: - STATE_IDLE = 0 - STATE_CHARGING = 1 - STATE_DISCHARGING = 2 - BATSERVICE_DEFAULT = 'default' - BATSERVICE_NOBATTERY = 'nobattery' - def __init__(self): - # Why this dummy? Because DbusMonitor expects these values to be there, even though we don't - # need them. So just add some dummy data. This can go away when DbusMonitor is more generic. - dummy = {'code': None, 'whenToLog': 'configChange', 'accessLevel': None} - dbus_tree = { - 'com.victronenergy.solarcharger': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Load/I': dummy, - '/FirmwareVersion': dummy}, - 'com.victronenergy.pvinverter': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, - '/Position': dummy, - '/ProductId': dummy}, - 'com.victronenergy.battery': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/DeviceInstance': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/1/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Soc': dummy, - '/Sense/Current': dummy, - '/TimeToGo': dummy, - '/ConsumedAmphours': dummy, - '/ProductId': dummy, - '/CustomName': dummy}, - 'com.victronenergy.vebus' : { - '/Ac/ActiveIn/ActiveInput': dummy, - '/Ac/ActiveIn/L1/P': dummy, - '/Ac/ActiveIn/L2/P': dummy, - '/Ac/ActiveIn/L3/P': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L2/P': dummy, - '/Ac/Out/L3/P': dummy, - '/Connected': dummy, - '/ProductId': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Mode': dummy, - '/State': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.fuelcell': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy}, - 'com.victronenergy.charger': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/1/Voltage': dummy, - '/Dc/1/Current': dummy, - '/Dc/2/Voltage': dummy, - '/Dc/2/Current': dummy}, - 'com.victronenergy.grid' : { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/ProductId' : dummy, - '/DeviceType' : dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy}, - 'com.victronenergy.genset' : { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/ProductId' : dummy, - '/DeviceType' : dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, - '/StarterVoltage': dummy}, - 'com.victronenergy.settings' : { - '/Settings/SystemSetup/AcInput1' : dummy, - '/Settings/SystemSetup/AcInput2' : dummy, - '/Settings/CGwacs/RunWithoutGridMeter' : dummy, - '/Settings/System/TimeZone' : dummy}, - 'com.victronenergy.temperature': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy}, - 'com.victronenergy.inverter': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L1/V': dummy, - '/Ac/Out/L1/I': dummy, - '/Yield/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.dcsystem': { - '/Dc/0/Voltage': dummy, - '/Dc/0/Power': dummy - } - } - - self._modules = [ - delegates.Multi(), - delegates.HubTypeSelect(), - delegates.VebusSocWriter(), - delegates.ServiceMapper(), - delegates.RelayState(), - delegates.BuzzerControl(), - delegates.LgCircuitBreakerDetect(), - delegates.Dvcc(self), - delegates.BatterySense(self), - delegates.BatterySettings(self), - delegates.SystemState(self), - delegates.BatteryLife(), - delegates.ScheduledCharging(), - delegates.SourceTimers(), - #delegates.BydCurrentSense(self), - delegates.BatteryData(), - delegates.Gps(), - delegates.AcInputs(), - delegates.GensetStartStop(), - delegates.SocSync(self)] - - for m in self._modules: - for service, paths in m.get_input(): - s = dbus_tree.setdefault(service, {}) - for path in paths: - s[path] = dummy - - self._dbusmonitor = self._create_dbus_monitor(dbus_tree, valueChangedCallback=self._dbus_value_changed, - deviceAddedCallback=self._device_added, deviceRemovedCallback=self._device_removed) - - # Connect to localsettings - supported_settings = { - 'batteryservice': ['/Settings/SystemSetup/BatteryService', self.BATSERVICE_DEFAULT, 0, 0], - 'hasdcsystem': ['/Settings/SystemSetup/HasDcSystem', 0, 0, 1], - 'useacout': ['/Settings/SystemSetup/HasAcOutSystem', 1, 0, 1]} - - for m in self._modules: - for setting in m.get_settings(): - supported_settings[setting[0]] = list(setting[1:]) - - self._settings = self._create_settings(supported_settings, self._handlechangedsetting) - - self._dbusservice = self._create_dbus_service() - - for m in self._modules: - m.set_sources(self._dbusmonitor, self._settings, self._dbusservice) - - # At this moment, VRM portal ID is the MAC address of the CCGX. Anyhow, it should be string uniquely - # identifying the CCGX. - self._dbusservice.add_path('/Serial', value=get_vrm_portal_id()) - self._dbusservice.add_path( - '/AvailableBatteryServices', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/AvailableBatteryMeasurements', value=None) - self._dbusservice.add_path( - '/AutoSelectedBatteryService', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/AutoSelectedBatteryMeasurement', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/ActiveBatteryService', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/Dc/Battery/BatteryService', value=None) - self._dbusservice.add_path( - '/PvInvertersProductIds', value=None) - self._summeditems = { - '/Ac/Grid/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Grid/ProductId': {'gettext': '%s'}, - '/Ac/Grid/DeviceType': {'gettext': '%s'}, - '/Ac/Genset/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Genset/ProductId': {'gettext': '%s'}, - '/Ac/Genset/DeviceType': {'gettext': '%s'}, - '/Ac/ConsumptionOnOutput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Consumption/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/L1/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/L2/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/L3/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/L1/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/L2/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/L3/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/NumberOfPhases': {'gettext': '%d'}, - '/Dc/Pv/Power': {'gettext': '%.0F W'}, - '/Dc/Pv/Current': {'gettext': '%.1F A'}, - '/Dc/Battery/Voltage': {'gettext': '%.2F V'}, - '/Dc/Battery/VoltageService': {'gettext': '%s'}, - '/Dc/Battery/Current': {'gettext': '%.1F A'}, - '/Dc/Battery/Power': {'gettext': '%.0F W'}, - '/Dc/Battery/Soc': {'gettext': '%.0F %%'}, - '/Dc/Battery/State': {'gettext': '%s'}, - '/Dc/Battery/TimeToGo': {'gettext': '%.0F s'}, - '/Dc/Battery/ConsumedAmphours': {'gettext': '%.1F Ah'}, - '/Dc/Battery/ProductId': {'gettext': '0x%x'}, - '/Dc/Charger/Power': {'gettext': '%.0F %%'}, - '/Dc/FuelCell/Power': {'gettext': '%.0F %%'}, - '/Dc/Vebus/Current': {'gettext': '%.1F A'}, - '/Dc/Vebus/Power': {'gettext': '%.0F W'}, - '/Dc/System/Power': {'gettext': '%.0F W'}, - '/Dc/System/MeasurementType': {'gettext': '%d'}, - '/Ac/ActiveIn/Source': {'gettext': '%s'}, - '/Ac/ActiveIn/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/L3/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/NumberOfPhases': {'gettext': '%d'}, - } - - for m in self._modules: - self._summeditems.update(m.get_output()) - - for path in self._summeditems.keys(): - self._dbusservice.add_path(path, value=None, gettextcallback=self._gettext) - - self._batteryservice = None - self._determinebatteryservice() - - if self._batteryservice is None: - logger.info("Battery service initialized to None (setting == %s)" % - self._settings['batteryservice']) - - self._changed = True - for service, instance in self._dbusmonitor.get_service_list().items(): - self._device_added(service, instance, do_service_change=False) - - self._handleservicechange() - self._updatevalues() - - GLib.timeout_add(1000, exit_on_error, self._handletimertick) - - 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): - raise Exception("This function should be overridden") - - def _handlechangedsetting(self, setting, oldvalue, newvalue): - self._determinebatteryservice() - self._changed = True - - # Give our delegates a chance to react on a settings change - for m in self._modules: - m.settings_changed(setting, oldvalue, newvalue) - - def _find_device_instance(self, serviceclass, instance): - """ Gets a mapping of services vs DeviceInstance using - get_service_list. Then searches for the specified DeviceInstance - and returns the service name. """ - services = self._dbusmonitor.get_service_list(classfilter=serviceclass) - - for k, v in services.items(): - if v == instance: - return k - return None - - def _determinebatteryservice(self): - auto_battery_service = self._autoselect_battery_service() - auto_battery_measurement = None - auto_selected = False - if auto_battery_service is not None: - services = self._dbusmonitor.get_service_list() - if auto_battery_service in services: - auto_battery_measurement = \ - self._get_instance_service_name(auto_battery_service, services[auto_battery_service]) - auto_battery_measurement = auto_battery_measurement.replace('.', '_').replace('/', '_') + '/Dc/0' - self._dbusservice['/AutoSelectedBatteryMeasurement'] = auto_battery_measurement - - if self._settings['batteryservice'] == self.BATSERVICE_DEFAULT: - auto_selected = True - newbatteryservice = auto_battery_service - self._dbusservice['/AutoSelectedBatteryService'] = ( - 'No battery monitor found' if newbatteryservice is None else - self._get_readable_service_name(newbatteryservice)) - - elif self._settings['batteryservice'] == self.BATSERVICE_NOBATTERY: - self._dbusservice['/AutoSelectedBatteryService'] = None - newbatteryservice = None - - else: - self._dbusservice['/AutoSelectedBatteryService'] = None - - s = self._settings['batteryservice'].split('/') - if len(s) != 2: - logger.error("The battery setting (%s) is invalid!" % self._settings['batteryservice']) - serviceclass = s[0] - instance = int(s[1]) if len(s) == 2 else None - - # newbatteryservice might turn into None if a chosen battery - # monitor no longer exists. Don't auto change the setting (it might - # come back) and don't autoselect another. - newbatteryservice = self._find_device_instance(serviceclass, instance) - - if newbatteryservice != self._batteryservice: - services = self._dbusmonitor.get_service_list() - instance = services.get(newbatteryservice, None) - if instance is None: - battery_service = None - else: - battery_service = self._get_instance_service_name(newbatteryservice, instance) - self._dbusservice['/ActiveBatteryService'] = battery_service - logger.info("Battery service, setting == %s, changed from %s to %s (%s)" % - (self._settings['batteryservice'], self._batteryservice, newbatteryservice, instance)) - - # Battery service has changed. Notify delegates. - for m in self._modules: - m.battery_service_changed(auto_selected, self._batteryservice, newbatteryservice) - self._dbusservice['/Dc/Battery/BatteryService'] = self._batteryservice = newbatteryservice - - def _autoselect_battery_service(self): - # Default setting business logic: - # first try to use a battery service (BMV or Lynx Shunt VE.Can). If there - # is more than one battery service, just use a random one. If no battery service is - # available, check if there are not Solar chargers and no normal chargers. If they are not - # there, assume this is a hub-2, hub-3 or hub-4 system and use VE.Bus SOC. - batteries = self._get_connected_service_list('com.victronenergy.battery') - - # Pick the first battery service - if len(batteries) > 0: - return sorted(batteries)[0] - - # No battery services, and there is a charger in the system. Abandon - # hope. - if self._get_first_connected_service('com.victronenergy.charger') is not None: - return None - - # Also no Multi, then give up. - vebus_service = self._get_service_having_lowest_instance('com.victronenergy.vebus') - if vebus_service is None: - # No VE.Bus, but maybe there is an inverter with built-in SOC - # tracking, eg RS Smart. - inverter = self._get_service_having_lowest_instance('com.victronenergy.inverter') - if inverter and self._dbusmonitor.get_value(inverter[0], '/Soc') is not None: - return inverter[0] - - return None - - # There is a Multi, it supports tracking external charge current from - # solarchargers, and there are no DC loads. Then use it. - if self._dbusmonitor.get_value( - vebus_service[0], '/ExtraBatteryCurrent') is not None \ - and self._get_first_connected_service('com.victronenergy.dcsystem') is None \ - and self._settings['hasdcsystem'] == 0: - return vebus_service[0] - - # Multi does not support tracking solarcharger current, and we have - # solar chargers. Then we cannot use it. - if self._get_first_connected_service('com.victronenergy.solarcharger') is not None: - return None - - # Only a Multi, no other chargers. Then we can use it. - return vebus_service[0] - - @property - def batteryservice(self): - return self._batteryservice - - # Called on a one second timer - def _handletimertick(self): - if self._changed: - self._updatevalues() - self._changed = False - - return True # keep timer running - - def _updatepvinverterspidlist(self): - # Create list of connected pv inverters id's - pvinverters = self._dbusmonitor.get_service_list('com.victronenergy.pvinverter') - productids = [] - - for pvinverter in pvinverters: - pid = self._dbusmonitor.get_value(pvinverter, '/ProductId') - if pid is not None and pid not in productids: - productids.append(pid) - self._dbusservice['/PvInvertersProductIds'] = productids - - def _updatevalues(self): - # ==== PREPARATIONS ==== - newvalues = {} - - # Set the user timezone - if 'TZ' not in os.environ: - tz = self._dbusmonitor.get_value('com.victronenergy.settings', '/Settings/System/TimeZone') - if tz is not None: - os.environ['TZ'] = tz - time.tzset() - - # Determine values used in logic below - vebusses = self._dbusmonitor.get_service_list('com.victronenergy.vebus') - vebuspower = 0 - for vebus in vebusses: - v = self._dbusmonitor.get_value(vebus, '/Dc/0/Voltage') - i = self._dbusmonitor.get_value(vebus, '/Dc/0/Current') - if v is not None and i is not None: - vebuspower += v * i - - # ==== PVINVERTERS ==== - pvinverters = self._dbusmonitor.get_service_list('com.victronenergy.pvinverter') - pos = {0: '/Ac/PvOnGrid', 1: '/Ac/PvOnOutput', 2: '/Ac/PvOnGenset'} - for pvinverter in pvinverters: - # Position will be None if PV inverter service has just been removed (after retrieving the - # service list). - position = pos.get(self._dbusmonitor.get_value(pvinverter, '/Position')) - if position is not None: - for phase in range(1, 4): - power = self._dbusmonitor.get_value(pvinverter, '/Ac/L%s/Power' % phase) - if power is not None: - path = '%s/L%s/Power' % (position, phase) - newvalues[path] = _safeadd(newvalues.get(path), power) - - for path in pos.values(): - self._compute_number_of_phases(path, newvalues) - - # ==== SOLARCHARGERS ==== - solarchargers = self._dbusmonitor.get_service_list('com.victronenergy.solarcharger') - solarcharger_batteryvoltage = None - solarcharger_batteryvoltage_service = None - solarchargers_charge_power = 0 - solarchargers_loadoutput_power = None - - for solarcharger in solarchargers: - v = self._dbusmonitor.get_value(solarcharger, '/Dc/0/Voltage') - if v is None: - continue - i = self._dbusmonitor.get_value(solarcharger, '/Dc/0/Current') - if i is None: - continue - l = self._dbusmonitor.get_value(solarcharger, '/Load/I', 0) - - if l is not None: - if solarchargers_loadoutput_power is None: - solarchargers_loadoutput_power = l * v - else: - solarchargers_loadoutput_power += l * v - - solarchargers_charge_power += v * i - - # Note that this path is not in the _summeditems{}, making for it to not be - # published on D-Bus. Which fine. The only one needing it is the vebussocwriter- - # delegate. - if '/Dc/Pv/ChargeCurrent' not in newvalues: - newvalues['/Dc/Pv/ChargeCurrent'] = i - else: - newvalues['/Dc/Pv/ChargeCurrent'] += i - - if '/Dc/Pv/Power' not in newvalues: - newvalues['/Dc/Pv/Power'] = v * _safeadd(i, l) - newvalues['/Dc/Pv/Current'] = _safeadd(i, l) - solarcharger_batteryvoltage = v - solarcharger_batteryvoltage_service = solarcharger - else: - newvalues['/Dc/Pv/Power'] += v * _safeadd(i, l) - newvalues['/Dc/Pv/Current'] += _safeadd(i, l) - - # ==== FUELCELLS ==== - fuelcells = self._dbusmonitor.get_service_list('com.victronenergy.fuelcell') - fuelcell_batteryvoltage = None - fuelcell_batteryvoltage_service = None - for fuelcell in fuelcells: - # Assume the battery connected to output 0 is the main battery - v = self._dbusmonitor.get_value(fuelcell, '/Dc/0/Voltage') - if v is None: - continue - - fuelcell_batteryvoltage = v - fuelcell_batteryvoltage_service = fuelcell - - i = self._dbusmonitor.get_value(fuelcell, '/Dc/0/Current') - if i is None: - continue - - if '/Dc/FuelCell/Power' not in newvalues: - newvalues['/Dc/FuelCell/Power'] = v * i - else: - newvalues['/Dc/FuelCell/Power'] += v * i - - # ==== CHARGERS ==== - chargers = self._dbusmonitor.get_service_list('com.victronenergy.charger') - charger_batteryvoltage = None - charger_batteryvoltage_service = None - for charger in chargers: - # Assume the battery connected to output 0 is the main battery - v = self._dbusmonitor.get_value(charger, '/Dc/0/Voltage') - if v is None: - continue - - charger_batteryvoltage = v - charger_batteryvoltage_service = charger - - i = self._dbusmonitor.get_value(charger, '/Dc/0/Current') - if i is None: - continue - - if '/Dc/Charger/Power' not in newvalues: - newvalues['/Dc/Charger/Power'] = v * i - else: - newvalues['/Dc/Charger/Power'] += v * i - - # ==== VE.Direct Inverters ==== - _vedirect_inverters = sorted((di, s) for s, di in self._dbusmonitor.get_service_list('com.victronenergy.inverter').items()) - vedirect_inverters = [x[1] for x in _vedirect_inverters] - vedirect_inverter = None - if vedirect_inverters: - vedirect_inverter = vedirect_inverters[0] - - # For RS Smart inverters, add PV to the yield - for i in vedirect_inverters: - pv_yield = self._dbusmonitor.get_value(i, "/Yield/Power") - if pv_yield is not None: - newvalues['/Dc/Pv/Power'] = newvalues.get('/Dc/Pv/Power', 0) + pv_yield - - - # Used lower down, possibly needed for battery values as well - dcsystems = self._dbusmonitor.get_service_list('com.victronenergy.dcsystem') - - # ==== BATTERY ==== - if self._batteryservice is not None: - batteryservicetype = self._batteryservice.split('.')[2] - assert batteryservicetype in ('battery', 'vebus', 'inverter') - - newvalues['/Dc/Battery/Soc'] = self._dbusmonitor.get_value(self._batteryservice,'/Soc') - newvalues['/Dc/Battery/TimeToGo'] = self._dbusmonitor.get_value(self._batteryservice,'/TimeToGo') - newvalues['/Dc/Battery/ConsumedAmphours'] = self._dbusmonitor.get_value(self._batteryservice,'/ConsumedAmphours') - newvalues['/Dc/Battery/ProductId'] = self._dbusmonitor.get_value(self._batteryservice, '/ProductId') - - if batteryservicetype in ('battery', 'inverter'): - newvalues['/Dc/Battery/Voltage'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Voltage') - newvalues['/Dc/Battery/VoltageService'] = self._batteryservice - newvalues['/Dc/Battery/Current'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Current') - newvalues['/Dc/Battery/Power'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Power') - - elif batteryservicetype == 'vebus': - vebus_voltage = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Voltage') - vebus_current = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Current') - vebus_power = None if vebus_voltage is None or vebus_current is None else vebus_current * vebus_voltage - newvalues['/Dc/Battery/Voltage'] = vebus_voltage - newvalues['/Dc/Battery/VoltageService'] = self._batteryservice - if self._settings['hasdcsystem'] == 1 or dcsystems: - # hasdcsystem will normally disqualify the multi from being - # auto-selected as battery monitor, so the only way we're - # here is if the user explicitly selected the multi as the - # battery service - newvalues['/Dc/Battery/Current'] = vebus_current - if vebus_power is not None: - newvalues['/Dc/Battery/Power'] = vebus_power - else: - battery_power = _safeadd(solarchargers_charge_power, vebus_power) - newvalues['/Dc/Battery/Current'] = battery_power / vebus_voltage if vebus_voltage is not None and vebus_voltage > 0 else None - newvalues['/Dc/Battery/Power'] = battery_power - - - p = newvalues.get('/Dc/Battery/Power', None) - if p is not None: - if p > 30: - newvalues['/Dc/Battery/State'] = self.STATE_CHARGING - elif p < -30: - newvalues['/Dc/Battery/State'] = self.STATE_DISCHARGING - else: - newvalues['/Dc/Battery/State'] = self.STATE_IDLE - - else: - # The battery service is not a BMS/BMV or a suitable vebus. A - # suitable vebus is defined as one explicitly selected by the user, - # or one that was automatically selected for SOC tracking. We may - # however still have a VE.Bus, just not one that can accurately - # track SOC. If we have one, use it as voltage source. Otherwise - # try a solar charger, a charger, a vedirect inverter or a dcsource - # as fallbacks. - batteryservicetype = None - vebusses = self._dbusmonitor.get_service_list('com.victronenergy.vebus') - for vebus in vebusses: - v = self._dbusmonitor.get_value(vebus, '/Dc/0/Voltage') - s = self._dbusmonitor.get_value(vebus, '/State') - if v is not None and s not in (0, None): - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = vebus - break # Skip the else below - else: - # No suitable vebus voltage, try other devices - if solarcharger_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = solarcharger_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = solarcharger_batteryvoltage_service - elif charger_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = charger_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = charger_batteryvoltage_service - elif fuelcell_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = fuelcell_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = fuelcell_batteryvoltage_service - elif vedirect_inverter is not None: - v = self._dbusmonitor.get_value(vedirect_inverter, '/Dc/0/Voltage') - if v is not None: - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = vedirect_inverter - elif dcsystems: - # Get voltage from first dcsystem - s = next(iter(dcsystems.keys())) - v = self._dbusmonitor.get_value(s, '/Dc/0/Voltage') - if v is not None: - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = s - - # We have no suitable battery monitor, so power and current data - # is not available. We can however calculate it from other values, - # if we have at least a battery voltage. - if '/Dc/Battery/Voltage' in newvalues: - dcsystempower = _safeadd(0, *(self._dbusmonitor.get_value(s, - '/Dc/0/Power', 0) for s in dcsystems)) - if dcsystems or self._settings['hasdcsystem'] == 0: - # Either DC loads are monitored, or there are no - # unmonitored DC loads or chargers: derive battery watts - # and amps from vebus, solarchargers, chargers and measured - # loads. - p = solarchargers_charge_power + newvalues.get('/Dc/Charger/Power', 0) + vebuspower - dcsystempower - voltage = newvalues['/Dc/Battery/Voltage'] - newvalues['/Dc/Battery/Current'] = p / voltage if voltage > 0 else None - newvalues['/Dc/Battery/Power'] = p - - # ==== SYSTEM POWER ==== - # Look for dcsytem devices, add them together. Otherwise, if enabled, - # calculate it - if dcsystems: - newvalues['/Dc/System/MeasurementType'] = 1 # measured - newvalues['/Dc/System/Power'] = 0 - for meter in dcsystems: - newvalues['/Dc/System/Power'] = _safeadd(newvalues['/Dc/System/Power'], - self._dbusmonitor.get_value(meter, '/Dc/0/Power')) - elif self._settings['hasdcsystem'] == 1 and batteryservicetype == 'battery': - # Calculate power being generated/consumed by not measured devices in the network. - # For MPPTs, take all the power, including power going out of the load output. - # /Dc/System: positive: consuming power - # VE.Bus: Positive: current flowing from the Multi to the dc system or battery - # Solarcharger & other chargers: positive: charging - # battery: Positive: charging battery. - # battery = solarcharger + charger + ve.bus - system - - battery_power = newvalues.get('/Dc/Battery/Power') - if battery_power is not None: - dc_pv_power = newvalues.get('/Dc/Pv/Power', 0) - charger_power = newvalues.get('/Dc/Charger/Power', 0) - fuelcell_power = newvalues.get('/Dc/FuelCell/Power', 0) - - # If there are VE.Direct inverters, remove their power from the - # DC estimate. This is done using the AC value when the DC - # power values are not available. - inverter_power = 0 - for i in vedirect_inverters: - inverter_current = self._dbusmonitor.get_value(i, '/Dc/0/Current') - if inverter_current is not None: - inverter_power += self._dbusmonitor.get_value( - i, '/Dc/0/Voltage', 0) * inverter_current - else: - inverter_power += self._dbusmonitor.get_value( - i, '/Ac/Out/L1/V', 0) * self._dbusmonitor.get_value( - i, '/Ac/Out/L1/I', 0) - newvalues['/Dc/System/MeasurementType'] = 0 # estimated - newvalues['/Dc/System/Power'] = dc_pv_power + charger_power + fuelcell_power + vebuspower - inverter_power - battery_power - - elif self._settings['hasdcsystem'] == 1 and solarchargers_loadoutput_power is not None: - newvalues['/Dc/System/MeasurementType'] = 0 # estimated - newvalues['/Dc/System/Power'] = solarchargers_loadoutput_power - - # ==== Vebus ==== - multi_path = getattr(delegates.Multi.instance.multi, 'service', None) - if multi_path is not None: - dc_current = self._dbusmonitor.get_value(multi_path, '/Dc/0/Current') - newvalues['/Dc/Vebus/Current'] = dc_current - dc_power = self._dbusmonitor.get_value(multi_path, '/Dc/0/Power') - # Just in case /Dc/0/Power is not available - if dc_power == None and dc_current is not None: - dc_voltage = self._dbusmonitor.get_value(multi_path, '/Dc/0/Voltage') - if dc_voltage is not None: - dc_power = dc_voltage * dc_current - # Note that there is also vebuspower, which is the total DC power summed over all multis. - # However, this value cannot be combined with /Dc/Multi/Current, because it does not make sense - # to add the Dc currents of all multis if they do not share the same DC voltage. - newvalues['/Dc/Vebus/Power'] = dc_power - - # ===== AC IN SOURCE ===== - ac_in_source = None - if multi_path is None: - # Check if we have an non-VE.Bus inverter. If yes, then ActiveInput - # is disconnected. - if vedirect_inverter is not None: - ac_in_source = 240 - else: - active_input = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/ActiveInput') - if active_input == 0xF0: - # Not connected - ac_in_source = 240 - elif active_input is not None: - settings_path = '/Settings/SystemSetup/AcInput%s' % (active_input + 1) - ac_in_source = self._dbusmonitor.get_value('com.victronenergy.settings', settings_path) - newvalues['/Ac/ActiveIn/Source'] = ac_in_source - - # ===== GRID METERS & CONSUMPTION ==== - grid_meter = delegates.AcInputs.instance.gridmeter - genset_meter = delegates.AcInputs.instance.gensetmeter - - # Make an educated guess as to what is being consumed from an AC source. If ac_in_source - # indicates grid, genset or shore, we use that. If the Multi is off, or disconnected through - # a relay assistant or otherwise, then assume the presence of a .grid or .genset service indicates - # presence of that AC source. If both are available, then give up. This decision making is here - # so the GUI has something to present even if the Multi is off. - ac_in_guess = ac_in_source - if ac_in_guess in (None, 0xF0): - if genset_meter is None and grid_meter is not None: - ac_in_guess = 1 - elif grid_meter is None and genset_meter is not None: - ac_in_guess = 2 - - consumption = { "L1" : None, "L2" : None, "L3" : None } - for device_type, em, _types in (('Grid', grid_meter, (1, 3)), ('Genset', genset_meter, (2,))): - # If a grid meter is present we use values from it. If not, we look at the multi. If it has - # AcIn1 or AcIn2 connected to the grid, we use those values. - # com.victronenergy.grid.??? indicates presence of an energy meter used as grid meter. - # com.victronenergy.vebus.???/Ac/ActiveIn/ActiveInput: decides which whether we look at AcIn1 - # or AcIn2 as possible grid connection. - uses_active_input = ac_in_source in _types - for phase in consumption: - p = None - pvpower = newvalues.get('/Ac/PvOn%s/%s/Power' % (device_type, phase)) - if em is not None: - p = self._dbusmonitor.get_value(em.service, '/Ac/%s/Power' % phase) - # Compute consumption between energy meter and multi (meter power - multi AC in) and - # add an optional PV inverter on input to the mix. - c = None - if uses_active_input: - ac_in = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/P' % phase) - if ac_in is not None: - c = _safeadd(c, -ac_in) - # If there's any power coming from a PV inverter in the inactive AC in (which is unlikely), - # it will still be used, because there may also be a load in the same ACIn consuming - # power, or the power could be fed back to the net. - c = _safeadd(c, p, pvpower) - consumption[phase] = _safeadd(consumption[phase], _safemax(0, c)) - else: - if uses_active_input: - p = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/P' % phase) - if p is not None: - consumption[phase] = _safeadd(0, consumption[phase]) - # No relevant energy meter present. Assume there is no load between the grid and the multi. - # There may be a PV inverter present though (Hub-3 setup). - if pvpower != None: - p = _safeadd(p, -pvpower) - newvalues['/Ac/%s/%s/Power' % (device_type, phase)] = p - if ac_in_guess in _types: - newvalues['/Ac/ActiveIn/%s/Power' % (phase,)] = p - - self._compute_number_of_phases('/Ac/%s' % device_type, newvalues) - self._compute_number_of_phases('/Ac/ActiveIn', newvalues) - - product_id = None - device_type_id = None - if em is not None: - product_id = em.product_id - device_type_id = em.device_type - if product_id is None and uses_active_input: - product_id = self._dbusmonitor.get_value(multi_path, '/ProductId') - newvalues['/Ac/%s/ProductId' % device_type] = product_id - newvalues['/Ac/%s/DeviceType' % device_type] = device_type_id - - # If we have an ESS system and RunWithoutGridMeter is set, there cannot be load on the AC-In, so it - # must be on AC-Out. Hence we do calculate AC-Out consumption even if 'useacout' is disabled. - # Similarly all load are by definition on the output if this is not an ESS system. - use_ac_out = \ - self._settings['useacout'] == 1 or \ - (multi_path is not None and self._dbusmonitor.get_value(multi_path, '/Hub4/AssistantId') not in (4, 5)) or \ - self._dbusmonitor.get_value('com.victronenergy.settings', '/Settings/CGwacs/RunWithoutGridMeter') == 1 - for phase in consumption: - c = None - if use_ac_out: - c = newvalues.get('/Ac/PvOnOutput/%s/Power' % phase) - if multi_path is None: - for inv in vedirect_inverters: - ac_out = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/P' % phase) - - # Some models don't show power, calculate it - if ac_out is None: - i = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/I' % phase) - u = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/V' % phase) - if None not in (i, u): - ac_out = i * u - c = _safeadd(c, ac_out) - else: - ac_out = self._dbusmonitor.get_value(multi_path, '/Ac/Out/%s/P' % phase) - c = _safeadd(c, ac_out) - c = _safemax(0, c) - newvalues['/Ac/ConsumptionOnOutput/%s/Power' % phase] = c - newvalues['/Ac/ConsumptionOnInput/%s/Power' % phase] = consumption[phase] - newvalues['/Ac/Consumption/%s/Power' % phase] = _safeadd(consumption[phase], c) - self._compute_number_of_phases('/Ac/Consumption', newvalues) - self._compute_number_of_phases('/Ac/ConsumptionOnOutput', newvalues) - self._compute_number_of_phases('/Ac/ConsumptionOnInput', newvalues) - - for m in self._modules: - m.update_values(newvalues) - - # ==== UPDATE DBUS ITEMS ==== - with self._dbusservice as sss: - for path in self._summeditems.keys(): - # Why the None? Because we want to invalidate things we don't have anymore. - sss[path] = newvalues.get(path, None) - - def _handleservicechange(self): - # Update the available battery monitor services, used to populate the dropdown in the settings. - # Below code makes a dictionary. The key is [dbuserviceclass]/[deviceinstance]. For example - # "battery/245". The value is the name to show to the user in the dropdown. The full dbus- - # servicename, ie 'com.victronenergy.vebus.ttyO1' is not used, since the last part of that is not - # fixed. dbus-serviceclass name and the device instance are already fixed, so best to use those. - - services = self._get_connected_service_list('com.victronenergy.vebus') - services.update(self._get_connected_service_list('com.victronenergy.battery')) - services.update({k: v for k, v in self._get_connected_service_list( - 'com.victronenergy.inverter').items() if self._dbusmonitor.get_value(k, '/Soc') is not None}) - - ul = {self.BATSERVICE_DEFAULT: 'Automatic', self.BATSERVICE_NOBATTERY: 'No battery monitor'} - for servicename, instance in services.items(): - key = self._get_instance_service_name(servicename, instance) - ul[key] = self._get_readable_service_name(servicename) - self._dbusservice['/AvailableBatteryServices'] = json.dumps(ul) - - ul = {self.BATSERVICE_DEFAULT: 'Automatic', self.BATSERVICE_NOBATTERY: 'No battery monitor'} - # For later: for device supporting multiple Dc measurement we should add entries for /Dc/1 etc as - # well. - for servicename, instance in services.items(): - key = self._get_instance_service_name(servicename, instance).replace('.', '_').replace('/', '_') + '/Dc/0' - ul[key] = self._get_readable_service_name(servicename) - self._dbusservice['/AvailableBatteryMeasurements'] = ul - - self._determinebatteryservice() - self._updatepvinverterspidlist() - - self._changed = True - - def _get_readable_service_name(self, servicename): - return '%s on %s' % ( - self._dbusmonitor.get_value(servicename, '/ProductName'), - self._dbusmonitor.get_value(servicename, '/Mgmt/Connection')) - - def _get_instance_service_name(self, service, instance): - return '%s/%s' % ('.'.join(service.split('.')[0:3]), instance) - - def _remove_unconnected_services(self, services): - # Workaround: because com.victronenergy.vebus is available even when there is no vebus product - # connected, remove any service that is not connected. Previously we used - # /State since mandatory path /Connected is not implemented in mk2dbus, - # but this has since been resolved. - for servicename in list(services.keys()): - if (self._dbusmonitor.get_value(servicename, '/Connected') != 1 - or self._dbusmonitor.get_value(servicename, '/ProductName') is None - or self._dbusmonitor.get_value(servicename, '/Mgmt/Connection') is None): - del services[servicename] - - def _dbus_value_changed(self, dbusServiceName, dbusPath, dict, changes, deviceInstance): - self._changed = True - - # Workaround because com.victronenergy.vebus is available even when there is no vebus product - # connected. - if (dbusPath in ['/Connected', '/ProductName', '/Mgmt/Connection'] or - (dbusPath == '/State' and dbusServiceName.split('.')[0:3] == ['com', 'victronenergy', 'vebus'])): - self._handleservicechange() - - # Track the timezone changes - if dbusPath == '/Settings/System/TimeZone': - tz = changes.get('Value') - if tz is not None: - os.environ['TZ'] = tz - - def _device_added(self, service, instance, do_service_change=True): - if do_service_change: - self._handleservicechange() - - for m in self._modules: - m.device_added(service, instance, do_service_change) - - def _device_removed(self, service, instance): - self._handleservicechange() - - for m in self._modules: - m.device_removed(service, instance) - - def _gettext(self, path, value): - if path == '/Dc/Battery/State': - state = {self.STATE_IDLE: 'Idle', self.STATE_CHARGING: 'Charging', - self.STATE_DISCHARGING: 'Discharging'} - return state[value] - item = self._summeditems.get(path) - if item is not None: - return item['gettext'] % value - return str(value) - - def _compute_number_of_phases(self, path, newvalues): - number_of_phases = None - for phase in range(1, 4): - p = newvalues.get('%s/L%s/Power' % (path, phase)) - if p is not None: - number_of_phases = phase - newvalues[path + '/NumberOfPhases'] = number_of_phases - - def _get_connected_service_list(self, classfilter=None): - services = self._dbusmonitor.get_service_list(classfilter=classfilter) - self._remove_unconnected_services(services) - return services - - # returns a servicename string - def _get_first_connected_service(self, classfilter): - services = self._get_connected_service_list(classfilter=classfilter) - if len(services) == 0: - return None - return next(iter(services.items()), (None,))[0] - - # returns a tuple (servicename, instance) - def _get_service_having_lowest_instance(self, classfilter=None): - services = self._get_connected_service_list(classfilter=classfilter) - if len(services) == 0: - return None - - # sort the dict by value; returns list of tuples: (value, key) - s = sorted((value, key) for (key, value) in services.items()) - return (s[0][1], s[0][0]) - - -class DbusSystemCalc(SystemCalc): - def _create_dbus_monitor(self, *args, **kwargs): - return DbusMonitor(*args, **kwargs) - - def _create_settings(self, *args, **kwargs): - bus = dbus.SessionBus() if 'DBUS_SESSION_BUS_ADDRESS' in os.environ else dbus.SystemBus() - return SettingsDevice(bus, *args, timeout=10, **kwargs) - - def _create_dbus_service(self): - venusversion, venusbuildtime = self._get_venus_versioninfo() - - dbusservice = VeDbusService('com.victronenergy.system') - dbusservice.add_mandatory_paths( - processname=__file__, - processversion=softwareVersion, - connection='data from other dbus processes', - deviceinstance=0, - productid=None, - productname=None, - firmwareversion=venusversion, - hardwareversion=None, - connected=1) - dbusservice.add_path('/FirmwareBuild', value=venusbuildtime) - return dbusservice - - def _get_venus_versioninfo(self): - try: - with open("/opt/victronenergy/version", "r") as fp: - version, software, buildtime = fp.read().split('\n')[:3] - major, minor, _, rev = re.compile('v([0-9]*)\.([0-9]*)(~([0-9]*))?').match(version).groups() - return (int(major, 16)<<16)+(int(minor, 16)<<8)+(0 if rev is None else int(rev, 16)), buildtime - except Exception: - pass - return 0, '0' - -if __name__ == "__main__": - # Argument parsing - parser = argparse.ArgumentParser( - description='Converts readings from AC-Sensors connected to a VE.Bus device in a pvinverter ' + - 'D-Bus service.' - ) - - parser.add_argument("-d", "--debug", help="set logging level to debug", - action="store_true") - - args = parser.parse_args() - - print("-------- dbus_systemcalc, v" + softwareVersion + " is starting up --------") - logger = setup_logging(args.debug) - - # Have a mainloop, so we can send/receive asynchronous calls to and from dbus - DBusGMainLoop(set_as_default=True) - - systemcalc = DbusSystemCalc() - - # Start and run the mainloop - logger.info("Starting mainloop, responding only on events") - mainloop = GLib.MainLoop() - mainloop.run() diff --git a/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v2.89 b/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v2.89 deleted file mode 100755 index eb1763fb..00000000 --- a/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v2.89 +++ /dev/null @@ -1,1305 +0,0 @@ -#!/usr/bin/python3 -u -# -*- coding: utf-8 -*- - -#### modified for GuiMods - -from dbus.mainloop.glib import DBusGMainLoop -import dbus -import argparse -import sys -import os -import json -import time -import re -from gi.repository import GLib - -# Victron packages -sys.path.insert(1, os.path.join(os.path.dirname(__file__), 'ext', 'velib_python')) -from vedbus import VeDbusService -from ve_utils import get_vrm_portal_id, exit_on_error -from dbusmonitor import DbusMonitor -from settingsdevice import SettingsDevice -from logger import setup_logging -import delegates -from sc_utils import safeadd as _safeadd, safemax as _safemax - -softwareVersion = '2.94' - -class SystemCalc: - STATE_IDLE = 0 - STATE_CHARGING = 1 - STATE_DISCHARGING = 2 - BATSERVICE_DEFAULT = 'default' - BATSERVICE_NOBATTERY = 'nobattery' - def __init__(self): - # Why this dummy? Because DbusMonitor expects these values to be there, even though we don't - # need them. So just add some dummy data. This can go away when DbusMonitor is more generic. - dummy = {'code': None, 'whenToLog': 'configChange', 'accessLevel': None} - dbus_tree = { - 'com.victronenergy.solarcharger': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Load/I': dummy, - '/FirmwareVersion': dummy}, - 'com.victronenergy.pvinverter': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, - '/Position': dummy, - '/ProductId': dummy}, - 'com.victronenergy.battery': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/DeviceInstance': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/1/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Soc': dummy, - '/Sense/Current': dummy, - '/TimeToGo': dummy, - '/ConsumedAmphours': dummy, - '/ProductId': dummy, - '/CustomName': dummy}, - 'com.victronenergy.vebus' : { - '/Ac/ActiveIn/ActiveInput': dummy, - '/Ac/ActiveIn/L1/P': dummy, - '/Ac/ActiveIn/L2/P': dummy, - '/Ac/ActiveIn/L3/P': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L2/P': dummy, - '/Ac/Out/L3/P': dummy, -#### add for GuiMods - '/Ac/Out/L1/I': dummy, - '/Ac/Out/L2/I': dummy, - '/Ac/Out/L3/I': dummy, - '/Ac/Out/L1/V': dummy, - '/Ac/Out/L2/V': dummy, - '/Ac/Out/L3/V': dummy, - '/Ac/Out/L1/F': dummy, - '/Ac/Out/L2/F': dummy, - '/Ac/Out/L3/F': dummy, - '/Ac/ActiveIn/L1/V': dummy, - '/Ac/ActiveIn/L2/V': dummy, - '/Ac/ActiveIn/L3/V': dummy, - '/Ac/ActiveIn/L1/F': dummy, - '/Ac/ActiveIn/L2/F': dummy, - '/Ac/ActiveIn/L3/F': dummy, - - '/Connected': dummy, - '/ProductId': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Mode': dummy, - '/State': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.fuelcell': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy}, - 'com.victronenergy.charger': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/1/Voltage': dummy, - '/Dc/1/Current': dummy, - '/Dc/2/Voltage': dummy, - '/Dc/2/Current': dummy}, - 'com.victronenergy.grid' : { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/ProductId' : dummy, - '/DeviceType' : dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, -#### add for GuiMods - '/Ac/L1/Current': dummy, - '/Ac/L2/Current': dummy, - '/Ac/L3/Current': dummy, - '/Ac/L1/Voltage': dummy, - '/Ac/L2/Voltage': dummy, - '/Ac/L3/Voltage': dummy, - '/Ac/L1/Frequency': dummy, - '/Ac/L2/Frequency': dummy, - '/Ac/L3/Frequency': dummy}, - 'com.victronenergy.genset' : { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/ProductId' : dummy, - '/DeviceType' : dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, -#### add for GuiMods - '/Ac/L1/Current': dummy, - '/Ac/L2/Current': dummy, - '/Ac/L3/Current': dummy, - '/Ac/L1/Voltage': dummy, - '/Ac/L2/Voltage': dummy, - '/Ac/L3/Voltage': dummy, - '/Ac/L1/Frequency': dummy, - '/Ac/L2/Frequency': dummy, - '/Ac/L3/Frequency': dummy, - - '/StarterVoltage': dummy}, - 'com.victronenergy.settings' : { - '/Settings/SystemSetup/AcInput1' : dummy, - '/Settings/SystemSetup/AcInput2' : dummy, - '/Settings/CGwacs/RunWithoutGridMeter' : dummy, - '/Settings/System/TimeZone' : dummy}, - 'com.victronenergy.temperature': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy}, - 'com.victronenergy.inverter': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Ac/Out/L1/P': dummy, -#### add for GuiMods - '/Ac/Out/L1/V': dummy, - '/Ac/Out/L1/I': dummy, - '/Ac/Out/L1/F': dummy, - - '/Yield/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.multi': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Ac/ActiveIn/ActiveInput': dummy, - '/Ac/In/1/Type': dummy, - '/Ac/In/2/Type': dummy, - '/Ac/In/1/L1/P': dummy, - '/Ac/In/2/L1/P': dummy, - '/Ac/Out/L1/P': dummy, -#### add for GuiMods - '/Ac/Out/L1/V': dummy, - '/Ac/Out/L1/I': dummy, - '/Ac/Out/L1/I': dummy, - '/Ac/L1/F': dummy, - - '/Yield/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.dcsystem': { - '/Dc/0/Voltage': dummy, - '/Dc/0/Power': dummy - } - } - - self._modules = [ - delegates.Multi(), - delegates.HubTypeSelect(), - delegates.VebusSocWriter(), - delegates.ServiceMapper(), - delegates.RelayState(), - delegates.BuzzerControl(), - delegates.LgCircuitBreakerDetect(), - delegates.Dvcc(self), - delegates.BatterySense(self), - delegates.BatterySettings(self), - delegates.SystemState(self), - delegates.BatteryLife(), - delegates.ScheduledCharging(), - delegates.SourceTimers(), - #delegates.BydCurrentSense(self), - delegates.BatteryData(), - delegates.Gps(), - delegates.AcInputs(), - delegates.GensetStartStop(), - delegates.SocSync(self)] - - for m in self._modules: - for service, paths in m.get_input(): - s = dbus_tree.setdefault(service, {}) - for path in paths: - s[path] = dummy - - self._dbusmonitor = self._create_dbus_monitor(dbus_tree, valueChangedCallback=self._dbus_value_changed, - deviceAddedCallback=self._device_added, deviceRemovedCallback=self._device_removed) - - # Connect to localsettings - supported_settings = { - 'batteryservice': ['/Settings/SystemSetup/BatteryService', self.BATSERVICE_DEFAULT, 0, 0], - 'hasdcsystem': ['/Settings/SystemSetup/HasDcSystem', 0, 0, 1], - 'useacout': ['/Settings/SystemSetup/HasAcOutSystem', 1, 0, 1]} - - for m in self._modules: - for setting in m.get_settings(): - supported_settings[setting[0]] = list(setting[1:]) - - self._settings = self._create_settings(supported_settings, self._handlechangedsetting) - - self._dbusservice = self._create_dbus_service() - - for m in self._modules: - m.set_sources(self._dbusmonitor, self._settings, self._dbusservice) - - # At this moment, VRM portal ID is the MAC address of the CCGX. Anyhow, it should be string uniquely - # identifying the CCGX. - self._dbusservice.add_path('/Serial', value=get_vrm_portal_id()) - self._dbusservice.add_path( - '/AvailableBatteryServices', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/AvailableBatteryMeasurements', value=None) - self._dbusservice.add_path( - '/AutoSelectedBatteryService', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/AutoSelectedBatteryMeasurement', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/ActiveBatteryService', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/Dc/Battery/BatteryService', value=None) - self._dbusservice.add_path( - '/PvInvertersProductIds', value=None) - self._summeditems = { - '/Ac/Grid/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L3/Power': {'gettext': '%.0F W'}, - - '/Ac/Grid/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Grid/ProductId': {'gettext': '%s'}, - '/Ac/Grid/DeviceType': {'gettext': '%s'}, - '/Ac/Genset/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Genset/ProductId': {'gettext': '%s'}, - '/Ac/Genset/DeviceType': {'gettext': '%s'}, - '/Ac/ConsumptionOnOutput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Consumption/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L1/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/L2/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/L1/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/L2/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/L3/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/L1/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/L2/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/L3/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/NumberOfPhases': {'gettext': '%d'}, - '/Dc/Pv/Power': {'gettext': '%.0F W'}, - '/Dc/Pv/Current': {'gettext': '%.1F A'}, - '/Dc/Battery/Voltage': {'gettext': '%.2F V'}, - '/Dc/Battery/VoltageService': {'gettext': '%s'}, - '/Dc/Battery/Current': {'gettext': '%.1F A'}, - '/Dc/Battery/Power': {'gettext': '%.0F W'}, - '/Dc/Battery/Soc': {'gettext': '%.0F %%'}, - '/Dc/Battery/State': {'gettext': '%s'}, - '/Dc/Battery/TimeToGo': {'gettext': '%.0F s'}, - '/Dc/Battery/ConsumedAmphours': {'gettext': '%.1F Ah'}, - '/Dc/Battery/ProductId': {'gettext': '0x%x'}, - '/Dc/Charger/Power': {'gettext': '%.0F %%'}, - '/Dc/FuelCell/Power': {'gettext': '%.0F %%'}, - '/Dc/Vebus/Current': {'gettext': '%.1F A'}, - '/Dc/Vebus/Power': {'gettext': '%.0F W'}, - '/Dc/System/Power': {'gettext': '%.0F W'}, - '/Dc/System/MeasurementType': {'gettext': '%d'}, - '/Ac/ActiveIn/Source': {'gettext': '%s'}, - '/Ac/ActiveIn/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/L3/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/NumberOfPhases': {'gettext': '%d'}, -#### added for GuiMods - '/Ac/Grid/L1/Current': {'gettext': '%.1F A'}, - '/Ac/Grid/L2/Current': {'gettext': '%.1F A'}, - '/Ac/Grid/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Grid/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/Grid/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/Grid/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/Grid/Frequency': {'gettext': '%.1F Hz'}, - '/Ac/Genset/L1/Current': {'gettext': '%.1F A'}, - '/Ac/Genset/L2/Current': {'gettext': '%.1F A'}, - '/Ac/Genset/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Genset/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/Genset/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/Genset/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/Genset/Frequency': {'gettext': '%.1F Hz'}, - '/Ac/ConsumptionOnOutput/L1/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnOutput/L2/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnOutput/L3/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnOutput/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnOutput/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnOutput/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnOutput/Frequency': {'gettext': '%.1F Hz'}, - '/Ac/ConsumptionOnInput/L1/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnInput/L2/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnInput/L3/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnInput/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnInput/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnInput/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnInput/Frequency': {'gettext': '%.1F Hz'}, - '/Ac/Consumption/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/Consumption/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/Consumption/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/Consumption/L1/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/L2/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/Frequency': {'gettext': '%.1F Hz'}, - '/Ac/ActiveIn/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/ActiveIn/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/ActiveIn/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/ActiveIn/L1/Current': {'gettext': '%.1F A'}, - '/Ac/ActiveIn/L2/Current': {'gettext': '%.1F A'}, - '/Ac/ActiveIn/L3/Current': {'gettext': '%.1F A'}, - '/Ac/ActiveIn/Frequency': {'gettext': '%.1F Hz'}, - - } - - for m in self._modules: - self._summeditems.update(m.get_output()) - - for path in self._summeditems.keys(): - self._dbusservice.add_path(path, value=None, gettextcallback=self._gettext) - - self._batteryservice = None - self._determinebatteryservice() - - if self._batteryservice is None: - logger.info("Battery service initialized to None (setting == %s)" % - self._settings['batteryservice']) - - self._changed = True - for service, instance in self._dbusmonitor.get_service_list().items(): - self._device_added(service, instance, do_service_change=False) - - self._handleservicechange() - self._updatevalues() - - GLib.timeout_add(1000, exit_on_error, self._handletimertick) - - 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): - raise Exception("This function should be overridden") - - def _handlechangedsetting(self, setting, oldvalue, newvalue): - self._determinebatteryservice() - self._changed = True - - # Give our delegates a chance to react on a settings change - for m in self._modules: - m.settings_changed(setting, oldvalue, newvalue) - - def _find_device_instance(self, serviceclass, instance): - """ Gets a mapping of services vs DeviceInstance using - get_service_list. Then searches for the specified DeviceInstance - and returns the service name. """ - services = self._dbusmonitor.get_service_list(classfilter=serviceclass) - - for k, v in services.items(): - if v == instance: - return k - return None - - def _determinebatteryservice(self): - auto_battery_service = self._autoselect_battery_service() - auto_battery_measurement = None - auto_selected = False - if auto_battery_service is not None: - services = self._dbusmonitor.get_service_list() - if auto_battery_service in services: - auto_battery_measurement = \ - self._get_instance_service_name(auto_battery_service, services[auto_battery_service]) - auto_battery_measurement = auto_battery_measurement.replace('.', '_').replace('/', '_') + '/Dc/0' - self._dbusservice['/AutoSelectedBatteryMeasurement'] = auto_battery_measurement - - if self._settings['batteryservice'] == self.BATSERVICE_DEFAULT: - auto_selected = True - newbatteryservice = auto_battery_service - self._dbusservice['/AutoSelectedBatteryService'] = ( - 'No battery monitor found' if newbatteryservice is None else - self._get_readable_service_name(newbatteryservice)) - - elif self._settings['batteryservice'] == self.BATSERVICE_NOBATTERY: - self._dbusservice['/AutoSelectedBatteryService'] = None - newbatteryservice = None - - else: - self._dbusservice['/AutoSelectedBatteryService'] = None - - s = self._settings['batteryservice'].split('/') - if len(s) != 2: - logger.error("The battery setting (%s) is invalid!" % self._settings['batteryservice']) - serviceclass = s[0] - instance = int(s[1]) if len(s) == 2 else None - - # newbatteryservice might turn into None if a chosen battery - # monitor no longer exists. Don't auto change the setting (it might - # come back) and don't autoselect another. - newbatteryservice = self._find_device_instance(serviceclass, instance) - - if newbatteryservice != self._batteryservice: - services = self._dbusmonitor.get_service_list() - instance = services.get(newbatteryservice, None) - if instance is None: - battery_service = None - else: - battery_service = self._get_instance_service_name(newbatteryservice, instance) - self._dbusservice['/ActiveBatteryService'] = battery_service - logger.info("Battery service, setting == %s, changed from %s to %s (%s)" % - (self._settings['batteryservice'], self._batteryservice, newbatteryservice, instance)) - - # Battery service has changed. Notify delegates. - for m in self._modules: - m.battery_service_changed(auto_selected, self._batteryservice, newbatteryservice) - self._dbusservice['/Dc/Battery/BatteryService'] = self._batteryservice = newbatteryservice - - def _autoselect_battery_service(self): - # Default setting business logic: - # first try to use a battery service (BMV or Lynx Shunt VE.Can). If there - # is more than one battery service, just use a random one. If no battery service is - # available, check if there are not Solar chargers and no normal chargers. If they are not - # there, assume this is a hub-2, hub-3 or hub-4 system and use VE.Bus SOC. - batteries = self._get_connected_service_list('com.victronenergy.battery') - - # Pick the first battery service - if len(batteries) > 0: - return sorted(batteries)[0] - - # No battery services, and there is a charger in the system. Abandon - # hope. - if self._get_first_connected_service('com.victronenergy.charger') is not None: - return None - - # Also no Multi, then give up. - vebus_service = self._get_service_having_lowest_instance('com.victronenergy.vebus') - if vebus_service is None: - # No VE.Bus, but maybe there is an inverter with built-in SOC - # tracking, eg RS Smart or Multi RS. - inverter = self._get_service_having_lowest_instance('com.victronenergy.multi') - if inverter and self._dbusmonitor.get_value(inverter[0], '/Soc') is not None: - return inverter[0] - - inverter = self._get_service_having_lowest_instance('com.victronenergy.inverter') - if inverter and self._dbusmonitor.get_value(inverter[0], '/Soc') is not None: - return inverter[0] - - return None - - # There is a Multi, it supports tracking external charge current from - # solarchargers, and there are no DC loads. Then use it. - if self._dbusmonitor.get_value( - vebus_service[0], '/ExtraBatteryCurrent') is not None \ - and self._get_first_connected_service('com.victronenergy.dcsystem') is None \ - and self._settings['hasdcsystem'] == 0: - return vebus_service[0] - - # Multi does not support tracking solarcharger current, and we have - # solar chargers. Then we cannot use it. - if self._get_first_connected_service('com.victronenergy.solarcharger') is not None: - return None - - # Only a Multi, no other chargers. Then we can use it. - return vebus_service[0] - - @property - def batteryservice(self): - return self._batteryservice - - # Called on a one second timer - def _handletimertick(self): - if self._changed: - self._updatevalues() - self._changed = False - - return True # keep timer running - - def _updatepvinverterspidlist(self): - # Create list of connected pv inverters id's - pvinverters = self._dbusmonitor.get_service_list('com.victronenergy.pvinverter') - productids = [] - - for pvinverter in pvinverters: - pid = self._dbusmonitor.get_value(pvinverter, '/ProductId') - if pid is not None and pid not in productids: - productids.append(pid) - self._dbusservice['/PvInvertersProductIds'] = productids - - def _updatevalues(self): - # ==== PREPARATIONS ==== - newvalues = {} - - # Set the user timezone - if 'TZ' not in os.environ: - tz = self._dbusmonitor.get_value('com.victronenergy.settings', '/Settings/System/TimeZone') - if tz is not None: - os.environ['TZ'] = tz - time.tzset() - - # Determine values used in logic below - vebusses = self._dbusmonitor.get_service_list('com.victronenergy.vebus') - vebuspower = 0 - for vebus in vebusses: - v = self._dbusmonitor.get_value(vebus, '/Dc/0/Voltage') - i = self._dbusmonitor.get_value(vebus, '/Dc/0/Current') - if v is not None and i is not None: - vebuspower += v * i - - # ==== PVINVERTERS ==== - pvinverters = self._dbusmonitor.get_service_list('com.victronenergy.pvinverter') - pos = {0: '/Ac/PvOnGrid', 1: '/Ac/PvOnOutput', 2: '/Ac/PvOnGenset'} - for pvinverter in pvinverters: - # Position will be None if PV inverter service has just been removed (after retrieving the - # service list). - position = pos.get(self._dbusmonitor.get_value(pvinverter, '/Position')) - if position is not None: - for phase in range(1, 4): - power = self._dbusmonitor.get_value(pvinverter, '/Ac/L%s/Power' % phase) - if power is not None: - path = '%s/L%s/Power' % (position, phase) - newvalues[path] = _safeadd(newvalues.get(path), power) - - for path in pos.values(): - self._compute_number_of_phases(path, newvalues) - - # ==== SOLARCHARGERS ==== - solarchargers = self._dbusmonitor.get_service_list('com.victronenergy.solarcharger') - solarcharger_batteryvoltage = None - solarcharger_batteryvoltage_service = None - solarchargers_charge_power = 0 - solarchargers_loadoutput_power = None - - for solarcharger in solarchargers: - v = self._dbusmonitor.get_value(solarcharger, '/Dc/0/Voltage') - if v is None: - continue - i = self._dbusmonitor.get_value(solarcharger, '/Dc/0/Current') - if i is None: - continue - l = self._dbusmonitor.get_value(solarcharger, '/Load/I', 0) - - if l is not None: - if solarchargers_loadoutput_power is None: - solarchargers_loadoutput_power = l * v - else: - solarchargers_loadoutput_power += l * v - - solarchargers_charge_power += v * i - - # Note that this path is not in the _summeditems{}, making for it to not be - # published on D-Bus. Which fine. The only one needing it is the vebussocwriter- - # delegate. - if '/Dc/Pv/ChargeCurrent' not in newvalues: - newvalues['/Dc/Pv/ChargeCurrent'] = i - else: - newvalues['/Dc/Pv/ChargeCurrent'] += i - - if '/Dc/Pv/Power' not in newvalues: - newvalues['/Dc/Pv/Power'] = v * _safeadd(i, l) - newvalues['/Dc/Pv/Current'] = _safeadd(i, l) - solarcharger_batteryvoltage = v - solarcharger_batteryvoltage_service = solarcharger - else: - newvalues['/Dc/Pv/Power'] += v * _safeadd(i, l) - newvalues['/Dc/Pv/Current'] += _safeadd(i, l) - - # ==== FUELCELLS ==== - fuelcells = self._dbusmonitor.get_service_list('com.victronenergy.fuelcell') - fuelcell_batteryvoltage = None - fuelcell_batteryvoltage_service = None - for fuelcell in fuelcells: - # Assume the battery connected to output 0 is the main battery - v = self._dbusmonitor.get_value(fuelcell, '/Dc/0/Voltage') - if v is None: - continue - - fuelcell_batteryvoltage = v - fuelcell_batteryvoltage_service = fuelcell - - i = self._dbusmonitor.get_value(fuelcell, '/Dc/0/Current') - if i is None: - continue - - if '/Dc/FuelCell/Power' not in newvalues: - newvalues['/Dc/FuelCell/Power'] = v * i - else: - newvalues['/Dc/FuelCell/Power'] += v * i - - # ==== CHARGERS ==== - chargers = self._dbusmonitor.get_service_list('com.victronenergy.charger') - charger_batteryvoltage = None - charger_batteryvoltage_service = None - for charger in chargers: - # Assume the battery connected to output 0 is the main battery - v = self._dbusmonitor.get_value(charger, '/Dc/0/Voltage') - if v is None: - continue - - charger_batteryvoltage = v - charger_batteryvoltage_service = charger - - i = self._dbusmonitor.get_value(charger, '/Dc/0/Current') - if i is None: - continue - - if '/Dc/Charger/Power' not in newvalues: - newvalues['/Dc/Charger/Power'] = v * i - else: - newvalues['/Dc/Charger/Power'] += v * i - - # ==== Other Inverters and Inverter/Chargers ==== - _other_inverters = sorted((di, s) for s, di in self._dbusmonitor.get_service_list('com.victronenergy.multi').items()) + \ - sorted((di, s) for s, di in self._dbusmonitor.get_service_list('com.victronenergy.inverter').items()) - non_vebus_inverters = [x[1] for x in _other_inverters] - non_vebus_inverter = None - if non_vebus_inverters: - non_vebus_inverter = non_vebus_inverters[0] - - # For RS Smart and Multi RS, add PV to the yield - for i in non_vebus_inverters: - if (pv_yield := self._dbusmonitor.get_value(i, "/Yield/Power")) is not None: - newvalues['/Dc/Pv/Power'] = newvalues.get('/Dc/Pv/Power', 0) + pv_yield - - # Used lower down, possibly needed for battery values as well - dcsystems = self._dbusmonitor.get_service_list('com.victronenergy.dcsystem') - - # ==== BATTERY ==== - if self._batteryservice is not None: - batteryservicetype = self._batteryservice.split('.')[2] - assert batteryservicetype in ('battery', 'vebus', 'inverter', 'multi') - - newvalues['/Dc/Battery/Soc'] = self._dbusmonitor.get_value(self._batteryservice,'/Soc') - newvalues['/Dc/Battery/TimeToGo'] = self._dbusmonitor.get_value(self._batteryservice,'/TimeToGo') - newvalues['/Dc/Battery/ConsumedAmphours'] = self._dbusmonitor.get_value(self._batteryservice,'/ConsumedAmphours') - newvalues['/Dc/Battery/ProductId'] = self._dbusmonitor.get_value(self._batteryservice, '/ProductId') - - if batteryservicetype in ('battery', 'inverter', 'multi'): - newvalues['/Dc/Battery/Voltage'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Voltage') - newvalues['/Dc/Battery/VoltageService'] = self._batteryservice - newvalues['/Dc/Battery/Current'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Current') - newvalues['/Dc/Battery/Power'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Power') - - elif batteryservicetype == 'vebus': - vebus_voltage = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Voltage') - vebus_current = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Current') - vebus_power = None if vebus_voltage is None or vebus_current is None else vebus_current * vebus_voltage - newvalues['/Dc/Battery/Voltage'] = vebus_voltage - newvalues['/Dc/Battery/VoltageService'] = self._batteryservice - if self._settings['hasdcsystem'] == 1 or dcsystems: - # hasdcsystem will normally disqualify the multi from being - # auto-selected as battery monitor, so the only way we're - # here is if the user explicitly selected the multi as the - # battery service - newvalues['/Dc/Battery/Current'] = vebus_current - if vebus_power is not None: - newvalues['/Dc/Battery/Power'] = vebus_power - else: - battery_power = _safeadd(solarchargers_charge_power, vebus_power) - newvalues['/Dc/Battery/Current'] = battery_power / vebus_voltage if vebus_voltage is not None and vebus_voltage > 0 else None - newvalues['/Dc/Battery/Power'] = battery_power - - - p = newvalues.get('/Dc/Battery/Power', None) - if p is not None: - if p > 30: - newvalues['/Dc/Battery/State'] = self.STATE_CHARGING - elif p < -30: - newvalues['/Dc/Battery/State'] = self.STATE_DISCHARGING - else: - newvalues['/Dc/Battery/State'] = self.STATE_IDLE - - else: - # The battery service is not a BMS/BMV or a suitable vebus. A - # suitable vebus is defined as one explicitly selected by the user, - # or one that was automatically selected for SOC tracking. We may - # however still have a VE.Bus, just not one that can accurately - # track SOC. If we have one, use it as voltage source. Otherwise - # try a solar charger, a charger, a vedirect inverter or a dcsource - # as fallbacks. - batteryservicetype = None - vebusses = self._dbusmonitor.get_service_list('com.victronenergy.vebus') - for vebus in vebusses: - v = self._dbusmonitor.get_value(vebus, '/Dc/0/Voltage') - s = self._dbusmonitor.get_value(vebus, '/State') - if v is not None and s not in (0, None): - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = vebus - break # Skip the else below - else: - # No suitable vebus voltage, try other devices - if non_vebus_inverter is not None and (v := self._dbusmonitor.get_value(non_vebus_inverter, '/Dc/0/Voltage')) is not None: - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = non_vebus_inverter - elif solarcharger_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = solarcharger_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = solarcharger_batteryvoltage_service - elif charger_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = charger_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = charger_batteryvoltage_service - elif fuelcell_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = fuelcell_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = fuelcell_batteryvoltage_service - elif dcsystems: - # Get voltage from first dcsystem - s = next(iter(dcsystems.keys())) - v = self._dbusmonitor.get_value(s, '/Dc/0/Voltage') - if v is not None: - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = s - - # We have no suitable battery monitor, so power and current data - # is not available. We can however calculate it from other values, - # if we have at least a battery voltage. - if '/Dc/Battery/Voltage' in newvalues: - dcsystempower = _safeadd(0, *(self._dbusmonitor.get_value(s, - '/Dc/0/Power', 0) for s in dcsystems)) - if dcsystems or self._settings['hasdcsystem'] == 0: - # Either DC loads are monitored, or there are no - # unmonitored DC loads or chargers: derive battery watts - # and amps from vebus, solarchargers, chargers and measured - # loads. - p = solarchargers_charge_power + newvalues.get('/Dc/Charger/Power', 0) + vebuspower - dcsystempower - voltage = newvalues['/Dc/Battery/Voltage'] - newvalues['/Dc/Battery/Current'] = p / voltage if voltage > 0 else None - newvalues['/Dc/Battery/Power'] = p - - # ==== SYSTEM POWER ==== - # Look for dcsytem devices, add them together. Otherwise, if enabled, - # calculate it - if dcsystems: - newvalues['/Dc/System/MeasurementType'] = 1 # measured - newvalues['/Dc/System/Power'] = 0 - for meter in dcsystems: - newvalues['/Dc/System/Power'] = _safeadd(newvalues['/Dc/System/Power'], - self._dbusmonitor.get_value(meter, '/Dc/0/Power')) - elif self._settings['hasdcsystem'] == 1 and batteryservicetype == 'battery': - # Calculate power being generated/consumed by not measured devices in the network. - # For MPPTs, take all the power, including power going out of the load output. - # /Dc/System: positive: consuming power - # VE.Bus: Positive: current flowing from the Multi to the dc system or battery - # Solarcharger & other chargers: positive: charging - # battery: Positive: charging battery. - # battery = solarcharger + charger + ve.bus - system - - battery_power = newvalues.get('/Dc/Battery/Power') - if battery_power is not None: - dc_pv_power = newvalues.get('/Dc/Pv/Power', 0) - charger_power = newvalues.get('/Dc/Charger/Power', 0) - fuelcell_power = newvalues.get('/Dc/FuelCell/Power', 0) - - # If there are VE.Direct inverters, remove their power from the - # DC estimate. This is done using the AC value when the DC - # power values are not available. - inverter_power = 0 - for i in non_vebus_inverters: - inverter_current = self._dbusmonitor.get_value(i, '/Dc/0/Current') - if inverter_current is not None: - inverter_power += self._dbusmonitor.get_value( - i, '/Dc/0/Voltage', 0) * inverter_current - else: - inverter_power += self._dbusmonitor.get_value( - i, '/Ac/Out/L1/V', 0) * self._dbusmonitor.get_value( - i, '/Ac/Out/L1/I', 0) - newvalues['/Dc/System/MeasurementType'] = 0 # estimated - newvalues['/Dc/System/Power'] = dc_pv_power + charger_power + fuelcell_power + vebuspower - inverter_power - battery_power - - elif self._settings['hasdcsystem'] == 1 and solarchargers_loadoutput_power is not None: - newvalues['/Dc/System/MeasurementType'] = 0 # estimated - newvalues['/Dc/System/Power'] = solarchargers_loadoutput_power - - # ==== Vebus ==== - multi_path = getattr(delegates.Multi.instance.multi, 'service', None) - if multi_path is not None: - dc_current = self._dbusmonitor.get_value(multi_path, '/Dc/0/Current') - newvalues['/Dc/Vebus/Current'] = dc_current - dc_power = self._dbusmonitor.get_value(multi_path, '/Dc/0/Power') - # Just in case /Dc/0/Power is not available - if dc_power == None and dc_current is not None: - dc_voltage = self._dbusmonitor.get_value(multi_path, '/Dc/0/Voltage') - if dc_voltage is not None: - dc_power = dc_voltage * dc_current - # Note that there is also vebuspower, which is the total DC power summed over all multis. - # However, this value cannot be combined with /Dc/Multi/Current, because it does not make sense - # to add the Dc currents of all multis if they do not share the same DC voltage. - newvalues['/Dc/Vebus/Power'] = dc_power - - # ===== AC IN SOURCE ===== - ac_in_source = None - active_input = None - if multi_path is None: - # Check if we have an non-VE.Bus inverter. - if non_vebus_inverter is not None: - if (active_input := self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/ActiveIn/ActiveInput')) is not None and \ - active_input in (0, 1) and \ - (active_type := self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/In/{}/Type'.format(active_input + 1))) is not None: - ac_in_source = active_type - else: - ac_in_source = 240 - else: - active_input = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/ActiveInput') - if active_input == 0xF0: - # Not connected - ac_in_source = 240 - elif active_input is not None: - settings_path = '/Settings/SystemSetup/AcInput%s' % (active_input + 1) - ac_in_source = self._dbusmonitor.get_value('com.victronenergy.settings', settings_path) - newvalues['/Ac/ActiveIn/Source'] = ac_in_source - - # ===== GRID METERS & CONSUMPTION ==== -#### begin GuiMods replaced section - added current, voltage and frequency - grid_meter = delegates.AcInputs.instance.gridmeter - genset_meter = delegates.AcInputs.instance.gensetmeter - - # Make an educated guess as to what is being consumed from an AC source. If ac_in_source - # indicates grid, genset or shore, we use that. If the Multi is off, or disconnected through - # a relay assistant or otherwise, then assume the presence of a .grid or .genset service indicates - # presence of that AC source. If both are available, then give up. This decision making is here - # so the GUI has something to present even if the Multi is off. - ac_in_guess = ac_in_source - if ac_in_guess in (None, 0xF0): - if genset_meter is None and grid_meter is not None: - ac_in_guess = 1 - elif grid_meter is None and genset_meter is not None: - ac_in_guess = 2 - - consumption = { "L1" : None, "L2" : None, "L3" : None } -#### added for GuiMods - currentconsumption = { "L1" : None, "L2" : None, "L3" : None } - voltageIn = { "L1" : None, "L2" : None, "L3" : None } - voltageOut = { "L1" : None, "L2" : None, "L3" : None } - frequencyIn = None - frequencyOut = None - - for device_type, em, _types in (('Grid', grid_meter, (1, 3)), ('Genset', genset_meter, (2,))): - # If a grid meter is present we use values from it. If not, we look at the multi. If it has - # AcIn1 or AcIn2 connected to the grid, we use those values. - # com.victronenergy.grid.??? indicates presence of an energy meter used as grid meter. - # com.victronenergy.vebus.???/Ac/ActiveIn/ActiveInput: decides which whether we look at AcIn1 - # or AcIn2 as possible grid connection. - uses_active_input = ac_in_source in _types - for phase in consumption: - p = None - pvpower = newvalues.get('/Ac/PvOn%s/%s/Power' % (device_type, phase)) -#### added for GuiMods - mc = None - pvcurrent = newvalues.get('/Ac/PvOn%s/%s/Current' % (device_type, phase)) - if em is not None: - p = self._dbusmonitor.get_value(em.service, '/Ac/%s/Power' % phase) -#### added for GuiMods - mc = self._dbusmonitor.get_value(em.service, '/Ac/%s/Current' % phase) - if voltageIn[phase] == None: - voltageIn[phase] = self._dbusmonitor.get_value(em.service, '/Ac/%s/Voltage' % phase) - if frequencyIn == None: - frequencyIn = self._dbusmonitor.get_value(em.service, '/Ac/%s/Frequency' % phase) - - # Compute consumption between energy meter and multi (meter power - multi AC in) and - # add an optional PV inverter on input to the mix. - c = None -#### added for GuiMods - cc = None - if uses_active_input: - if multi_path is not None: - try: - c = _safeadd(c, -self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/P' % phase)) -#### added for GuiMods - cc = _safeadd(cc, -self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/I' % phase)) - if voltageIn[phase] == None: - voltageIn[phase] = self._dbusmonitor.get_value(em.service, '/Ac/ActiveIn/%s/V' % phase) - if frequencyIn == None: - frequencyIn = self._dbusmonitor.get_value(em.service, '/Ac/ActiveIn/%s/F' % phase) - - except TypeError: - pass - elif non_vebus_inverter is not None and active_input in (0, 1): - try: - c = _safeadd(c, -self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/In/%d/%s/P' % (active_input+1, phase))) -#### added for GuiMods - cc = _safeadd(cc, -self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/In/%d/%s/I' % (active_input+1, phase))) - if voltageIn[phase] == None: - voltageIn[phase] = self._dbusmonitor.get_value(em.service, '/Ac/In/%d/%s/V' % (active_input+1, phase)) - if frequencyIn == None: - frequencyIn = self._dbusmonitor.get_value(em.service, '/Ac/In/%d/%s/F' % (active_input+1, phase)) - - except TypeError: - pass - - # If there's any power coming from a PV inverter in the inactive AC in (which is unlikely), - # it will still be used, because there may also be a load in the same ACIn consuming - # power, or the power could be fed back to the net. - c = _safeadd(c, p, pvpower) - consumption[phase] = _safeadd(consumption[phase], _safemax(0, c)) -#### added for GuiMods - cc = _safeadd(cc, mc, pvcurrent) - currentconsumption[phase] = _safeadd(currentconsumption[phase], _safemax(0, cc)) - else: - if uses_active_input: - if multi_path is not None and ( - p := self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/P' % phase)) is not None: - consumption[phase] = _safeadd(0, consumption[phase]) -#### added for GuiMods - currentconsumption[phase] = _safeadd(0, currentconsumption[phase]) - mc = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/I' % phase) - if voltageIn[phase] == None: - voltageIn[phase] = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/V' % phase) - if frequencyIn == None: - freq = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/F' % phase) - if freq != None: - frequencyIn = freq - - elif non_vebus_inverter is not None and active_input in (0, 1): - p = self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/In/%d/%s/P' % (active_input + 1, phase)) -#### added for GuiMods - mc = self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/In/%d/%s/I' % (active_input + 1, phase)) - if voltageIn[phase] == None: - voltageIn[phase] = self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/In/%d/%s/V' % (active_input + 1, phase)) - if frequencyIn == None: - frequencyIn = self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/In/%d/%s/F' % (active_input + 1, phase)) - - if p is not None: - consumption[phase] = _safeadd(0, consumption[phase]) -#### added for GuiMods - currentconsumption[phase] = _safeadd(0, currentconsumption[phase]) - - # No relevant energy meter present. Assume there is no load between the grid and the multi. - # There may be a PV inverter present though (Hub-3 setup). - try: - p = _safeadd(p, -pvpower) -#### added for GuiMods - mc = _safeadd(mc, -pvcurrent) - except TypeError: - pass - - newvalues['/Ac/%s/%s/Power' % (device_type, phase)] = p -#### added for GuiMods - newvalues['/Ac/%s/%s/Current' % (device_type, phase)] = mc - if p != None: - newvalues['/Ac/%s/%s/Voltage' % (device_type, phase)] = voltageIn[phase] - newvalues['/Ac/%s/Frequency' % (device_type)] = frequencyIn - - if ac_in_guess in _types: - newvalues['/Ac/ActiveIn/%s/Power' % (phase,)] = p -#### added for GuiMods - newvalues['/Ac/ActiveIn/%s/Current' % (phase,)] = mc - if p != None: - newvalues['/Ac/ActiveIn/%s/Voltage' % (phase,)] = voltageIn[phase] - newvalues['/Ac/ActiveIn/Frequency'] = frequencyIn - - self._compute_number_of_phases('/Ac/%s' % device_type, newvalues) - self._compute_number_of_phases('/Ac/ActiveIn', newvalues) - - product_id = None - device_type_id = None - if em is not None: - product_id = em.product_id - device_type_id = em.device_type - if product_id is None and uses_active_input: - if multi_path is not None: - product_id = self._dbusmonitor.get_value(multi_path, '/ProductId') - elif non_vebus_inverter is not None: - product_id = self._dbusmonitor.get_value(non_vebus_inverter, '/ProductId') - newvalues['/Ac/%s/ProductId' % device_type] = product_id - newvalues['/Ac/%s/DeviceType' % device_type] = device_type_id - - # If we have an ESS system and RunWithoutGridMeter is set, there cannot be load on the AC-In, so it - # must be on AC-Out. Hence we do calculate AC-Out consumption even if 'useacout' is disabled. - # Similarly all load are by definition on the output if this is not an ESS system. - use_ac_out = \ - self._settings['useacout'] == 1 or \ - (multi_path is not None and self._dbusmonitor.get_value(multi_path, '/Hub4/AssistantId') not in (4, 5)) or \ - self._dbusmonitor.get_value('com.victronenergy.settings', '/Settings/CGwacs/RunWithoutGridMeter') == 1 - for phase in consumption: - c = None -#### added for GuiMods - a = None - if use_ac_out: - c = newvalues.get('/Ac/PvOnOutput/%s/Power' % phase) -#### added for GuiMods - a = newvalues.get('/Ac/PvOnOutput/%s/Current' % phase) - if voltageOut[phase] == None: - voltageOut[phase] = newvalues.get('/Ac/PvOnOutput/%s/Voltage' % phase) - if frequencyOut == None: - frequencyOut = newvalues.get('/Ac/PvOnOutput/%s/Frequency' % phase) - - if multi_path is None: - for inv in non_vebus_inverters: - ac_out = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/P' % phase) -#### added for GuiMods - i = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/I' % phase) - if voltageOut[phase] == None: - voltageOut[phase] = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/V' % phase) - if frequencyOut == None: - frequencyOut = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/F' % phase) - - # Some models don't show power, try apparent power, - # else calculate it - if ac_out is None: - ac_out = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/S' % phase) - if ac_out is None: -#### modified for GuiMods - # u = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/V' % phase) - if None not in (i, voltageOut[phase]): - ac_out = i * voltageOut[phase] - c = _safeadd(c, ac_out) -#### modified for GuiMods - a = _safeadd(a, i) - else: - ac_out = self._dbusmonitor.get_value(multi_path, '/Ac/Out/%s/P' % phase) - c = _safeadd(c, ac_out) -#### added for GuiMods - i_out = self._dbusmonitor.get_value(multi_path, '/Ac/Out/%s/I' % phase) - a = _safeadd(a, i_out) - if voltageOut[phase] == None: - voltageOut[phase] = self._dbusmonitor.get_value(multi_path, '/Ac/Out/%s/V' % phase) - if frequencyOut == None: - frequencyOut = self._dbusmonitor.get_value(multi_path, '/Ac/Out/%s/F' % phase) - c = _safemax(0, c) -#### added for GuiMods - a = _safemax(0, a) - newvalues['/Ac/ConsumptionOnOutput/%s/Power' % phase] = c - newvalues['/Ac/ConsumptionOnOutput/%s/Current' % phase] = a - newvalues['/Ac/ConsumptionOnInput/%s/Power' % phase] = consumption[phase] - newvalues['/Ac/ConsumptionOnInput/%s/Current' % phase] = currentconsumption[phase] - newvalues['/Ac/Consumption/%s/Power' % phase] = _safeadd(consumption[phase], c) -#### added for GuiMods - newvalues['/Ac/Consumption/%s/Current' % phase] = _safeadd(currentconsumption[phase], a) - newvalues['/Ac/ConsumptionOnOutput/%s/Voltage' % phase] = voltageOut[phase] - newvalues['/Ac/ConsumptionOnInput/%s/Voltage' % phase] = voltageIn[phase] - if voltageOut[phase] != None: - newvalues['/Ac/Consumption/%s/Voltage' % phase] = voltageOut[phase] - elif voltageIn[phase] != None: - newvalues['/Ac/Consumption/%s/Voltage' % phase] = voltageIn[phase] - if frequencyIn != None: - newvalues['/Ac/ConsumptionOnInput/Frequency'] = frequencyIn - if frequencyOut != None: - newvalues['/Ac/ConsumptionOnOutput/Frequency'] = frequencyOut - if frequencyOut != None: - newvalues['/Ac/Consumption/Frequency'] = frequencyOut - elif frequencyIn != None: - newvalues['/Ac/Consumption/Frequency'] = frequencyIn - -#### end GuiMods replaced section - self._compute_number_of_phases('/Ac/Consumption', newvalues) - self._compute_number_of_phases('/Ac/ConsumptionOnOutput', newvalues) - self._compute_number_of_phases('/Ac/ConsumptionOnInput', newvalues) - - for m in self._modules: - m.update_values(newvalues) - - # ==== UPDATE DBUS ITEMS ==== - with self._dbusservice as sss: - for path in self._summeditems.keys(): - # Why the None? Because we want to invalidate things we don't have anymore. - sss[path] = newvalues.get(path, None) - - def _handleservicechange(self): - # Update the available battery monitor services, used to populate the dropdown in the settings. - # Below code makes a dictionary. The key is [dbuserviceclass]/[deviceinstance]. For example - # "battery/245". The value is the name to show to the user in the dropdown. The full dbus- - # servicename, ie 'com.victronenergy.vebus.ttyO1' is not used, since the last part of that is not - # fixed. dbus-serviceclass name and the device instance are already fixed, so best to use those. - - services = self._get_connected_service_list('com.victronenergy.vebus') - services.update(self._get_connected_service_list('com.victronenergy.battery')) - services.update({k: v for k, v in self._get_connected_service_list( - 'com.victronenergy.multi').items() if self._dbusmonitor.get_value(k, '/Soc') is not None}) - services.update({k: v for k, v in self._get_connected_service_list( - 'com.victronenergy.inverter').items() if self._dbusmonitor.get_value(k, '/Soc') is not None}) - - ul = {self.BATSERVICE_DEFAULT: 'Automatic', self.BATSERVICE_NOBATTERY: 'No battery monitor'} - for servicename, instance in services.items(): - key = self._get_instance_service_name(servicename, instance) - ul[key] = self._get_readable_service_name(servicename) - self._dbusservice['/AvailableBatteryServices'] = json.dumps(ul) - - ul = {self.BATSERVICE_DEFAULT: 'Automatic', self.BATSERVICE_NOBATTERY: 'No battery monitor'} - # For later: for device supporting multiple Dc measurement we should add entries for /Dc/1 etc as - # well. - for servicename, instance in services.items(): - key = self._get_instance_service_name(servicename, instance).replace('.', '_').replace('/', '_') + '/Dc/0' - ul[key] = self._get_readable_service_name(servicename) - self._dbusservice['/AvailableBatteryMeasurements'] = ul - - self._determinebatteryservice() - self._updatepvinverterspidlist() - - self._changed = True - - def _get_readable_service_name(self, servicename): - return '%s on %s' % ( - self._dbusmonitor.get_value(servicename, '/ProductName'), - self._dbusmonitor.get_value(servicename, '/Mgmt/Connection')) - - def _get_instance_service_name(self, service, instance): - return '%s/%s' % ('.'.join(service.split('.')[0:3]), instance) - - def _remove_unconnected_services(self, services): - # Workaround: because com.victronenergy.vebus is available even when there is no vebus product - # connected, remove any service that is not connected. Previously we used - # /State since mandatory path /Connected is not implemented in mk2dbus, - # but this has since been resolved. - for servicename in list(services.keys()): - if (self._dbusmonitor.get_value(servicename, '/Connected') != 1 - or self._dbusmonitor.get_value(servicename, '/ProductName') is None - or self._dbusmonitor.get_value(servicename, '/Mgmt/Connection') is None): - del services[servicename] - - def _dbus_value_changed(self, dbusServiceName, dbusPath, dict, changes, deviceInstance): - self._changed = True - - # Workaround because com.victronenergy.vebus is available even when there is no vebus product - # connected. - if (dbusPath in ['/Connected', '/ProductName', '/Mgmt/Connection'] or - (dbusPath == '/State' and dbusServiceName.split('.')[0:3] == ['com', 'victronenergy', 'vebus'])): - self._handleservicechange() - - # Track the timezone changes - if dbusPath == '/Settings/System/TimeZone': - tz = changes.get('Value') - if tz is not None: - os.environ['TZ'] = tz - - def _device_added(self, service, instance, do_service_change=True): - if do_service_change: - self._handleservicechange() - - for m in self._modules: - m.device_added(service, instance, do_service_change) - - def _device_removed(self, service, instance): - self._handleservicechange() - - for m in self._modules: - m.device_removed(service, instance) - - def _gettext(self, path, value): - if path == '/Dc/Battery/State': - state = {self.STATE_IDLE: 'Idle', self.STATE_CHARGING: 'Charging', - self.STATE_DISCHARGING: 'Discharging'} - return state[value] - item = self._summeditems.get(path) - if item is not None: - return item['gettext'] % value - return str(value) - - def _compute_number_of_phases(self, path, newvalues): - number_of_phases = None - for phase in range(1, 4): - p = newvalues.get('%s/L%s/Power' % (path, phase)) - if p is not None: - number_of_phases = phase - newvalues[path + '/NumberOfPhases'] = number_of_phases - - def _get_connected_service_list(self, classfilter=None): - services = self._dbusmonitor.get_service_list(classfilter=classfilter) - self._remove_unconnected_services(services) - return services - - # returns a servicename string - def _get_first_connected_service(self, classfilter): - services = self._get_connected_service_list(classfilter=classfilter) - if len(services) == 0: - return None - return next(iter(services.items()), (None,))[0] - - # returns a tuple (servicename, instance) - def _get_service_having_lowest_instance(self, classfilter=None): - services = self._get_connected_service_list(classfilter=classfilter) - if len(services) == 0: - return None - - # sort the dict by value; returns list of tuples: (value, key) - s = sorted((value, key) for (key, value) in services.items()) - return (s[0][1], s[0][0]) - - -class DbusSystemCalc(SystemCalc): - def _create_dbus_monitor(self, *args, **kwargs): - return DbusMonitor(*args, **kwargs) - - def _create_settings(self, *args, **kwargs): - bus = dbus.SessionBus() if 'DBUS_SESSION_BUS_ADDRESS' in os.environ else dbus.SystemBus() - return SettingsDevice(bus, *args, timeout=10, **kwargs) - - def _create_dbus_service(self): - venusversion, venusbuildtime = self._get_venus_versioninfo() - - dbusservice = VeDbusService('com.victronenergy.system') - dbusservice.add_mandatory_paths( - processname=__file__, - processversion=softwareVersion, - connection='data from other dbus processes', - deviceinstance=0, - productid=None, - productname=None, - firmwareversion=venusversion, - hardwareversion=None, - connected=1) - dbusservice.add_path('/FirmwareBuild', value=venusbuildtime) - return dbusservice - - def _get_venus_versioninfo(self): - try: - with open("/opt/victronenergy/version", "r") as fp: - version, software, buildtime = fp.read().split('\n')[:3] - major, minor, _, rev = re.compile('v([0-9]*)\.([0-9]*)(~([0-9]*))?').match(version).groups() - return (int(major, 16)<<16)+(int(minor, 16)<<8)+(0 if rev is None else int(rev, 16)), buildtime - except Exception: - pass - return 0, '0' - -if __name__ == "__main__": - # Argument parsing - parser = argparse.ArgumentParser( - description='Converts readings from AC-Sensors connected to a VE.Bus device in a pvinverter ' + - 'D-Bus service.' - ) - - parser.add_argument("-d", "--debug", help="set logging level to debug", - action="store_true") - - args = parser.parse_args() - - print("-------- dbus_systemcalc, v" + softwareVersion + " is starting up --------") - logger = setup_logging(args.debug) - - # Have a mainloop, so we can send/receive asynchronous calls to and from dbus - DBusGMainLoop(set_as_default=True) - - systemcalc = DbusSystemCalc() - - # Start and run the mainloop - logger.info("Starting mainloop, responding only on events") - mainloop = GLib.MainLoop() - mainloop.run() diff --git a/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v2.89.orig b/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v2.89.orig deleted file mode 100755 index e94ecb0a..00000000 --- a/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v2.89.orig +++ /dev/null @@ -1,1085 +0,0 @@ -#!/usr/bin/python3 -u -# -*- coding: utf-8 -*- - -from dbus.mainloop.glib import DBusGMainLoop -import dbus -import argparse -import sys -import os -import json -import time -import re -from gi.repository import GLib - -# Victron packages -sys.path.insert(1, os.path.join(os.path.dirname(__file__), 'ext', 'velib_python')) -from vedbus import VeDbusService -from ve_utils import get_vrm_portal_id, exit_on_error -from dbusmonitor import DbusMonitor -from settingsdevice import SettingsDevice -from logger import setup_logging -import delegates -from sc_utils import safeadd as _safeadd, safemax as _safemax - -softwareVersion = '2.94' - -class SystemCalc: - STATE_IDLE = 0 - STATE_CHARGING = 1 - STATE_DISCHARGING = 2 - BATSERVICE_DEFAULT = 'default' - BATSERVICE_NOBATTERY = 'nobattery' - def __init__(self): - # Why this dummy? Because DbusMonitor expects these values to be there, even though we don't - # need them. So just add some dummy data. This can go away when DbusMonitor is more generic. - dummy = {'code': None, 'whenToLog': 'configChange', 'accessLevel': None} - dbus_tree = { - 'com.victronenergy.solarcharger': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Load/I': dummy, - '/FirmwareVersion': dummy}, - 'com.victronenergy.pvinverter': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, - '/Position': dummy, - '/ProductId': dummy}, - 'com.victronenergy.battery': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/DeviceInstance': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/1/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Soc': dummy, - '/Sense/Current': dummy, - '/TimeToGo': dummy, - '/ConsumedAmphours': dummy, - '/ProductId': dummy, - '/CustomName': dummy}, - 'com.victronenergy.vebus' : { - '/Ac/ActiveIn/ActiveInput': dummy, - '/Ac/ActiveIn/L1/P': dummy, - '/Ac/ActiveIn/L2/P': dummy, - '/Ac/ActiveIn/L3/P': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L2/P': dummy, - '/Ac/Out/L3/P': dummy, - '/Connected': dummy, - '/ProductId': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Mode': dummy, - '/State': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.fuelcell': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy}, - 'com.victronenergy.charger': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/1/Voltage': dummy, - '/Dc/1/Current': dummy, - '/Dc/2/Voltage': dummy, - '/Dc/2/Current': dummy}, - 'com.victronenergy.grid' : { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/ProductId' : dummy, - '/DeviceType' : dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy}, - 'com.victronenergy.genset' : { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/ProductId' : dummy, - '/DeviceType' : dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, - '/StarterVoltage': dummy}, - 'com.victronenergy.settings' : { - '/Settings/SystemSetup/AcInput1' : dummy, - '/Settings/SystemSetup/AcInput2' : dummy, - '/Settings/CGwacs/RunWithoutGridMeter' : dummy, - '/Settings/System/TimeZone' : dummy}, - 'com.victronenergy.temperature': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy}, - 'com.victronenergy.inverter': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L1/V': dummy, - '/Ac/Out/L1/I': dummy, - '/Yield/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.multi': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Ac/ActiveIn/ActiveInput': dummy, - '/Ac/In/1/Type': dummy, - '/Ac/In/2/Type': dummy, - '/Ac/In/1/L1/P': dummy, - '/Ac/In/2/L1/P': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L1/V': dummy, - '/Ac/Out/L1/I': dummy, - '/Yield/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.dcsystem': { - '/Dc/0/Voltage': dummy, - '/Dc/0/Power': dummy - } - } - - self._modules = [ - delegates.Multi(), - delegates.HubTypeSelect(), - delegates.VebusSocWriter(), - delegates.ServiceMapper(), - delegates.RelayState(), - delegates.BuzzerControl(), - delegates.LgCircuitBreakerDetect(), - delegates.Dvcc(self), - delegates.BatterySense(self), - delegates.BatterySettings(self), - delegates.SystemState(self), - delegates.BatteryLife(), - delegates.ScheduledCharging(), - delegates.SourceTimers(), - #delegates.BydCurrentSense(self), - delegates.BatteryData(), - delegates.Gps(), - delegates.AcInputs(), - delegates.GensetStartStop(), - delegates.SocSync(self)] - - for m in self._modules: - for service, paths in m.get_input(): - s = dbus_tree.setdefault(service, {}) - for path in paths: - s[path] = dummy - - self._dbusmonitor = self._create_dbus_monitor(dbus_tree, valueChangedCallback=self._dbus_value_changed, - deviceAddedCallback=self._device_added, deviceRemovedCallback=self._device_removed) - - # Connect to localsettings - supported_settings = { - 'batteryservice': ['/Settings/SystemSetup/BatteryService', self.BATSERVICE_DEFAULT, 0, 0], - 'hasdcsystem': ['/Settings/SystemSetup/HasDcSystem', 0, 0, 1], - 'useacout': ['/Settings/SystemSetup/HasAcOutSystem', 1, 0, 1]} - - for m in self._modules: - for setting in m.get_settings(): - supported_settings[setting[0]] = list(setting[1:]) - - self._settings = self._create_settings(supported_settings, self._handlechangedsetting) - - self._dbusservice = self._create_dbus_service() - - for m in self._modules: - m.set_sources(self._dbusmonitor, self._settings, self._dbusservice) - - # At this moment, VRM portal ID is the MAC address of the CCGX. Anyhow, it should be string uniquely - # identifying the CCGX. - self._dbusservice.add_path('/Serial', value=get_vrm_portal_id()) - self._dbusservice.add_path( - '/AvailableBatteryServices', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/AvailableBatteryMeasurements', value=None) - self._dbusservice.add_path( - '/AutoSelectedBatteryService', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/AutoSelectedBatteryMeasurement', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/ActiveBatteryService', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/Dc/Battery/BatteryService', value=None) - self._dbusservice.add_path( - '/PvInvertersProductIds', value=None) - self._summeditems = { - '/Ac/Grid/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Grid/ProductId': {'gettext': '%s'}, - '/Ac/Grid/DeviceType': {'gettext': '%s'}, - '/Ac/Genset/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Genset/ProductId': {'gettext': '%s'}, - '/Ac/Genset/DeviceType': {'gettext': '%s'}, - '/Ac/ConsumptionOnOutput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Consumption/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/L1/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/L2/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/L3/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/L1/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/L2/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/L3/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/NumberOfPhases': {'gettext': '%d'}, - '/Dc/Pv/Power': {'gettext': '%.0F W'}, - '/Dc/Pv/Current': {'gettext': '%.1F A'}, - '/Dc/Battery/Voltage': {'gettext': '%.2F V'}, - '/Dc/Battery/VoltageService': {'gettext': '%s'}, - '/Dc/Battery/Current': {'gettext': '%.1F A'}, - '/Dc/Battery/Power': {'gettext': '%.0F W'}, - '/Dc/Battery/Soc': {'gettext': '%.0F %%'}, - '/Dc/Battery/State': {'gettext': '%s'}, - '/Dc/Battery/TimeToGo': {'gettext': '%.0F s'}, - '/Dc/Battery/ConsumedAmphours': {'gettext': '%.1F Ah'}, - '/Dc/Battery/ProductId': {'gettext': '0x%x'}, - '/Dc/Charger/Power': {'gettext': '%.0F %%'}, - '/Dc/FuelCell/Power': {'gettext': '%.0F %%'}, - '/Dc/Vebus/Current': {'gettext': '%.1F A'}, - '/Dc/Vebus/Power': {'gettext': '%.0F W'}, - '/Dc/System/Power': {'gettext': '%.0F W'}, - '/Dc/System/MeasurementType': {'gettext': '%d'}, - '/Ac/ActiveIn/Source': {'gettext': '%s'}, - '/Ac/ActiveIn/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/L3/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/NumberOfPhases': {'gettext': '%d'}, - } - - for m in self._modules: - self._summeditems.update(m.get_output()) - - for path in self._summeditems.keys(): - self._dbusservice.add_path(path, value=None, gettextcallback=self._gettext) - - self._batteryservice = None - self._determinebatteryservice() - - if self._batteryservice is None: - logger.info("Battery service initialized to None (setting == %s)" % - self._settings['batteryservice']) - - self._changed = True - for service, instance in self._dbusmonitor.get_service_list().items(): - self._device_added(service, instance, do_service_change=False) - - self._handleservicechange() - self._updatevalues() - - GLib.timeout_add(1000, exit_on_error, self._handletimertick) - - 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): - raise Exception("This function should be overridden") - - def _handlechangedsetting(self, setting, oldvalue, newvalue): - self._determinebatteryservice() - self._changed = True - - # Give our delegates a chance to react on a settings change - for m in self._modules: - m.settings_changed(setting, oldvalue, newvalue) - - def _find_device_instance(self, serviceclass, instance): - """ Gets a mapping of services vs DeviceInstance using - get_service_list. Then searches for the specified DeviceInstance - and returns the service name. """ - services = self._dbusmonitor.get_service_list(classfilter=serviceclass) - - for k, v in services.items(): - if v == instance: - return k - return None - - def _determinebatteryservice(self): - auto_battery_service = self._autoselect_battery_service() - auto_battery_measurement = None - auto_selected = False - if auto_battery_service is not None: - services = self._dbusmonitor.get_service_list() - if auto_battery_service in services: - auto_battery_measurement = \ - self._get_instance_service_name(auto_battery_service, services[auto_battery_service]) - auto_battery_measurement = auto_battery_measurement.replace('.', '_').replace('/', '_') + '/Dc/0' - self._dbusservice['/AutoSelectedBatteryMeasurement'] = auto_battery_measurement - - if self._settings['batteryservice'] == self.BATSERVICE_DEFAULT: - auto_selected = True - newbatteryservice = auto_battery_service - self._dbusservice['/AutoSelectedBatteryService'] = ( - 'No battery monitor found' if newbatteryservice is None else - self._get_readable_service_name(newbatteryservice)) - - elif self._settings['batteryservice'] == self.BATSERVICE_NOBATTERY: - self._dbusservice['/AutoSelectedBatteryService'] = None - newbatteryservice = None - - else: - self._dbusservice['/AutoSelectedBatteryService'] = None - - s = self._settings['batteryservice'].split('/') - if len(s) != 2: - logger.error("The battery setting (%s) is invalid!" % self._settings['batteryservice']) - serviceclass = s[0] - instance = int(s[1]) if len(s) == 2 else None - - # newbatteryservice might turn into None if a chosen battery - # monitor no longer exists. Don't auto change the setting (it might - # come back) and don't autoselect another. - newbatteryservice = self._find_device_instance(serviceclass, instance) - - if newbatteryservice != self._batteryservice: - services = self._dbusmonitor.get_service_list() - instance = services.get(newbatteryservice, None) - if instance is None: - battery_service = None - else: - battery_service = self._get_instance_service_name(newbatteryservice, instance) - self._dbusservice['/ActiveBatteryService'] = battery_service - logger.info("Battery service, setting == %s, changed from %s to %s (%s)" % - (self._settings['batteryservice'], self._batteryservice, newbatteryservice, instance)) - - # Battery service has changed. Notify delegates. - for m in self._modules: - m.battery_service_changed(auto_selected, self._batteryservice, newbatteryservice) - self._dbusservice['/Dc/Battery/BatteryService'] = self._batteryservice = newbatteryservice - - def _autoselect_battery_service(self): - # Default setting business logic: - # first try to use a battery service (BMV or Lynx Shunt VE.Can). If there - # is more than one battery service, just use a random one. If no battery service is - # available, check if there are not Solar chargers and no normal chargers. If they are not - # there, assume this is a hub-2, hub-3 or hub-4 system and use VE.Bus SOC. - batteries = self._get_connected_service_list('com.victronenergy.battery') - - # Pick the first battery service - if len(batteries) > 0: - return sorted(batteries)[0] - - # No battery services, and there is a charger in the system. Abandon - # hope. - if self._get_first_connected_service('com.victronenergy.charger') is not None: - return None - - # Also no Multi, then give up. - vebus_service = self._get_service_having_lowest_instance('com.victronenergy.vebus') - if vebus_service is None: - # No VE.Bus, but maybe there is an inverter with built-in SOC - # tracking, eg RS Smart or Multi RS. - inverter = self._get_service_having_lowest_instance('com.victronenergy.multi') - if inverter and self._dbusmonitor.get_value(inverter[0], '/Soc') is not None: - return inverter[0] - - inverter = self._get_service_having_lowest_instance('com.victronenergy.inverter') - if inverter and self._dbusmonitor.get_value(inverter[0], '/Soc') is not None: - return inverter[0] - - return None - - # There is a Multi, it supports tracking external charge current from - # solarchargers, and there are no DC loads. Then use it. - if self._dbusmonitor.get_value( - vebus_service[0], '/ExtraBatteryCurrent') is not None \ - and self._get_first_connected_service('com.victronenergy.dcsystem') is None \ - and self._settings['hasdcsystem'] == 0: - return vebus_service[0] - - # Multi does not support tracking solarcharger current, and we have - # solar chargers. Then we cannot use it. - if self._get_first_connected_service('com.victronenergy.solarcharger') is not None: - return None - - # Only a Multi, no other chargers. Then we can use it. - return vebus_service[0] - - @property - def batteryservice(self): - return self._batteryservice - - # Called on a one second timer - def _handletimertick(self): - if self._changed: - self._updatevalues() - self._changed = False - - return True # keep timer running - - def _updatepvinverterspidlist(self): - # Create list of connected pv inverters id's - pvinverters = self._dbusmonitor.get_service_list('com.victronenergy.pvinverter') - productids = [] - - for pvinverter in pvinverters: - pid = self._dbusmonitor.get_value(pvinverter, '/ProductId') - if pid is not None and pid not in productids: - productids.append(pid) - self._dbusservice['/PvInvertersProductIds'] = productids - - def _updatevalues(self): - # ==== PREPARATIONS ==== - newvalues = {} - - # Set the user timezone - if 'TZ' not in os.environ: - tz = self._dbusmonitor.get_value('com.victronenergy.settings', '/Settings/System/TimeZone') - if tz is not None: - os.environ['TZ'] = tz - time.tzset() - - # Determine values used in logic below - vebusses = self._dbusmonitor.get_service_list('com.victronenergy.vebus') - vebuspower = 0 - for vebus in vebusses: - v = self._dbusmonitor.get_value(vebus, '/Dc/0/Voltage') - i = self._dbusmonitor.get_value(vebus, '/Dc/0/Current') - if v is not None and i is not None: - vebuspower += v * i - - # ==== PVINVERTERS ==== - pvinverters = self._dbusmonitor.get_service_list('com.victronenergy.pvinverter') - pos = {0: '/Ac/PvOnGrid', 1: '/Ac/PvOnOutput', 2: '/Ac/PvOnGenset'} - for pvinverter in pvinverters: - # Position will be None if PV inverter service has just been removed (after retrieving the - # service list). - position = pos.get(self._dbusmonitor.get_value(pvinverter, '/Position')) - if position is not None: - for phase in range(1, 4): - power = self._dbusmonitor.get_value(pvinverter, '/Ac/L%s/Power' % phase) - if power is not None: - path = '%s/L%s/Power' % (position, phase) - newvalues[path] = _safeadd(newvalues.get(path), power) - - for path in pos.values(): - self._compute_number_of_phases(path, newvalues) - - # ==== SOLARCHARGERS ==== - solarchargers = self._dbusmonitor.get_service_list('com.victronenergy.solarcharger') - solarcharger_batteryvoltage = None - solarcharger_batteryvoltage_service = None - solarchargers_charge_power = 0 - solarchargers_loadoutput_power = None - - for solarcharger in solarchargers: - v = self._dbusmonitor.get_value(solarcharger, '/Dc/0/Voltage') - if v is None: - continue - i = self._dbusmonitor.get_value(solarcharger, '/Dc/0/Current') - if i is None: - continue - l = self._dbusmonitor.get_value(solarcharger, '/Load/I', 0) - - if l is not None: - if solarchargers_loadoutput_power is None: - solarchargers_loadoutput_power = l * v - else: - solarchargers_loadoutput_power += l * v - - solarchargers_charge_power += v * i - - # Note that this path is not in the _summeditems{}, making for it to not be - # published on D-Bus. Which fine. The only one needing it is the vebussocwriter- - # delegate. - if '/Dc/Pv/ChargeCurrent' not in newvalues: - newvalues['/Dc/Pv/ChargeCurrent'] = i - else: - newvalues['/Dc/Pv/ChargeCurrent'] += i - - if '/Dc/Pv/Power' not in newvalues: - newvalues['/Dc/Pv/Power'] = v * _safeadd(i, l) - newvalues['/Dc/Pv/Current'] = _safeadd(i, l) - solarcharger_batteryvoltage = v - solarcharger_batteryvoltage_service = solarcharger - else: - newvalues['/Dc/Pv/Power'] += v * _safeadd(i, l) - newvalues['/Dc/Pv/Current'] += _safeadd(i, l) - - # ==== FUELCELLS ==== - fuelcells = self._dbusmonitor.get_service_list('com.victronenergy.fuelcell') - fuelcell_batteryvoltage = None - fuelcell_batteryvoltage_service = None - for fuelcell in fuelcells: - # Assume the battery connected to output 0 is the main battery - v = self._dbusmonitor.get_value(fuelcell, '/Dc/0/Voltage') - if v is None: - continue - - fuelcell_batteryvoltage = v - fuelcell_batteryvoltage_service = fuelcell - - i = self._dbusmonitor.get_value(fuelcell, '/Dc/0/Current') - if i is None: - continue - - if '/Dc/FuelCell/Power' not in newvalues: - newvalues['/Dc/FuelCell/Power'] = v * i - else: - newvalues['/Dc/FuelCell/Power'] += v * i - - # ==== CHARGERS ==== - chargers = self._dbusmonitor.get_service_list('com.victronenergy.charger') - charger_batteryvoltage = None - charger_batteryvoltage_service = None - for charger in chargers: - # Assume the battery connected to output 0 is the main battery - v = self._dbusmonitor.get_value(charger, '/Dc/0/Voltage') - if v is None: - continue - - charger_batteryvoltage = v - charger_batteryvoltage_service = charger - - i = self._dbusmonitor.get_value(charger, '/Dc/0/Current') - if i is None: - continue - - if '/Dc/Charger/Power' not in newvalues: - newvalues['/Dc/Charger/Power'] = v * i - else: - newvalues['/Dc/Charger/Power'] += v * i - - # ==== Other Inverters and Inverter/Chargers ==== - _other_inverters = sorted((di, s) for s, di in self._dbusmonitor.get_service_list('com.victronenergy.multi').items()) + \ - sorted((di, s) for s, di in self._dbusmonitor.get_service_list('com.victronenergy.inverter').items()) - non_vebus_inverters = [x[1] for x in _other_inverters] - non_vebus_inverter = None - if non_vebus_inverters: - non_vebus_inverter = non_vebus_inverters[0] - - # For RS Smart and Multi RS, add PV to the yield - for i in non_vebus_inverters: - if (pv_yield := self._dbusmonitor.get_value(i, "/Yield/Power")) is not None: - newvalues['/Dc/Pv/Power'] = newvalues.get('/Dc/Pv/Power', 0) + pv_yield - - # Used lower down, possibly needed for battery values as well - dcsystems = self._dbusmonitor.get_service_list('com.victronenergy.dcsystem') - - # ==== BATTERY ==== - if self._batteryservice is not None: - batteryservicetype = self._batteryservice.split('.')[2] - assert batteryservicetype in ('battery', 'vebus', 'inverter', 'multi') - - newvalues['/Dc/Battery/Soc'] = self._dbusmonitor.get_value(self._batteryservice,'/Soc') - newvalues['/Dc/Battery/TimeToGo'] = self._dbusmonitor.get_value(self._batteryservice,'/TimeToGo') - newvalues['/Dc/Battery/ConsumedAmphours'] = self._dbusmonitor.get_value(self._batteryservice,'/ConsumedAmphours') - newvalues['/Dc/Battery/ProductId'] = self._dbusmonitor.get_value(self._batteryservice, '/ProductId') - - if batteryservicetype in ('battery', 'inverter', 'multi'): - newvalues['/Dc/Battery/Voltage'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Voltage') - newvalues['/Dc/Battery/VoltageService'] = self._batteryservice - newvalues['/Dc/Battery/Current'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Current') - newvalues['/Dc/Battery/Power'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Power') - - elif batteryservicetype == 'vebus': - vebus_voltage = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Voltage') - vebus_current = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Current') - vebus_power = None if vebus_voltage is None or vebus_current is None else vebus_current * vebus_voltage - newvalues['/Dc/Battery/Voltage'] = vebus_voltage - newvalues['/Dc/Battery/VoltageService'] = self._batteryservice - if self._settings['hasdcsystem'] == 1 or dcsystems: - # hasdcsystem will normally disqualify the multi from being - # auto-selected as battery monitor, so the only way we're - # here is if the user explicitly selected the multi as the - # battery service - newvalues['/Dc/Battery/Current'] = vebus_current - if vebus_power is not None: - newvalues['/Dc/Battery/Power'] = vebus_power - else: - battery_power = _safeadd(solarchargers_charge_power, vebus_power) - newvalues['/Dc/Battery/Current'] = battery_power / vebus_voltage if vebus_voltage is not None and vebus_voltage > 0 else None - newvalues['/Dc/Battery/Power'] = battery_power - - - p = newvalues.get('/Dc/Battery/Power', None) - if p is not None: - if p > 30: - newvalues['/Dc/Battery/State'] = self.STATE_CHARGING - elif p < -30: - newvalues['/Dc/Battery/State'] = self.STATE_DISCHARGING - else: - newvalues['/Dc/Battery/State'] = self.STATE_IDLE - - else: - # The battery service is not a BMS/BMV or a suitable vebus. A - # suitable vebus is defined as one explicitly selected by the user, - # or one that was automatically selected for SOC tracking. We may - # however still have a VE.Bus, just not one that can accurately - # track SOC. If we have one, use it as voltage source. Otherwise - # try a solar charger, a charger, a vedirect inverter or a dcsource - # as fallbacks. - batteryservicetype = None - vebusses = self._dbusmonitor.get_service_list('com.victronenergy.vebus') - for vebus in vebusses: - v = self._dbusmonitor.get_value(vebus, '/Dc/0/Voltage') - s = self._dbusmonitor.get_value(vebus, '/State') - if v is not None and s not in (0, None): - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = vebus - break # Skip the else below - else: - # No suitable vebus voltage, try other devices - if non_vebus_inverter is not None and (v := self._dbusmonitor.get_value(non_vebus_inverter, '/Dc/0/Voltage')) is not None: - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = non_vebus_inverter - elif solarcharger_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = solarcharger_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = solarcharger_batteryvoltage_service - elif charger_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = charger_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = charger_batteryvoltage_service - elif fuelcell_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = fuelcell_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = fuelcell_batteryvoltage_service - elif dcsystems: - # Get voltage from first dcsystem - s = next(iter(dcsystems.keys())) - v = self._dbusmonitor.get_value(s, '/Dc/0/Voltage') - if v is not None: - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = s - - # We have no suitable battery monitor, so power and current data - # is not available. We can however calculate it from other values, - # if we have at least a battery voltage. - if '/Dc/Battery/Voltage' in newvalues: - dcsystempower = _safeadd(0, *(self._dbusmonitor.get_value(s, - '/Dc/0/Power', 0) for s in dcsystems)) - if dcsystems or self._settings['hasdcsystem'] == 0: - # Either DC loads are monitored, or there are no - # unmonitored DC loads or chargers: derive battery watts - # and amps from vebus, solarchargers, chargers and measured - # loads. - p = solarchargers_charge_power + newvalues.get('/Dc/Charger/Power', 0) + vebuspower - dcsystempower - voltage = newvalues['/Dc/Battery/Voltage'] - newvalues['/Dc/Battery/Current'] = p / voltage if voltage > 0 else None - newvalues['/Dc/Battery/Power'] = p - - # ==== SYSTEM POWER ==== - # Look for dcsytem devices, add them together. Otherwise, if enabled, - # calculate it - if dcsystems: - newvalues['/Dc/System/MeasurementType'] = 1 # measured - newvalues['/Dc/System/Power'] = 0 - for meter in dcsystems: - newvalues['/Dc/System/Power'] = _safeadd(newvalues['/Dc/System/Power'], - self._dbusmonitor.get_value(meter, '/Dc/0/Power')) - elif self._settings['hasdcsystem'] == 1 and batteryservicetype == 'battery': - # Calculate power being generated/consumed by not measured devices in the network. - # For MPPTs, take all the power, including power going out of the load output. - # /Dc/System: positive: consuming power - # VE.Bus: Positive: current flowing from the Multi to the dc system or battery - # Solarcharger & other chargers: positive: charging - # battery: Positive: charging battery. - # battery = solarcharger + charger + ve.bus - system - - battery_power = newvalues.get('/Dc/Battery/Power') - if battery_power is not None: - dc_pv_power = newvalues.get('/Dc/Pv/Power', 0) - charger_power = newvalues.get('/Dc/Charger/Power', 0) - fuelcell_power = newvalues.get('/Dc/FuelCell/Power', 0) - - # If there are VE.Direct inverters, remove their power from the - # DC estimate. This is done using the AC value when the DC - # power values are not available. - inverter_power = 0 - for i in non_vebus_inverters: - inverter_current = self._dbusmonitor.get_value(i, '/Dc/0/Current') - if inverter_current is not None: - inverter_power += self._dbusmonitor.get_value( - i, '/Dc/0/Voltage', 0) * inverter_current - else: - inverter_power += self._dbusmonitor.get_value( - i, '/Ac/Out/L1/V', 0) * self._dbusmonitor.get_value( - i, '/Ac/Out/L1/I', 0) - newvalues['/Dc/System/MeasurementType'] = 0 # estimated - newvalues['/Dc/System/Power'] = dc_pv_power + charger_power + fuelcell_power + vebuspower - inverter_power - battery_power - - elif self._settings['hasdcsystem'] == 1 and solarchargers_loadoutput_power is not None: - newvalues['/Dc/System/MeasurementType'] = 0 # estimated - newvalues['/Dc/System/Power'] = solarchargers_loadoutput_power - - # ==== Vebus ==== - multi_path = getattr(delegates.Multi.instance.multi, 'service', None) - if multi_path is not None: - dc_current = self._dbusmonitor.get_value(multi_path, '/Dc/0/Current') - newvalues['/Dc/Vebus/Current'] = dc_current - dc_power = self._dbusmonitor.get_value(multi_path, '/Dc/0/Power') - # Just in case /Dc/0/Power is not available - if dc_power == None and dc_current is not None: - dc_voltage = self._dbusmonitor.get_value(multi_path, '/Dc/0/Voltage') - if dc_voltage is not None: - dc_power = dc_voltage * dc_current - # Note that there is also vebuspower, which is the total DC power summed over all multis. - # However, this value cannot be combined with /Dc/Multi/Current, because it does not make sense - # to add the Dc currents of all multis if they do not share the same DC voltage. - newvalues['/Dc/Vebus/Power'] = dc_power - - # ===== AC IN SOURCE ===== - ac_in_source = None - active_input = None - if multi_path is None: - # Check if we have an non-VE.Bus inverter. - if non_vebus_inverter is not None: - if (active_input := self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/ActiveIn/ActiveInput')) is not None and \ - active_input in (0, 1) and \ - (active_type := self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/In/{}/Type'.format(active_input + 1))) is not None: - ac_in_source = active_type - else: - ac_in_source = 240 - else: - active_input = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/ActiveInput') - if active_input == 0xF0: - # Not connected - ac_in_source = 240 - elif active_input is not None: - settings_path = '/Settings/SystemSetup/AcInput%s' % (active_input + 1) - ac_in_source = self._dbusmonitor.get_value('com.victronenergy.settings', settings_path) - newvalues['/Ac/ActiveIn/Source'] = ac_in_source - - # ===== GRID METERS & CONSUMPTION ==== - grid_meter = delegates.AcInputs.instance.gridmeter - genset_meter = delegates.AcInputs.instance.gensetmeter - - # Make an educated guess as to what is being consumed from an AC source. If ac_in_source - # indicates grid, genset or shore, we use that. If the Multi is off, or disconnected through - # a relay assistant or otherwise, then assume the presence of a .grid or .genset service indicates - # presence of that AC source. If both are available, then give up. This decision making is here - # so the GUI has something to present even if the Multi is off. - ac_in_guess = ac_in_source - if ac_in_guess in (None, 0xF0): - if genset_meter is None and grid_meter is not None: - ac_in_guess = 1 - elif grid_meter is None and genset_meter is not None: - ac_in_guess = 2 - - consumption = { "L1" : None, "L2" : None, "L3" : None } - for device_type, em, _types in (('Grid', grid_meter, (1, 3)), ('Genset', genset_meter, (2,))): - # If a grid meter is present we use values from it. If not, we look at the multi. If it has - # AcIn1 or AcIn2 connected to the grid, we use those values. - # com.victronenergy.grid.??? indicates presence of an energy meter used as grid meter. - # com.victronenergy.vebus.???/Ac/ActiveIn/ActiveInput: decides which whether we look at AcIn1 - # or AcIn2 as possible grid connection. - uses_active_input = ac_in_source in _types - for phase in consumption: - p = None - pvpower = newvalues.get('/Ac/PvOn%s/%s/Power' % (device_type, phase)) - if em is not None: - p = self._dbusmonitor.get_value(em.service, '/Ac/%s/Power' % phase) - # Compute consumption between energy meter and multi (meter power - multi AC in) and - # add an optional PV inverter on input to the mix. - c = None - if uses_active_input: - if multi_path is not None and (ac_in := self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/P' % phase)) is not None: - c = _safeadd(c, -ac_in) - elif non_vebus_inverter is not None and active_input in (0, 1): - ac_in = self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/In/%d/%s/P' % (active_input+1, phase)) - if ac_in is not None: - c = _safeadd(c, -ac_in) - - # If there's any power coming from a PV inverter in the inactive AC in (which is unlikely), - # it will still be used, because there may also be a load in the same ACIn consuming - # power, or the power could be fed back to the net. - c = _safeadd(c, p, pvpower) - consumption[phase] = _safeadd(consumption[phase], _safemax(0, c)) - else: - if uses_active_input: - if multi_path is not None and ( - p := self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/P' % phase)) is not None: - consumption[phase] = _safeadd(0, consumption[phase]) - elif non_vebus_inverter is not None and active_input in (0, 1): - p = self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/In/%d/%s/P' % (active_input + 1, phase)) - if p is not None: - consumption[phase] = _safeadd(0, consumption[phase]) - - # No relevant energy meter present. Assume there is no load between the grid and the multi. - # There may be a PV inverter present though (Hub-3 setup). - if pvpower != None: - p = _safeadd(p, -pvpower) - newvalues['/Ac/%s/%s/Power' % (device_type, phase)] = p - if ac_in_guess in _types: - newvalues['/Ac/ActiveIn/%s/Power' % (phase,)] = p - - self._compute_number_of_phases('/Ac/%s' % device_type, newvalues) - self._compute_number_of_phases('/Ac/ActiveIn', newvalues) - - product_id = None - device_type_id = None - if em is not None: - product_id = em.product_id - device_type_id = em.device_type - if product_id is None and uses_active_input: - if multi_path is not None: - product_id = self._dbusmonitor.get_value(multi_path, '/ProductId') - elif non_vebus_inverter is not None: - product_id = self._dbusmonitor.get_value(non_vebus_inverter, '/ProductId') - newvalues['/Ac/%s/ProductId' % device_type] = product_id - newvalues['/Ac/%s/DeviceType' % device_type] = device_type_id - - # If we have an ESS system and RunWithoutGridMeter is set, there cannot be load on the AC-In, so it - # must be on AC-Out. Hence we do calculate AC-Out consumption even if 'useacout' is disabled. - # Similarly all load are by definition on the output if this is not an ESS system. - use_ac_out = \ - self._settings['useacout'] == 1 or \ - (multi_path is not None and self._dbusmonitor.get_value(multi_path, '/Hub4/AssistantId') not in (4, 5)) or \ - self._dbusmonitor.get_value('com.victronenergy.settings', '/Settings/CGwacs/RunWithoutGridMeter') == 1 - for phase in consumption: - c = None - if use_ac_out: - c = newvalues.get('/Ac/PvOnOutput/%s/Power' % phase) - if multi_path is None: - for inv in non_vebus_inverters: - ac_out = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/P' % phase) - - # Some models don't show power, calculate it - if ac_out is None: - i = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/I' % phase) - u = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/V' % phase) - if None not in (i, u): - ac_out = i * u - c = _safeadd(c, ac_out) - else: - ac_out = self._dbusmonitor.get_value(multi_path, '/Ac/Out/%s/P' % phase) - c = _safeadd(c, ac_out) - c = _safemax(0, c) - newvalues['/Ac/ConsumptionOnOutput/%s/Power' % phase] = c - newvalues['/Ac/ConsumptionOnInput/%s/Power' % phase] = consumption[phase] - newvalues['/Ac/Consumption/%s/Power' % phase] = _safeadd(consumption[phase], c) - self._compute_number_of_phases('/Ac/Consumption', newvalues) - self._compute_number_of_phases('/Ac/ConsumptionOnOutput', newvalues) - self._compute_number_of_phases('/Ac/ConsumptionOnInput', newvalues) - - for m in self._modules: - m.update_values(newvalues) - - # ==== UPDATE DBUS ITEMS ==== - with self._dbusservice as sss: - for path in self._summeditems.keys(): - # Why the None? Because we want to invalidate things we don't have anymore. - sss[path] = newvalues.get(path, None) - - def _handleservicechange(self): - # Update the available battery monitor services, used to populate the dropdown in the settings. - # Below code makes a dictionary. The key is [dbuserviceclass]/[deviceinstance]. For example - # "battery/245". The value is the name to show to the user in the dropdown. The full dbus- - # servicename, ie 'com.victronenergy.vebus.ttyO1' is not used, since the last part of that is not - # fixed. dbus-serviceclass name and the device instance are already fixed, so best to use those. - - services = self._get_connected_service_list('com.victronenergy.vebus') - services.update(self._get_connected_service_list('com.victronenergy.battery')) - services.update({k: v for k, v in self._get_connected_service_list( - 'com.victronenergy.multi').items() if self._dbusmonitor.get_value(k, '/Soc') is not None}) - services.update({k: v for k, v in self._get_connected_service_list( - 'com.victronenergy.inverter').items() if self._dbusmonitor.get_value(k, '/Soc') is not None}) - - ul = {self.BATSERVICE_DEFAULT: 'Automatic', self.BATSERVICE_NOBATTERY: 'No battery monitor'} - for servicename, instance in services.items(): - key = self._get_instance_service_name(servicename, instance) - ul[key] = self._get_readable_service_name(servicename) - self._dbusservice['/AvailableBatteryServices'] = json.dumps(ul) - - ul = {self.BATSERVICE_DEFAULT: 'Automatic', self.BATSERVICE_NOBATTERY: 'No battery monitor'} - # For later: for device supporting multiple Dc measurement we should add entries for /Dc/1 etc as - # well. - for servicename, instance in services.items(): - key = self._get_instance_service_name(servicename, instance).replace('.', '_').replace('/', '_') + '/Dc/0' - ul[key] = self._get_readable_service_name(servicename) - self._dbusservice['/AvailableBatteryMeasurements'] = ul - - self._determinebatteryservice() - self._updatepvinverterspidlist() - - self._changed = True - - def _get_readable_service_name(self, servicename): - return '%s on %s' % ( - self._dbusmonitor.get_value(servicename, '/ProductName'), - self._dbusmonitor.get_value(servicename, '/Mgmt/Connection')) - - def _get_instance_service_name(self, service, instance): - return '%s/%s' % ('.'.join(service.split('.')[0:3]), instance) - - def _remove_unconnected_services(self, services): - # Workaround: because com.victronenergy.vebus is available even when there is no vebus product - # connected, remove any service that is not connected. Previously we used - # /State since mandatory path /Connected is not implemented in mk2dbus, - # but this has since been resolved. - for servicename in list(services.keys()): - if (self._dbusmonitor.get_value(servicename, '/Connected') != 1 - or self._dbusmonitor.get_value(servicename, '/ProductName') is None - or self._dbusmonitor.get_value(servicename, '/Mgmt/Connection') is None): - del services[servicename] - - def _dbus_value_changed(self, dbusServiceName, dbusPath, dict, changes, deviceInstance): - self._changed = True - - # Workaround because com.victronenergy.vebus is available even when there is no vebus product - # connected. - if (dbusPath in ['/Connected', '/ProductName', '/Mgmt/Connection'] or - (dbusPath == '/State' and dbusServiceName.split('.')[0:3] == ['com', 'victronenergy', 'vebus'])): - self._handleservicechange() - - # Track the timezone changes - if dbusPath == '/Settings/System/TimeZone': - tz = changes.get('Value') - if tz is not None: - os.environ['TZ'] = tz - - def _device_added(self, service, instance, do_service_change=True): - if do_service_change: - self._handleservicechange() - - for m in self._modules: - m.device_added(service, instance, do_service_change) - - def _device_removed(self, service, instance): - self._handleservicechange() - - for m in self._modules: - m.device_removed(service, instance) - - def _gettext(self, path, value): - if path == '/Dc/Battery/State': - state = {self.STATE_IDLE: 'Idle', self.STATE_CHARGING: 'Charging', - self.STATE_DISCHARGING: 'Discharging'} - return state[value] - item = self._summeditems.get(path) - if item is not None: - return item['gettext'] % value - return str(value) - - def _compute_number_of_phases(self, path, newvalues): - number_of_phases = None - for phase in range(1, 4): - p = newvalues.get('%s/L%s/Power' % (path, phase)) - if p is not None: - number_of_phases = phase - newvalues[path + '/NumberOfPhases'] = number_of_phases - - def _get_connected_service_list(self, classfilter=None): - services = self._dbusmonitor.get_service_list(classfilter=classfilter) - self._remove_unconnected_services(services) - return services - - # returns a servicename string - def _get_first_connected_service(self, classfilter): - services = self._get_connected_service_list(classfilter=classfilter) - if len(services) == 0: - return None - return next(iter(services.items()), (None,))[0] - - # returns a tuple (servicename, instance) - def _get_service_having_lowest_instance(self, classfilter=None): - services = self._get_connected_service_list(classfilter=classfilter) - if len(services) == 0: - return None - - # sort the dict by value; returns list of tuples: (value, key) - s = sorted((value, key) for (key, value) in services.items()) - return (s[0][1], s[0][0]) - - -class DbusSystemCalc(SystemCalc): - def _create_dbus_monitor(self, *args, **kwargs): - return DbusMonitor(*args, **kwargs) - - def _create_settings(self, *args, **kwargs): - bus = dbus.SessionBus() if 'DBUS_SESSION_BUS_ADDRESS' in os.environ else dbus.SystemBus() - return SettingsDevice(bus, *args, timeout=10, **kwargs) - - def _create_dbus_service(self): - venusversion, venusbuildtime = self._get_venus_versioninfo() - - dbusservice = VeDbusService('com.victronenergy.system') - dbusservice.add_mandatory_paths( - processname=__file__, - processversion=softwareVersion, - connection='data from other dbus processes', - deviceinstance=0, - productid=None, - productname=None, - firmwareversion=venusversion, - hardwareversion=None, - connected=1) - dbusservice.add_path('/FirmwareBuild', value=venusbuildtime) - return dbusservice - - def _get_venus_versioninfo(self): - try: - with open("/opt/victronenergy/version", "r") as fp: - version, software, buildtime = fp.read().split('\n')[:3] - major, minor, _, rev = re.compile('v([0-9]*)\.([0-9]*)(~([0-9]*))?').match(version).groups() - return (int(major, 16)<<16)+(int(minor, 16)<<8)+(0 if rev is None else int(rev, 16)), buildtime - except Exception: - pass - return 0, '0' - -if __name__ == "__main__": - # Argument parsing - parser = argparse.ArgumentParser( - description='Converts readings from AC-Sensors connected to a VE.Bus device in a pvinverter ' + - 'D-Bus service.' - ) - - parser.add_argument("-d", "--debug", help="set logging level to debug", - action="store_true") - - args = parser.parse_args() - - print("-------- dbus_systemcalc, v" + softwareVersion + " is starting up --------") - logger = setup_logging(args.debug) - - # Have a mainloop, so we can send/receive asynchronous calls to and from dbus - DBusGMainLoop(set_as_default=True) - - systemcalc = DbusSystemCalc() - - # Start and run the mainloop - logger.info("Starting mainloop, responding only on events") - mainloop = GLib.MainLoop() - mainloop.run() diff --git a/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v2.94 b/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v2.94 deleted file mode 100755 index f641b360..00000000 --- a/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v2.94 +++ /dev/null @@ -1,1394 +0,0 @@ -#!/usr/bin/python3 -u -# -*- coding: utf-8 -*- - -#### modified for GuiMods - -from dbus.mainloop.glib import DBusGMainLoop -import dbus -import argparse -import sys -import os -import json -import time -import re -from gi.repository import GLib - -# Victron packages -sys.path.insert(1, os.path.join(os.path.dirname(__file__), 'ext', 'velib_python')) -from vedbus import VeDbusService -from ve_utils import get_vrm_portal_id, exit_on_error -from dbusmonitor import DbusMonitor -from settingsdevice import SettingsDevice -from logger import setup_logging -import delegates -from sc_utils import safeadd as _safeadd, safemax as _safemax - -softwareVersion = '2.103' - -class SystemCalc: - STATE_IDLE = 0 - STATE_CHARGING = 1 - STATE_DISCHARGING = 2 - BATSERVICE_DEFAULT = 'default' - BATSERVICE_NOBATTERY = 'nobattery' - def __init__(self): - # Why this dummy? Because DbusMonitor expects these values to be there, even though we don't - # need them. So just add some dummy data. This can go away when DbusMonitor is more generic. - dummy = {'code': None, 'whenToLog': 'configChange', 'accessLevel': None} - dbus_tree = { - 'com.victronenergy.solarcharger': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Load/I': dummy, - '/FirmwareVersion': dummy}, - 'com.victronenergy.pvinverter': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, - '/Ac/L1/Current': dummy, - '/Ac/L2/Current': dummy, - '/Ac/L3/Current': dummy, - '/Position': dummy, - '/ProductId': dummy}, - 'com.victronenergy.battery': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/DeviceInstance': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/1/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Soc': dummy, - '/Sense/Current': dummy, - '/TimeToGo': dummy, - '/ConsumedAmphours': dummy, - '/ProductId': dummy, - '/CustomName': dummy}, - 'com.victronenergy.vebus' : { - '/Ac/ActiveIn/ActiveInput': dummy, - '/Ac/ActiveIn/L1/P': dummy, - '/Ac/ActiveIn/L2/P': dummy, - '/Ac/ActiveIn/L3/P': dummy, - '/Ac/ActiveIn/L1/I': dummy, - '/Ac/ActiveIn/L2/I': dummy, - '/Ac/ActiveIn/L3/I': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L2/P': dummy, - '/Ac/Out/L3/P': dummy, - '/Ac/Out/L1/I': dummy, - '/Ac/Out/L2/I': dummy, - '/Ac/Out/L3/I': dummy, -#### add for GuiMods - '/Ac/Out/L1/V': dummy, - '/Ac/Out/L2/V': dummy, - '/Ac/Out/L3/V': dummy, - '/Ac/Out/L1/F': dummy, - '/Ac/Out/L2/F': dummy, - '/Ac/Out/L3/F': dummy, - '/Ac/ActiveIn/L1/V': dummy, - '/Ac/ActiveIn/L2/V': dummy, - '/Ac/ActiveIn/L3/V': dummy, - '/Ac/ActiveIn/L1/F': dummy, - '/Ac/ActiveIn/L2/F': dummy, - '/Ac/ActiveIn/L3/F': dummy, - - '/Connected': dummy, - '/ProductId': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Mode': dummy, - '/State': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.fuelcell': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy}, - 'com.victronenergy.charger': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/1/Voltage': dummy, - '/Dc/1/Current': dummy, - '/Dc/2/Voltage': dummy, - '/Dc/2/Current': dummy}, - 'com.victronenergy.grid' : { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/ProductId' : dummy, - '/DeviceType' : dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, - '/Ac/L1/Current': dummy, - '/Ac/L2/Current': dummy, - '/Ac/L3/Current': dummy, -#### add for GuiMods - '/Ac/L1/Voltage': dummy, - '/Ac/L2/Voltage': dummy, - '/Ac/L3/Voltage': dummy, - '/Ac/L1/Frequency': dummy, - '/Ac/L2/Frequency': dummy, - '/Ac/L3/Frequency': dummy}, - 'com.victronenergy.genset' : { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/ProductId' : dummy, - '/DeviceType' : dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, - '/Ac/L1/Current': dummy, - '/Ac/L2/Current': dummy, - '/Ac/L3/Current': dummy, -#### add for GuiMods - '/Ac/L1/Voltage': dummy, - '/Ac/L2/Voltage': dummy, - '/Ac/L3/Voltage': dummy, - '/Ac/L1/Frequency': dummy, - '/Ac/L2/Frequency': dummy, - '/Ac/L3/Frequency': dummy, - - '/StarterVoltage': dummy}, - 'com.victronenergy.settings' : { - '/Settings/SystemSetup/AcInput1' : dummy, - '/Settings/SystemSetup/AcInput2' : dummy, - '/Settings/CGwacs/RunWithoutGridMeter' : dummy, - '/Settings/System/TimeZone' : dummy}, - 'com.victronenergy.temperature': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy}, - 'com.victronenergy.inverter': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L1/S': dummy, - '/Ac/Out/L1/V': dummy, - '/Ac/Out/L1/I': dummy, -#### add for GuiMods - '/Ac/Out/L1/F': dummy, - - '/Yield/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.multi': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Ac/ActiveIn/ActiveInput': dummy, - '/Ac/In/1/Type': dummy, - '/Ac/In/2/Type': dummy, - '/Ac/In/1/L1/P': dummy, - '/Ac/In/1/L1/I': dummy, - '/Ac/In/2/L1/P': dummy, - '/Ac/In/2/L1/I': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L1/V': dummy, - '/Ac/Out/L1/I': dummy, -#### add for GuiMods - '/Ac/L1/F': dummy, - - '/Yield/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.dcsystem': { - '/Dc/0/Voltage': dummy, - '/Dc/0/Power': dummy - }, - 'com.victronenergy.alternator': { - '/Dc/0/Power': dummy - }, -#### added for GuiMods - 'com.victronenergy.dcsource': { - '/Dc/0/Power': dummy, - '/Settings/MonitorMode': dummy - }, - 'com.victronenergy.motordrive': - { - '/Dc/0/Power': dummy - } - } - - self._modules = [ - delegates.Multi(), - delegates.HubTypeSelect(), - delegates.VebusSocWriter(), - delegates.ServiceMapper(), - delegates.RelayState(), - delegates.BuzzerControl(), - delegates.LgCircuitBreakerDetect(), - delegates.Dvcc(self), - delegates.BatterySense(self), - delegates.BatterySettings(self), - delegates.SystemState(self), - delegates.BatteryLife(), - delegates.ScheduledCharging(), - delegates.SourceTimers(), - #delegates.BydCurrentSense(self), - delegates.BatteryData(), - delegates.Gps(), - delegates.AcInputs(), - delegates.GensetStartStop(), - delegates.SocSync(self)] - - for m in self._modules: - for service, paths in m.get_input(): - s = dbus_tree.setdefault(service, {}) - for path in paths: - s[path] = dummy - - self._dbusmonitor = self._create_dbus_monitor(dbus_tree, valueChangedCallback=self._dbus_value_changed, - deviceAddedCallback=self._device_added, deviceRemovedCallback=self._device_removed) - - # Connect to localsettings - supported_settings = { - 'batteryservice': ['/Settings/SystemSetup/BatteryService', self.BATSERVICE_DEFAULT, 0, 0], - 'hasdcsystem': ['/Settings/SystemSetup/HasDcSystem', 0, 0, 1], - 'useacout': ['/Settings/SystemSetup/HasAcOutSystem', 1, 0, 1]} - - for m in self._modules: - for setting in m.get_settings(): - supported_settings[setting[0]] = list(setting[1:]) - - self._settings = self._create_settings(supported_settings, self._handlechangedsetting) - - self._dbusservice = self._create_dbus_service() - - for m in self._modules: - m.set_sources(self._dbusmonitor, self._settings, self._dbusservice) - - # At this moment, VRM portal ID is the MAC address of the CCGX. Anyhow, it should be string uniquely - # identifying the CCGX. - self._dbusservice.add_path('/Serial', value=get_vrm_portal_id()) - self._dbusservice.add_path( - '/AvailableBatteryServices', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/AvailableBatteryMeasurements', value=None) - self._dbusservice.add_path( - '/AutoSelectedBatteryService', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/AutoSelectedBatteryMeasurement', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/ActiveBatteryService', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/Dc/Battery/BatteryService', value=None) - self._dbusservice.add_path( - '/PvInvertersProductIds', value=None) - self._summeditems = { - '/Ac/Grid/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L1/Current': {'gettext': '%.1F A'}, - '/Ac/Grid/L2/Current': {'gettext': '%.1F A'}, - '/Ac/Grid/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Grid/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Grid/ProductId': {'gettext': '%s'}, - '/Ac/Grid/DeviceType': {'gettext': '%s'}, - '/Ac/Genset/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L1/Current': {'gettext': '%.1F A'}, - '/Ac/Genset/L2/Current': {'gettext': '%.1F A'}, - '/Ac/Genset/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Genset/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Genset/ProductId': {'gettext': '%s'}, - '/Ac/Genset/DeviceType': {'gettext': '%s'}, - '/Ac/ConsumptionOnOutput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L1/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnOutput/L2/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnOutput/L3/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnInput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L1/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnInput/L2/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnInput/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Consumption/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L1/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/L2/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/L1/Current': {'gettext': '%.1F A'}, - '/Ac/PvOnOutput/L2/Current': {'gettext': '%.1F A'}, - '/Ac/PvOnOutput/L3/Current': {'gettext': '%.1F A'}, - '/Ac/PvOnOutput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/L1/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/L2/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/L3/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/L1/Current': {'gettext': '%.1F A'}, - '/Ac/PvOnGrid/L2/Current': {'gettext': '%.1F A'}, - '/Ac/PvOnGrid/L3/Current': {'gettext': '%.1F A'}, - '/Ac/PvOnGrid/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/L1/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/L2/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/L3/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/L1/Current': {'gettext': '%.1F A'}, - '/Ac/PvOnGenset/L2/Current': {'gettext': '%.1F A'}, - '/Ac/PvOnGenset/L3/Current': {'gettext': '%.1F A'}, - '/Ac/PvOnGenset/NumberOfPhases': {'gettext': '%d'}, - '/Dc/Pv/Power': {'gettext': '%.0F W'}, - '/Dc/Pv/Current': {'gettext': '%.1F A'}, - '/Dc/Battery/Voltage': {'gettext': '%.2F V'}, - '/Dc/Battery/VoltageService': {'gettext': '%s'}, - '/Dc/Battery/Current': {'gettext': '%.1F A'}, - '/Dc/Battery/Power': {'gettext': '%.0F W'}, - '/Dc/Battery/Soc': {'gettext': '%.0F %%'}, - '/Dc/Battery/State': {'gettext': '%s'}, - '/Dc/Battery/TimeToGo': {'gettext': '%.0F s'}, - '/Dc/Battery/ConsumedAmphours': {'gettext': '%.1F Ah'}, - '/Dc/Battery/ProductId': {'gettext': '0x%x'}, - '/Dc/Charger/Power': {'gettext': '%.0F %%'}, - '/Dc/FuelCell/Power': {'gettext': '%.0F %%'}, - '/Dc/Alternator/Power': {'gettext': '%.0F W'}, - '/Dc/Vebus/Current': {'gettext': '%.1F A'}, - '/Dc/Vebus/Power': {'gettext': '%.0F W'}, - '/Dc/System/Power': {'gettext': '%.0F W'}, - '/Dc/System/MeasurementType': {'gettext': '%d'}, - '/Ac/ActiveIn/Source': {'gettext': '%s'}, - '/Ac/ActiveIn/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/L3/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/L1/Current': {'gettext': '%.1F A'}, - '/Ac/ActiveIn/L2/Current': {'gettext': '%.1F A'}, - '/Ac/ActiveIn/L3/Current': {'gettext': '%.1F A'}, - '/Ac/ActiveIn/NumberOfPhases': {'gettext': '%d'}, -#### added for GuiMods - '/Dc/WindGenerator/Power': {'gettext': '%.0F W'}, - '/Dc/MotorDrive/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/Grid/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/Grid/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/Grid/Frequency': {'gettext': '%.1F Hz'}, - '/Ac/Genset/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/Genset/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/Genset/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/Genset/Frequency': {'gettext': '%.1F Hz'}, - '/Ac/ConsumptionOnOutput/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnOutput/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnOutput/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnOutput/Frequency': {'gettext': '%.1F Hz'}, - '/Ac/ConsumptionOnInput/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnInput/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnInput/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnInput/Frequency': {'gettext': '%.1F Hz'}, - '/Ac/Consumption/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/Consumption/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/Consumption/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/Consumption/Frequency': {'gettext': '%.1F Hz'}, - '/Ac/ActiveIn/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/ActiveIn/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/ActiveIn/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/ActiveIn/Frequency': {'gettext': '%.1F Hz'}, - } - - for m in self._modules: - self._summeditems.update(m.get_output()) - - for path in self._summeditems.keys(): - self._dbusservice.add_path(path, value=None, gettextcallback=self._gettext) - - self._batteryservice = None - self._determinebatteryservice() - - if self._batteryservice is None: - logger.info("Battery service initialized to None (setting == %s)" % - self._settings['batteryservice']) - - self._changed = True - for service, instance in self._dbusmonitor.get_service_list().items(): - self._device_added(service, instance, do_service_change=False) - -#### added for GuiMods - self.dcSystemPower = [0, 0, 0] - - self._handleservicechange() - self._updatevalues() - - GLib.timeout_add(1000, exit_on_error, self._handletimertick) - - 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): - raise Exception("This function should be overridden") - - def _handlechangedsetting(self, setting, oldvalue, newvalue): - self._determinebatteryservice() - self._changed = True - - # Give our delegates a chance to react on a settings change - for m in self._modules: - m.settings_changed(setting, oldvalue, newvalue) - - def _find_device_instance(self, serviceclass, instance): - """ Gets a mapping of services vs DeviceInstance using - get_service_list. Then searches for the specified DeviceInstance - and returns the service name. """ - services = self._dbusmonitor.get_service_list(classfilter=serviceclass) - - for k, v in services.items(): - if v == instance: - return k - return None - - def _determinebatteryservice(self): - auto_battery_service = self._autoselect_battery_service() - auto_battery_measurement = None - auto_selected = False - if auto_battery_service is not None: - services = self._dbusmonitor.get_service_list() - if auto_battery_service in services: - auto_battery_measurement = \ - self._get_instance_service_name(auto_battery_service, services[auto_battery_service]) - auto_battery_measurement = auto_battery_measurement.replace('.', '_').replace('/', '_') + '/Dc/0' - self._dbusservice['/AutoSelectedBatteryMeasurement'] = auto_battery_measurement - - if self._settings['batteryservice'] == self.BATSERVICE_DEFAULT: - auto_selected = True - newbatteryservice = auto_battery_service - self._dbusservice['/AutoSelectedBatteryService'] = ( - 'No battery monitor found' if newbatteryservice is None else - self._get_readable_service_name(newbatteryservice)) - - elif self._settings['batteryservice'] == self.BATSERVICE_NOBATTERY: - self._dbusservice['/AutoSelectedBatteryService'] = None - newbatteryservice = None - - else: - self._dbusservice['/AutoSelectedBatteryService'] = None - - s = self._settings['batteryservice'].split('/') - if len(s) != 2: - logger.error("The battery setting (%s) is invalid!" % self._settings['batteryservice']) - serviceclass = s[0] - instance = int(s[1]) if len(s) == 2 else None - - # newbatteryservice might turn into None if a chosen battery - # monitor no longer exists. Don't auto change the setting (it might - # come back) and don't autoselect another. - newbatteryservice = self._find_device_instance(serviceclass, instance) - - if newbatteryservice != self._batteryservice: - services = self._dbusmonitor.get_service_list() - instance = services.get(newbatteryservice, None) - if instance is None: - battery_service = None - else: - battery_service = self._get_instance_service_name(newbatteryservice, instance) - self._dbusservice['/ActiveBatteryService'] = battery_service - logger.info("Battery service, setting == %s, changed from %s to %s (%s)" % - (self._settings['batteryservice'], self._batteryservice, newbatteryservice, instance)) - - # Battery service has changed. Notify delegates. - for m in self._modules: - m.battery_service_changed(auto_selected, self._batteryservice, newbatteryservice) - self._dbusservice['/Dc/Battery/BatteryService'] = self._batteryservice = newbatteryservice - - def _autoselect_battery_service(self): - # Default setting business logic: - # first try to use a battery service (BMV or Lynx Shunt VE.Can). If there - # is more than one battery service, just use a random one. If no battery service is - # available, check if there are not Solar chargers and no normal chargers. If they are not - # there, assume this is a hub-2, hub-3 or hub-4 system and use VE.Bus SOC. - batteries = self._get_connected_service_list('com.victronenergy.battery') - - # Pick the first battery service - if len(batteries) > 0: - return sorted(batteries)[0] - - # No battery services, and there is a charger in the system. Abandon - # hope. - if self._get_first_connected_service('com.victronenergy.charger') is not None: - return None - - # Also no Multi, then give up. - vebus_service = self._get_service_having_lowest_instance('com.victronenergy.vebus') - if vebus_service is None: - # No VE.Bus, but maybe there is an inverter with built-in SOC - # tracking, eg RS Smart or Multi RS. - inverter = self._get_service_having_lowest_instance('com.victronenergy.multi') - if inverter and self._dbusmonitor.get_value(inverter[0], '/Soc') is not None: - return inverter[0] - - inverter = self._get_service_having_lowest_instance('com.victronenergy.inverter') - if inverter and self._dbusmonitor.get_value(inverter[0], '/Soc') is not None: - return inverter[0] - - return None - - # There is a Multi, it supports tracking external charge current from - # solarchargers, and there are no DC loads. Then use it. - if self._dbusmonitor.get_value( - vebus_service[0], '/ExtraBatteryCurrent') is not None \ - and self._get_first_connected_service('com.victronenergy.dcsystem') is None \ - and self._settings['hasdcsystem'] == 0: - return vebus_service[0] - - # Multi does not support tracking solarcharger current, and we have - # solar chargers. Then we cannot use it. - if self._get_first_connected_service('com.victronenergy.solarcharger') is not None: - return None - - # Only a Multi, no other chargers. Then we can use it. - return vebus_service[0] - - @property - def batteryservice(self): - return self._batteryservice - - # Called on a one second timer - def _handletimertick(self): - if self._changed: - self._updatevalues() - self._changed = False - - return True # keep timer running - - def _updatepvinverterspidlist(self): - # Create list of connected pv inverters id's - pvinverters = self._dbusmonitor.get_service_list('com.victronenergy.pvinverter') - productids = [] - - for pvinverter in pvinverters: - pid = self._dbusmonitor.get_value(pvinverter, '/ProductId') - if pid is not None and pid not in productids: - productids.append(pid) - self._dbusservice['/PvInvertersProductIds'] = productids - - def _updatevalues(self): - # ==== PREPARATIONS ==== - newvalues = {} - - # Set the user timezone - if 'TZ' not in os.environ: - tz = self._dbusmonitor.get_value('com.victronenergy.settings', '/Settings/System/TimeZone') - if tz is not None: - os.environ['TZ'] = tz - time.tzset() - - # Determine values used in logic below - vebusses = self._dbusmonitor.get_service_list('com.victronenergy.vebus') - vebuspower = 0 - for vebus in vebusses: - v = self._dbusmonitor.get_value(vebus, '/Dc/0/Voltage') - i = self._dbusmonitor.get_value(vebus, '/Dc/0/Current') - if v is not None and i is not None: - vebuspower += v * i - - # ==== PVINVERTERS ==== - pvinverters = self._dbusmonitor.get_service_list('com.victronenergy.pvinverter') - pos = {0: '/Ac/PvOnGrid', 1: '/Ac/PvOnOutput', 2: '/Ac/PvOnGenset'} - for pvinverter in pvinverters: - # Position will be None if PV inverter service has just been removed (after retrieving the - # service list). - position = pos.get(self._dbusmonitor.get_value(pvinverter, '/Position')) - if position is not None: - for phase in range(1, 4): - power = self._dbusmonitor.get_value(pvinverter, '/Ac/L%s/Power' % phase) - if power is not None: - path = '%s/L%s/Power' % (position, phase) - newvalues[path] = _safeadd(newvalues.get(path), power) - - current = self._dbusmonitor.get_value(pvinverter, '/Ac/L%s/Current' % phase) - if current is not None: - path = '%s/L%s/Current' % (position, phase) - newvalues[path] = _safeadd(newvalues.get(path), current) - - for path in pos.values(): - self._compute_number_of_phases(path, newvalues) - - # ==== SOLARCHARGERS ==== - solarchargers = self._dbusmonitor.get_service_list('com.victronenergy.solarcharger') - solarcharger_batteryvoltage = None - solarcharger_batteryvoltage_service = None - solarchargers_charge_power = 0 - solarchargers_loadoutput_power = None - - for solarcharger in solarchargers: - v = self._dbusmonitor.get_value(solarcharger, '/Dc/0/Voltage') - if v is None: - continue - i = self._dbusmonitor.get_value(solarcharger, '/Dc/0/Current') - if i is None: - continue - l = self._dbusmonitor.get_value(solarcharger, '/Load/I', 0) - - if l is not None: - if solarchargers_loadoutput_power is None: - solarchargers_loadoutput_power = l * v - else: - solarchargers_loadoutput_power += l * v - - solarchargers_charge_power += v * i - - # Note that this path is not in the _summeditems{}, making for it to not be - # published on D-Bus. Which fine. The only one needing it is the vebussocwriter- - # delegate. - if '/Dc/Pv/ChargeCurrent' not in newvalues: - newvalues['/Dc/Pv/ChargeCurrent'] = i - else: - newvalues['/Dc/Pv/ChargeCurrent'] += i - - if '/Dc/Pv/Power' not in newvalues: - newvalues['/Dc/Pv/Power'] = v * _safeadd(i, l) - newvalues['/Dc/Pv/Current'] = _safeadd(i, l) - solarcharger_batteryvoltage = v - solarcharger_batteryvoltage_service = solarcharger - else: - newvalues['/Dc/Pv/Power'] += v * _safeadd(i, l) - newvalues['/Dc/Pv/Current'] += _safeadd(i, l) - - # ==== FUELCELLS ==== - fuelcells = self._dbusmonitor.get_service_list('com.victronenergy.fuelcell') - fuelcell_batteryvoltage = None - fuelcell_batteryvoltage_service = None - for fuelcell in fuelcells: - # Assume the battery connected to output 0 is the main battery - v = self._dbusmonitor.get_value(fuelcell, '/Dc/0/Voltage') - if v is None: - continue - - fuelcell_batteryvoltage = v - fuelcell_batteryvoltage_service = fuelcell - - i = self._dbusmonitor.get_value(fuelcell, '/Dc/0/Current') - if i is None: - continue - - if '/Dc/FuelCell/Power' not in newvalues: - newvalues['/Dc/FuelCell/Power'] = v * i - else: - newvalues['/Dc/FuelCell/Power'] += v * i - - # ==== ALTERNATOR ==== - alternators = self._dbusmonitor.get_service_list('com.victronenergy.alternator') - for alternator in alternators: -#### changed for GuiMods - # some alternators do not provide a valid power value if not running - # or below a minimum power/current - # so fill in a zero power so that the systemcalc power becomes valid - # Assume the battery connected to output 0 is the main battery - p = self._dbusmonitor.get_value(alternator, '/Dc/0/Power') - if p is None: - #### continue - p = 0 - - if '/Dc/Alternator/Power' not in newvalues: - newvalues['/Dc/Alternator/Power'] = p - else: - newvalues['/Dc/Alternator/Power'] += p - -#### added for GuiMods - # ==== MOTOR DRIVE ==== - motordrives = self._dbusmonitor.get_service_list('com.victronenergy.motordrive') - for motordrive in motordrives: - p = self._dbusmonitor.get_value(motordrive, '/Dc/0/Power') - if p is None: - p = 0 - - if '/Dc/MotorDrive/Power' not in newvalues: - newvalues['/Dc/MotorDrive/Power'] = p - else: - newvalues['/Dc/MotorDrive/Power'] += p - -#### added for GuiMods - # ==== DC SOURCES ==== - dcSources = self._dbusmonitor.get_service_list('com.victronenergy.dcsource') - for dcSource in dcSources: - monitorMode = self._dbusmonitor.get_value(dcSource,'/Settings/MonitorMode') - # ==== WIND GENERATOR ==== - if monitorMode == -8: - p = self._dbusmonitor.get_value(dcSource, '/Dc/0/Power') - if p is None: - continue - if '/Dc/WindGenerator/Power' not in newvalues: - newvalues['/Dc/WindGenerator/Power'] = p - else: - newvalues['/Dc/WindGenerator/Power'] += p - - # ==== CHARGERS ==== - chargers = self._dbusmonitor.get_service_list('com.victronenergy.charger') - charger_batteryvoltage = None - charger_batteryvoltage_service = None - for charger in chargers: - # Assume the battery connected to output 0 is the main battery - v = self._dbusmonitor.get_value(charger, '/Dc/0/Voltage') - if v is None: - continue - - charger_batteryvoltage = v - charger_batteryvoltage_service = charger - - i = self._dbusmonitor.get_value(charger, '/Dc/0/Current') - if i is None: - continue - - if '/Dc/Charger/Power' not in newvalues: - newvalues['/Dc/Charger/Power'] = v * i - else: - newvalues['/Dc/Charger/Power'] += v * i - - # ==== Other Inverters and Inverter/Chargers ==== - _other_inverters = sorted((di, s) for s, di in self._dbusmonitor.get_service_list('com.victronenergy.multi').items()) + \ - sorted((di, s) for s, di in self._dbusmonitor.get_service_list('com.victronenergy.inverter').items()) - non_vebus_inverters = [x[1] for x in _other_inverters] - non_vebus_inverter = None - if non_vebus_inverters: - non_vebus_inverter = non_vebus_inverters[0] - - # For RS Smart and Multi RS, add PV to the yield - for i in non_vebus_inverters: - if (pv_yield := self._dbusmonitor.get_value(i, "/Yield/Power")) is not None: - newvalues['/Dc/Pv/Power'] = newvalues.get('/Dc/Pv/Power', 0) + pv_yield - - # Used lower down, possibly needed for battery values as well - dcsystems = self._dbusmonitor.get_service_list('com.victronenergy.dcsystem') - - # ==== BATTERY ==== - if self._batteryservice is not None: - batteryservicetype = self._batteryservice.split('.')[2] - assert batteryservicetype in ('battery', 'vebus', 'inverter', 'multi') - - newvalues['/Dc/Battery/Soc'] = self._dbusmonitor.get_value(self._batteryservice,'/Soc') - newvalues['/Dc/Battery/TimeToGo'] = self._dbusmonitor.get_value(self._batteryservice,'/TimeToGo') - newvalues['/Dc/Battery/ConsumedAmphours'] = self._dbusmonitor.get_value(self._batteryservice,'/ConsumedAmphours') - newvalues['/Dc/Battery/ProductId'] = self._dbusmonitor.get_value(self._batteryservice, '/ProductId') - - if batteryservicetype in ('battery', 'inverter', 'multi'): - newvalues['/Dc/Battery/Voltage'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Voltage') - newvalues['/Dc/Battery/VoltageService'] = self._batteryservice - newvalues['/Dc/Battery/Current'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Current') - newvalues['/Dc/Battery/Power'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Power') - - elif batteryservicetype == 'vebus': - vebus_voltage = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Voltage') - vebus_current = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Current') - vebus_power = None if vebus_voltage is None or vebus_current is None else vebus_current * vebus_voltage - newvalues['/Dc/Battery/Voltage'] = vebus_voltage - newvalues['/Dc/Battery/VoltageService'] = self._batteryservice - if self._settings['hasdcsystem'] == 1 or dcsystems: - # hasdcsystem will normally disqualify the multi from being - # auto-selected as battery monitor, so the only way we're - # here is if the user explicitly selected the multi as the - # battery service - newvalues['/Dc/Battery/Current'] = vebus_current - if vebus_power is not None: - newvalues['/Dc/Battery/Power'] = vebus_power - else: - battery_power = _safeadd(solarchargers_charge_power, vebus_power) - newvalues['/Dc/Battery/Current'] = battery_power / vebus_voltage if vebus_voltage is not None and vebus_voltage > 0 else None - newvalues['/Dc/Battery/Power'] = battery_power - - - p = newvalues.get('/Dc/Battery/Power', None) - if p is not None: - if p > 30: - newvalues['/Dc/Battery/State'] = self.STATE_CHARGING - elif p < -30: - newvalues['/Dc/Battery/State'] = self.STATE_DISCHARGING - else: - newvalues['/Dc/Battery/State'] = self.STATE_IDLE - - else: - # The battery service is not a BMS/BMV or a suitable vebus. A - # suitable vebus is defined as one explicitly selected by the user, - # or one that was automatically selected for SOC tracking. We may - # however still have a VE.Bus, just not one that can accurately - # track SOC. If we have one, use it as voltage source. Otherwise - # try a solar charger, a charger, a vedirect inverter or a dcsource - # as fallbacks. - batteryservicetype = None - vebusses = self._dbusmonitor.get_service_list('com.victronenergy.vebus') - for vebus in vebusses: - v = self._dbusmonitor.get_value(vebus, '/Dc/0/Voltage') - s = self._dbusmonitor.get_value(vebus, '/State') - if v is not None and s not in (0, None): - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = vebus - break # Skip the else below - else: - # No suitable vebus voltage, try other devices - if non_vebus_inverter is not None and (v := self._dbusmonitor.get_value(non_vebus_inverter, '/Dc/0/Voltage')) is not None: - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = non_vebus_inverter - elif solarcharger_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = solarcharger_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = solarcharger_batteryvoltage_service - elif charger_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = charger_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = charger_batteryvoltage_service - elif fuelcell_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = fuelcell_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = fuelcell_batteryvoltage_service - elif dcsystems: - # Get voltage from first dcsystem - s = next(iter(dcsystems.keys())) - v = self._dbusmonitor.get_value(s, '/Dc/0/Voltage') - if v is not None: - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = s - - # We have no suitable battery monitor, so power and current data - # is not available. We can however calculate it from other values, - # if we have at least a battery voltage. - if '/Dc/Battery/Voltage' in newvalues: - dcsystempower = _safeadd(0, *(self._dbusmonitor.get_value(s, - '/Dc/0/Power', 0) for s in dcsystems)) - if dcsystems or self._settings['hasdcsystem'] == 0: - # Either DC loads are monitored, or there are no - # unmonitored DC loads or chargers: derive battery watts - # and amps from vebus, solarchargers, chargers and measured - # loads. - p = solarchargers_charge_power + newvalues.get('/Dc/Charger/Power', 0) + vebuspower - dcsystempower - voltage = newvalues['/Dc/Battery/Voltage'] - newvalues['/Dc/Battery/Current'] = p / voltage if voltage > 0 else None - newvalues['/Dc/Battery/Power'] = p - - # ==== SYSTEM POWER ==== - # Look for dcsytem devices, add them together. Otherwise, if enabled, - # calculate it - if dcsystems: - newvalues['/Dc/System/MeasurementType'] = 1 # measured - newvalues['/Dc/System/Power'] = 0 - for meter in dcsystems: - newvalues['/Dc/System/Power'] = _safeadd(newvalues['/Dc/System/Power'], - self._dbusmonitor.get_value(meter, '/Dc/0/Power')) - elif self._settings['hasdcsystem'] == 1 and batteryservicetype == 'battery': - # Calculate power being generated/consumed by not measured devices in the network. - # For MPPTs, take all the power, including power going out of the load output. - # /Dc/System: positive: consuming power - # VE.Bus: Positive: current flowing from the Multi to the dc system or battery - # Solarcharger & other chargers: positive: charging - # battery: Positive: charging battery. - # battery = solarcharger + charger + ve.bus - system - - battery_power = newvalues.get('/Dc/Battery/Power') - if battery_power is not None: - dc_pv_power = newvalues.get('/Dc/Pv/Power', 0) - charger_power = newvalues.get('/Dc/Charger/Power', 0) - fuelcell_power = newvalues.get('/Dc/FuelCell/Power', 0) - alternator_power = newvalues.get('/Dc/Alternator/Power', 0) -#### added for GuiMods - windgen_power = newvalues.get('/Dc/WindGenerator/Power', 0) - motordrive_power = newvalues.get('/Dc/MotorDrive/Power', 0) - - # If there are VE.Direct inverters, remove their power from the - # DC estimate. This is done using the AC value when the DC - # power values are not available. - inverter_power = 0 - for i in non_vebus_inverters: - inverter_current = self._dbusmonitor.get_value(i, '/Dc/0/Current') - if inverter_current is not None: - inverter_power += self._dbusmonitor.get_value( - i, '/Dc/0/Voltage', 0) * inverter_current - else: - inverter_power += self._dbusmonitor.get_value( - i, '/Ac/Out/L1/V', 0) * self._dbusmonitor.get_value( - i, '/Ac/Out/L1/I', 0) - newvalues['/Dc/System/MeasurementType'] = 0 # estimated - # FIXME In future we will subtract alternator power from the - # calculated DC power, because it will be individually - # displayed. For now, we leave it out so that in the current - # version of Venus it does not break user's expectations. - #newvalues['/Dc/System/Power'] = dc_pv_power + charger_power + fuelcell_power + vebuspower - inverter_power - battery_power - alternator_power -#### changed for GuiMods - # average DC system power over 3 passes (seconds) to minimize wild swings in displayed value - self.dcSystemPower[2] = self.dcSystemPower[1] - self.dcSystemPower[1] = self.dcSystemPower[2] - self.dcSystemPower[0] = dc_pv_power + charger_power + fuelcell_power + vebuspower - inverter_power - battery_power + alternator_power + windgen_power - motordrive_power - newvalues['/Dc/System/Power'] = (self.dcSystemPower[0] + self.dcSystemPower[1] + self.dcSystemPower[2]) / 3 - - elif self._settings['hasdcsystem'] == 1 and solarchargers_loadoutput_power is not None: - newvalues['/Dc/System/MeasurementType'] = 0 # estimated - newvalues['/Dc/System/Power'] = solarchargers_loadoutput_power - - # ==== Vebus ==== - multi_path = getattr(delegates.Multi.instance.multi, 'service', None) - if multi_path is not None: - dc_current = self._dbusmonitor.get_value(multi_path, '/Dc/0/Current') - newvalues['/Dc/Vebus/Current'] = dc_current - dc_power = self._dbusmonitor.get_value(multi_path, '/Dc/0/Power') - # Just in case /Dc/0/Power is not available - if dc_power == None and dc_current is not None: - dc_voltage = self._dbusmonitor.get_value(multi_path, '/Dc/0/Voltage') - if dc_voltage is not None: - dc_power = dc_voltage * dc_current - # Note that there is also vebuspower, which is the total DC power summed over all multis. - # However, this value cannot be combined with /Dc/Multi/Current, because it does not make sense - # to add the Dc currents of all multis if they do not share the same DC voltage. - newvalues['/Dc/Vebus/Power'] = dc_power - - # ===== AC IN SOURCE ===== - ac_in_source = None - active_input = None - if multi_path is None: - # Check if we have an non-VE.Bus inverter. - if non_vebus_inverter is not None: - if (active_input := self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/ActiveIn/ActiveInput')) is not None and \ - active_input in (0, 1) and \ - (active_type := self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/In/{}/Type'.format(active_input + 1))) is not None: - ac_in_source = active_type - else: - ac_in_source = 240 - else: - active_input = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/ActiveInput') - if active_input == 0xF0: - # Not connected - ac_in_source = 240 - elif active_input is not None: - settings_path = '/Settings/SystemSetup/AcInput%s' % (active_input + 1) - ac_in_source = self._dbusmonitor.get_value('com.victronenergy.settings', settings_path) - newvalues['/Ac/ActiveIn/Source'] = ac_in_source - - # ===== GRID METERS & CONSUMPTION ==== - grid_meter = delegates.AcInputs.instance.gridmeter - genset_meter = delegates.AcInputs.instance.gensetmeter - - # Make an educated guess as to what is being consumed from an AC source. If ac_in_source - # indicates grid, genset or shore, we use that. If the Multi is off, or disconnected through - # a relay assistant or otherwise, then assume the presence of a .grid or .genset service indicates - # presence of that AC source. If both are available, then give up. This decision making is here - # so the GUI has something to present even if the Multi is off. - ac_in_guess = ac_in_source - if ac_in_guess in (None, 0xF0): - if genset_meter is None and grid_meter is not None: - ac_in_guess = 1 - elif grid_meter is None and genset_meter is not None: - ac_in_guess = 2 - - consumption = { "L1" : None, "L2" : None, "L3" : None } - currentconsumption = { "L1" : None, "L2" : None, "L3" : None } - -#### added for GuiMods - voltageIn = { "L1" : None, "L2" : None, "L3" : None } - voltageOut = { "L1" : None, "L2" : None, "L3" : None } - frequencyIn = None - frequencyOut = None - - for device_type, em, _types in (('Grid', grid_meter, (1, 3)), ('Genset', genset_meter, (2,))): - # If a grid meter is present we use values from it. If not, we look at the multi. If it has - # AcIn1 or AcIn2 connected to the grid, we use those values. - # com.victronenergy.grid.??? indicates presence of an energy meter used as grid meter. - # com.victronenergy.vebus.???/Ac/ActiveIn/ActiveInput: decides which whether we look at AcIn1 - # or AcIn2 as possible grid connection. - uses_active_input = ac_in_source in _types - for phase in consumption: - p = None - mc = None - pvpower = newvalues.get('/Ac/PvOn%s/%s/Power' % (device_type, phase)) - pvcurrent = newvalues.get('/Ac/PvOn%s/%s/Current' % (device_type, phase)) - if em is not None: - p = self._dbusmonitor.get_value(em.service, '/Ac/%s/Power' % phase) - mc = self._dbusmonitor.get_value(em.service, '/Ac/%s/Current' % phase) -#### added for GuiMods - if voltageIn[phase] == None: - voltageIn[phase] = self._dbusmonitor.get_value(em.service, '/Ac/%s/Voltage' % phase) - if frequencyIn == None: - frequencyIn = self._dbusmonitor.get_value(em.service, '/Ac/%s/Frequency' % phase) - - # Compute consumption between energy meter and multi (meter power - multi AC in) and - # add an optional PV inverter on input to the mix. - c = None - cc = None - if uses_active_input: - if multi_path is not None: - try: - c = _safeadd(c, -self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/P' % phase)) - cc = _safeadd(cc, -self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/I' % phase)) -#### added for GuiMods - if voltageIn[phase] == None: - voltageIn[phase] = self._dbusmonitor.get_value(em.service, '/Ac/ActiveIn/%s/V' % phase) - if frequencyIn == None: - frequencyIn = self._dbusmonitor.get_value(em.service, '/Ac/ActiveIn/%s/F' % phase) - - except TypeError: - pass - elif non_vebus_inverter is not None and active_input in (0, 1): - try: - c = _safeadd(c, -self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/In/%d/%s/P' % (active_input+1, phase))) - cc = _safeadd(cc, -self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/In/%d/%s/I' % (active_input+1, phase))) -#### added for GuiMods - if voltageIn[phase] == None: - voltageIn[phase] = self._dbusmonitor.get_value(em.service, '/Ac/In/%d/%s/V' % (active_input+1, phase)) - if frequencyIn == None: - frequencyIn = self._dbusmonitor.get_value(em.service, '/Ac/In/%d/%s/F' % (active_input+1, phase)) - - except TypeError: - pass - - # If there's any power coming from a PV inverter in the inactive AC in (which is unlikely), - # it will still be used, because there may also be a load in the same ACIn consuming - # power, or the power could be fed back to the net. - c = _safeadd(c, p, pvpower) - cc = _safeadd(cc, mc, pvcurrent) - consumption[phase] = _safeadd(consumption[phase], _safemax(0, c)) - currentconsumption[phase] = _safeadd(currentconsumption[phase], _safemax(0, cc)) - else: - if uses_active_input: - if multi_path is not None and ( - p := self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/P' % phase)) is not None: - consumption[phase] = _safeadd(0, consumption[phase]) - currentconsumption[phase] = _safeadd(0, currentconsumption[phase]) - mc = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/I' % phase) -#### added for GuiMods - if voltageIn[phase] == None: - voltageIn[phase] = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/V' % phase) - if frequencyIn == None: - freq = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/F' % phase) - if freq != None: - frequencyIn = freq - - elif non_vebus_inverter is not None and active_input in (0, 1): - p = self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/In/%d/%s/P' % (active_input + 1, phase)) - mc = self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/In/%d/%s/I' % (active_input + 1, phase)) -#### added for GuiMods - if voltageIn[phase] == None: - voltageIn[phase] = self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/In/%d/%s/V' % (active_input + 1, phase)) - if frequencyIn == None: - frequencyIn = self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/In/%d/%s/F' % (active_input + 1, phase)) - - if p is not None: - consumption[phase] = _safeadd(0, consumption[phase]) - currentconsumption[phase] = _safeadd(0, currentconsumption[phase]) - - # No relevant energy meter present. Assume there is no load between the grid and the multi. - # There may be a PV inverter present though (Hub-3 setup). - try: - p = _safeadd(p, -pvpower) - mc = _safeadd(mc, -pvcurrent) - except TypeError: - pass - - newvalues['/Ac/%s/%s/Power' % (device_type, phase)] = p - newvalues['/Ac/%s/%s/Current' % (device_type, phase)] = mc -#### added for GuiMods - if p != None: - newvalues['/Ac/%s/%s/Voltage' % (device_type, phase)] = voltageIn[phase] - newvalues['/Ac/%s/Frequency' % (device_type)] = frequencyIn - - if ac_in_guess in _types: - newvalues['/Ac/ActiveIn/%s/Power' % (phase,)] = p - newvalues['/Ac/ActiveIn/%s/Current' % (phase,)] = mc -#### added for GuiMods - if p != None: - newvalues['/Ac/ActiveIn/%s/Voltage' % (phase,)] = voltageIn[phase] - if frequencyIn != None: - newvalues['/Ac/ActiveIn/Frequency'] = frequencyIn - - self._compute_number_of_phases('/Ac/%s' % device_type, newvalues) - self._compute_number_of_phases('/Ac/ActiveIn', newvalues) - - product_id = None - device_type_id = None - if em is not None: - product_id = em.product_id - device_type_id = em.device_type - if product_id is None and uses_active_input: - if multi_path is not None: - product_id = self._dbusmonitor.get_value(multi_path, '/ProductId') - elif non_vebus_inverter is not None: - product_id = self._dbusmonitor.get_value(non_vebus_inverter, '/ProductId') - newvalues['/Ac/%s/ProductId' % device_type] = product_id - newvalues['/Ac/%s/DeviceType' % device_type] = device_type_id - - # If we have an ESS system and RunWithoutGridMeter is set, there cannot be load on the AC-In, so it - # must be on AC-Out. Hence we do calculate AC-Out consumption even if 'useacout' is disabled. - # Similarly all load are by definition on the output if this is not an ESS system. - use_ac_out = \ - self._settings['useacout'] == 1 or \ - (multi_path is not None and self._dbusmonitor.get_value(multi_path, '/Hub4/AssistantId') not in (4, 5)) or \ - self._dbusmonitor.get_value('com.victronenergy.settings', '/Settings/CGwacs/RunWithoutGridMeter') == 1 - for phase in consumption: - c = None - a = None - if use_ac_out: - c = newvalues.get('/Ac/PvOnOutput/%s/Power' % phase) - a = newvalues.get('/Ac/PvOnOutput/%s/Current' % phase) -#### added for GuiMods - if voltageOut[phase] == None: - voltageOut[phase] = newvalues.get('/Ac/PvOnOutput/%s/Voltage' % phase) - if frequencyOut == None: - frequencyOut = newvalues.get('/Ac/PvOnOutput/%s/Frequency' % phase) - - if multi_path is None: - for inv in non_vebus_inverters: - ac_out = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/P' % phase) - i = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/I' % phase) - -#### added for GuiMods - if voltageOut[phase] == None: - voltageOut[phase] = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/V' % phase) - if frequencyOut == None: - frequencyOut = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/F' % phase) - - # Some models don't show power, try apparent power, - # else calculate it - if ac_out is None: - ac_out = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/S' % phase) - if ac_out is None: -#### modified for GuiMods - # u = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/V' % phase) - if None not in (i, voltageOut[phase]): - ac_out = i * voltageOut[phase] - c = _safeadd(c, ac_out) - a = _safeadd(a, i) - else: - ac_out = self._dbusmonitor.get_value(multi_path, '/Ac/Out/%s/P' % phase) - c = _safeadd(c, ac_out) - i_out = self._dbusmonitor.get_value(multi_path, '/Ac/Out/%s/I' % phase) - a = _safeadd(a, i_out) -#### added for GuiMods - if voltageOut[phase] == None: - voltageOut[phase] = self._dbusmonitor.get_value(multi_path, '/Ac/Out/%s/V' % phase) - if frequencyOut == None: - frequencyOut = self._dbusmonitor.get_value(multi_path, '/Ac/Out/%s/F' % phase) - c = _safemax(0, c) - a = _safemax(0, a) - newvalues['/Ac/ConsumptionOnOutput/%s/Power' % phase] = c - newvalues['/Ac/ConsumptionOnOutput/%s/Current' % phase] = a - newvalues['/Ac/ConsumptionOnInput/%s/Power' % phase] = consumption[phase] - newvalues['/Ac/ConsumptionOnInput/%s/Current' % phase] = currentconsumption[phase] - newvalues['/Ac/Consumption/%s/Power' % phase] = _safeadd(consumption[phase], c) - newvalues['/Ac/Consumption/%s/Current' % phase] = _safeadd(currentconsumption[phase], a) -#### added for GuiMods - newvalues['/Ac/ConsumptionOnOutput/%s/Voltage' % phase] = voltageOut[phase] - newvalues['/Ac/ConsumptionOnInput/%s/Voltage' % phase] = voltageIn[phase] - if voltageOut[phase] != None: - newvalues['/Ac/Consumption/%s/Voltage' % phase] = voltageOut[phase] - elif voltageIn[phase] != None: - newvalues['/Ac/Consumption/%s/Voltage' % phase] = voltageIn[phase] - if frequencyIn != None: - newvalues['/Ac/ConsumptionOnInput/Frequency'] = frequencyIn - if frequencyOut != None: - newvalues['/Ac/ConsumptionOnOutput/Frequency'] = frequencyOut - if frequencyOut != None: - newvalues['/Ac/Consumption/Frequency'] = frequencyOut - elif frequencyIn != None: - newvalues['/Ac/Consumption/Frequency'] = frequencyIn - - self._compute_number_of_phases('/Ac/Consumption', newvalues) - self._compute_number_of_phases('/Ac/ConsumptionOnOutput', newvalues) - self._compute_number_of_phases('/Ac/ConsumptionOnInput', newvalues) - - for m in self._modules: - m.update_values(newvalues) - - # ==== UPDATE DBUS ITEMS ==== - with self._dbusservice as sss: - for path in self._summeditems.keys(): - # Why the None? Because we want to invalidate things we don't have anymore. - sss[path] = newvalues.get(path, None) - - def _handleservicechange(self): - # Update the available battery monitor services, used to populate the dropdown in the settings. - # Below code makes a dictionary. The key is [dbuserviceclass]/[deviceinstance]. For example - # "battery/245". The value is the name to show to the user in the dropdown. The full dbus- - # servicename, ie 'com.victronenergy.vebus.ttyO1' is not used, since the last part of that is not - # fixed. dbus-serviceclass name and the device instance are already fixed, so best to use those. - - services = self._get_connected_service_list('com.victronenergy.vebus') - services.update(self._get_connected_service_list('com.victronenergy.battery')) - services.update({k: v for k, v in self._get_connected_service_list( - 'com.victronenergy.multi').items() if self._dbusmonitor.get_value(k, '/Soc') is not None}) - services.update({k: v for k, v in self._get_connected_service_list( - 'com.victronenergy.inverter').items() if self._dbusmonitor.get_value(k, '/Soc') is not None}) - - ul = {self.BATSERVICE_DEFAULT: 'Automatic', self.BATSERVICE_NOBATTERY: 'No battery monitor'} - for servicename, instance in services.items(): - key = self._get_instance_service_name(servicename, instance) - ul[key] = self._get_readable_service_name(servicename) - self._dbusservice['/AvailableBatteryServices'] = json.dumps(ul) - - ul = {self.BATSERVICE_DEFAULT: 'Automatic', self.BATSERVICE_NOBATTERY: 'No battery monitor'} - # For later: for device supporting multiple Dc measurement we should add entries for /Dc/1 etc as - # well. - for servicename, instance in services.items(): - key = self._get_instance_service_name(servicename, instance).replace('.', '_').replace('/', '_') + '/Dc/0' - ul[key] = self._get_readable_service_name(servicename) - self._dbusservice['/AvailableBatteryMeasurements'] = ul - - self._determinebatteryservice() - self._updatepvinverterspidlist() - - self._changed = True - - def _get_readable_service_name(self, servicename): - return '%s on %s' % ( - self._dbusmonitor.get_value(servicename, '/ProductName'), - self._dbusmonitor.get_value(servicename, '/Mgmt/Connection')) - - def _get_instance_service_name(self, service, instance): - return '%s/%s' % ('.'.join(service.split('.')[0:3]), instance) - - def _remove_unconnected_services(self, services): - # Workaround: because com.victronenergy.vebus is available even when there is no vebus product - # connected, remove any service that is not connected. Previously we used - # /State since mandatory path /Connected is not implemented in mk2dbus, - # but this has since been resolved. - for servicename in list(services.keys()): - if (self._dbusmonitor.get_value(servicename, '/Connected') != 1 - or self._dbusmonitor.get_value(servicename, '/ProductName') is None - or self._dbusmonitor.get_value(servicename, '/Mgmt/Connection') is None): - del services[servicename] - - def _dbus_value_changed(self, dbusServiceName, dbusPath, dict, changes, deviceInstance): - self._changed = True - - # Workaround because com.victronenergy.vebus is available even when there is no vebus product - # connected. - if (dbusPath in ['/Connected', '/ProductName', '/Mgmt/Connection'] or - (dbusPath == '/State' and dbusServiceName.split('.')[0:3] == ['com', 'victronenergy', 'vebus'])): - self._handleservicechange() - - # Track the timezone changes - if dbusPath == '/Settings/System/TimeZone': - tz = changes.get('Value') - if tz is not None: - os.environ['TZ'] = tz - time.tzset() - - def _device_added(self, service, instance, do_service_change=True): - if do_service_change: - self._handleservicechange() - - for m in self._modules: - m.device_added(service, instance, do_service_change) - - def _device_removed(self, service, instance): - self._handleservicechange() - - for m in self._modules: - m.device_removed(service, instance) - - def _gettext(self, path, value): - if path == '/Dc/Battery/State': - state = {self.STATE_IDLE: 'Idle', self.STATE_CHARGING: 'Charging', - self.STATE_DISCHARGING: 'Discharging'} - return state[value] - item = self._summeditems.get(path) - if item is not None: - return item['gettext'] % value - return str(value) - - def _compute_number_of_phases(self, path, newvalues): - number_of_phases = None - for phase in range(1, 4): - p = newvalues.get('%s/L%s/Power' % (path, phase)) - if p is not None: - number_of_phases = phase - newvalues[path + '/NumberOfPhases'] = number_of_phases - - def _get_connected_service_list(self, classfilter=None): - services = self._dbusmonitor.get_service_list(classfilter=classfilter) - self._remove_unconnected_services(services) - return services - - # returns a servicename string - def _get_first_connected_service(self, classfilter): - services = self._get_connected_service_list(classfilter=classfilter) - if len(services) == 0: - return None - return next(iter(services.items()), (None,))[0] - - # returns a tuple (servicename, instance) - def _get_service_having_lowest_instance(self, classfilter=None): - services = self._get_connected_service_list(classfilter=classfilter) - if len(services) == 0: - return None - - # sort the dict by value; returns list of tuples: (value, key) - s = sorted((value, key) for (key, value) in services.items()) - return (s[0][1], s[0][0]) - - -class DbusSystemCalc(SystemCalc): - def _create_dbus_monitor(self, *args, **kwargs): - return DbusMonitor(*args, **kwargs) - - def _create_settings(self, *args, **kwargs): - bus = dbus.SessionBus() if 'DBUS_SESSION_BUS_ADDRESS' in os.environ else dbus.SystemBus() - return SettingsDevice(bus, *args, timeout=10, **kwargs) - - def _create_dbus_service(self): - venusversion, venusbuildtime = self._get_venus_versioninfo() - - dbusservice = VeDbusService('com.victronenergy.system') - dbusservice.add_mandatory_paths( - processname=__file__, - processversion=softwareVersion, - connection='data from other dbus processes', - deviceinstance=0, - productid=None, - productname=None, - firmwareversion=venusversion, - hardwareversion=None, - connected=1) - dbusservice.add_path('/FirmwareBuild', value=venusbuildtime) - return dbusservice - - def _get_venus_versioninfo(self): - try: - with open("/opt/victronenergy/version", "r") as fp: - version, software, buildtime = fp.read().split('\n')[:3] - major, minor, _, rev = re.compile('v([0-9]*)\.([0-9]*)(~([0-9]*))?').match(version).groups() - return (int(major, 16)<<16)+(int(minor, 16)<<8)+(0 if rev is None else int(rev, 16)), buildtime - except Exception: - pass - return 0, '0' - -if __name__ == "__main__": - # Argument parsing - parser = argparse.ArgumentParser( - description='Converts readings from AC-Sensors connected to a VE.Bus device in a pvinverter ' + - 'D-Bus service.' - ) - - parser.add_argument("-d", "--debug", help="set logging level to debug", - action="store_true") - - args = parser.parse_args() - - print("-------- dbus_systemcalc, v" + softwareVersion + " is starting up --------") - logger = setup_logging(args.debug) - - # Have a mainloop, so we can send/receive asynchronous calls to and from dbus - DBusGMainLoop(set_as_default=True) - - systemcalc = DbusSystemCalc() - - # Start and run the mainloop - logger.info("Starting mainloop, responding only on events") - mainloop = GLib.MainLoop() - mainloop.run() diff --git a/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v2.94.orig b/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v2.94.orig deleted file mode 100755 index 27a130fb..00000000 --- a/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v2.94.orig +++ /dev/null @@ -1,1194 +0,0 @@ -#!/usr/bin/python3 -u -# -*- coding: utf-8 -*- - -from dbus.mainloop.glib import DBusGMainLoop -import dbus -import argparse -import sys -import os -import json -import time -import re -from gi.repository import GLib - -# Victron packages -sys.path.insert(1, os.path.join(os.path.dirname(__file__), 'ext', 'velib_python')) -from vedbus import VeDbusService -from ve_utils import get_vrm_portal_id, exit_on_error -from dbusmonitor import DbusMonitor -from settingsdevice import SettingsDevice -from logger import setup_logging -import delegates -from sc_utils import safeadd as _safeadd, safemax as _safemax - -softwareVersion = '2.103' - -class SystemCalc: - STATE_IDLE = 0 - STATE_CHARGING = 1 - STATE_DISCHARGING = 2 - BATSERVICE_DEFAULT = 'default' - BATSERVICE_NOBATTERY = 'nobattery' - def __init__(self): - # Why this dummy? Because DbusMonitor expects these values to be there, even though we don't - # need them. So just add some dummy data. This can go away when DbusMonitor is more generic. - dummy = {'code': None, 'whenToLog': 'configChange', 'accessLevel': None} - dbus_tree = { - 'com.victronenergy.solarcharger': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Load/I': dummy, - '/FirmwareVersion': dummy}, - 'com.victronenergy.pvinverter': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, - '/Ac/L1/Current': dummy, - '/Ac/L2/Current': dummy, - '/Ac/L3/Current': dummy, - '/Position': dummy, - '/ProductId': dummy}, - 'com.victronenergy.battery': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/DeviceInstance': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/1/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Soc': dummy, - '/Sense/Current': dummy, - '/TimeToGo': dummy, - '/ConsumedAmphours': dummy, - '/ProductId': dummy, - '/CustomName': dummy}, - 'com.victronenergy.vebus' : { - '/Ac/ActiveIn/ActiveInput': dummy, - '/Ac/ActiveIn/L1/P': dummy, - '/Ac/ActiveIn/L2/P': dummy, - '/Ac/ActiveIn/L3/P': dummy, - '/Ac/ActiveIn/L1/I': dummy, - '/Ac/ActiveIn/L2/I': dummy, - '/Ac/ActiveIn/L3/I': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L2/P': dummy, - '/Ac/Out/L3/P': dummy, - '/Ac/Out/L1/I': dummy, - '/Ac/Out/L2/I': dummy, - '/Ac/Out/L3/I': dummy, - '/Connected': dummy, - '/ProductId': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Mode': dummy, - '/State': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.fuelcell': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy}, - 'com.victronenergy.charger': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/1/Voltage': dummy, - '/Dc/1/Current': dummy, - '/Dc/2/Voltage': dummy, - '/Dc/2/Current': dummy}, - 'com.victronenergy.grid' : { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/ProductId' : dummy, - '/DeviceType' : dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, - '/Ac/L1/Current': dummy, - '/Ac/L2/Current': dummy, - '/Ac/L3/Current': dummy}, - 'com.victronenergy.genset' : { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/ProductId' : dummy, - '/DeviceType' : dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, - '/Ac/L1/Current': dummy, - '/Ac/L2/Current': dummy, - '/Ac/L3/Current': dummy, - '/StarterVoltage': dummy}, - 'com.victronenergy.settings' : { - '/Settings/SystemSetup/AcInput1' : dummy, - '/Settings/SystemSetup/AcInput2' : dummy, - '/Settings/CGwacs/RunWithoutGridMeter' : dummy, - '/Settings/System/TimeZone' : dummy}, - 'com.victronenergy.temperature': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy}, - 'com.victronenergy.inverter': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L1/S': dummy, - '/Ac/Out/L1/V': dummy, - '/Ac/Out/L1/I': dummy, - '/Yield/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.multi': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Ac/ActiveIn/ActiveInput': dummy, - '/Ac/In/1/Type': dummy, - '/Ac/In/2/Type': dummy, - '/Ac/In/1/L1/P': dummy, - '/Ac/In/1/L1/I': dummy, - '/Ac/In/2/L1/P': dummy, - '/Ac/In/2/L1/I': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L1/V': dummy, - '/Ac/Out/L1/I': dummy, - '/Yield/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.dcsystem': { - '/Dc/0/Voltage': dummy, - '/Dc/0/Power': dummy - }, - 'com.victronenergy.alternator': { - '/Dc/0/Power': dummy - } - } - - self._modules = [ - delegates.Multi(), - delegates.HubTypeSelect(), - delegates.VebusSocWriter(), - delegates.ServiceMapper(), - delegates.RelayState(), - delegates.BuzzerControl(), - delegates.LgCircuitBreakerDetect(), - delegates.Dvcc(self), - delegates.BatterySense(self), - delegates.BatterySettings(self), - delegates.SystemState(self), - delegates.BatteryLife(), - delegates.ScheduledCharging(), - delegates.SourceTimers(), - #delegates.BydCurrentSense(self), - delegates.BatteryData(), - delegates.Gps(), - delegates.AcInputs(), - delegates.GensetStartStop(), - delegates.SocSync(self)] - - for m in self._modules: - for service, paths in m.get_input(): - s = dbus_tree.setdefault(service, {}) - for path in paths: - s[path] = dummy - - self._dbusmonitor = self._create_dbus_monitor(dbus_tree, valueChangedCallback=self._dbus_value_changed, - deviceAddedCallback=self._device_added, deviceRemovedCallback=self._device_removed) - - # Connect to localsettings - supported_settings = { - 'batteryservice': ['/Settings/SystemSetup/BatteryService', self.BATSERVICE_DEFAULT, 0, 0], - 'hasdcsystem': ['/Settings/SystemSetup/HasDcSystem', 0, 0, 1], - 'useacout': ['/Settings/SystemSetup/HasAcOutSystem', 1, 0, 1]} - - for m in self._modules: - for setting in m.get_settings(): - supported_settings[setting[0]] = list(setting[1:]) - - self._settings = self._create_settings(supported_settings, self._handlechangedsetting) - - self._dbusservice = self._create_dbus_service() - - for m in self._modules: - m.set_sources(self._dbusmonitor, self._settings, self._dbusservice) - - # At this moment, VRM portal ID is the MAC address of the CCGX. Anyhow, it should be string uniquely - # identifying the CCGX. - self._dbusservice.add_path('/Serial', value=get_vrm_portal_id()) - self._dbusservice.add_path( - '/AvailableBatteryServices', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/AvailableBatteryMeasurements', value=None) - self._dbusservice.add_path( - '/AutoSelectedBatteryService', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/AutoSelectedBatteryMeasurement', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/ActiveBatteryService', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/Dc/Battery/BatteryService', value=None) - self._dbusservice.add_path( - '/PvInvertersProductIds', value=None) - self._summeditems = { - '/Ac/Grid/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L1/Current': {'gettext': '%.1F A'}, - '/Ac/Grid/L2/Current': {'gettext': '%.1F A'}, - '/Ac/Grid/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Grid/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Grid/ProductId': {'gettext': '%s'}, - '/Ac/Grid/DeviceType': {'gettext': '%s'}, - '/Ac/Genset/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L1/Current': {'gettext': '%.1F A'}, - '/Ac/Genset/L2/Current': {'gettext': '%.1F A'}, - '/Ac/Genset/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Genset/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Genset/ProductId': {'gettext': '%s'}, - '/Ac/Genset/DeviceType': {'gettext': '%s'}, - '/Ac/ConsumptionOnOutput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L1/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnOutput/L2/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnOutput/L3/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnInput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L1/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnInput/L2/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnInput/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Consumption/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L1/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/L2/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnOutput/L1/Current': {'gettext': '%.1F A'}, - '/Ac/PvOnOutput/L2/Current': {'gettext': '%.1F A'}, - '/Ac/PvOnOutput/L3/Current': {'gettext': '%.1F A'}, - '/Ac/PvOnOutput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/L1/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/L2/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/L3/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGrid/L1/Current': {'gettext': '%.1F A'}, - '/Ac/PvOnGrid/L2/Current': {'gettext': '%.1F A'}, - '/Ac/PvOnGrid/L3/Current': {'gettext': '%.1F A'}, - '/Ac/PvOnGrid/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/L1/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/L2/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/L3/Power': {'gettext': '%.0F W'}, - '/Ac/PvOnGenset/L1/Current': {'gettext': '%.1F A'}, - '/Ac/PvOnGenset/L2/Current': {'gettext': '%.1F A'}, - '/Ac/PvOnGenset/L3/Current': {'gettext': '%.1F A'}, - '/Ac/PvOnGenset/NumberOfPhases': {'gettext': '%d'}, - '/Dc/Pv/Power': {'gettext': '%.0F W'}, - '/Dc/Pv/Current': {'gettext': '%.1F A'}, - '/Dc/Battery/Voltage': {'gettext': '%.2F V'}, - '/Dc/Battery/VoltageService': {'gettext': '%s'}, - '/Dc/Battery/Current': {'gettext': '%.1F A'}, - '/Dc/Battery/Power': {'gettext': '%.0F W'}, - '/Dc/Battery/Soc': {'gettext': '%.0F %%'}, - '/Dc/Battery/State': {'gettext': '%s'}, - '/Dc/Battery/TimeToGo': {'gettext': '%.0F s'}, - '/Dc/Battery/ConsumedAmphours': {'gettext': '%.1F Ah'}, - '/Dc/Battery/ProductId': {'gettext': '0x%x'}, - '/Dc/Charger/Power': {'gettext': '%.0F %%'}, - '/Dc/FuelCell/Power': {'gettext': '%.0F %%'}, - '/Dc/Alternator/Power': {'gettext': '%.0F W'}, - '/Dc/Vebus/Current': {'gettext': '%.1F A'}, - '/Dc/Vebus/Power': {'gettext': '%.0F W'}, - '/Dc/System/Power': {'gettext': '%.0F W'}, - '/Dc/System/MeasurementType': {'gettext': '%d'}, - '/Ac/ActiveIn/Source': {'gettext': '%s'}, - '/Ac/ActiveIn/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/L3/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/L1/Current': {'gettext': '%.1F A'}, - '/Ac/ActiveIn/L2/Current': {'gettext': '%.1F A'}, - '/Ac/ActiveIn/L3/Current': {'gettext': '%.1F A'}, - '/Ac/ActiveIn/NumberOfPhases': {'gettext': '%d'}, - } - - for m in self._modules: - self._summeditems.update(m.get_output()) - - for path in self._summeditems.keys(): - self._dbusservice.add_path(path, value=None, gettextcallback=self._gettext) - - self._batteryservice = None - self._determinebatteryservice() - - if self._batteryservice is None: - logger.info("Battery service initialized to None (setting == %s)" % - self._settings['batteryservice']) - - self._changed = True - for service, instance in self._dbusmonitor.get_service_list().items(): - self._device_added(service, instance, do_service_change=False) - - self._handleservicechange() - self._updatevalues() - - GLib.timeout_add(1000, exit_on_error, self._handletimertick) - - 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): - raise Exception("This function should be overridden") - - def _handlechangedsetting(self, setting, oldvalue, newvalue): - self._determinebatteryservice() - self._changed = True - - # Give our delegates a chance to react on a settings change - for m in self._modules: - m.settings_changed(setting, oldvalue, newvalue) - - def _find_device_instance(self, serviceclass, instance): - """ Gets a mapping of services vs DeviceInstance using - get_service_list. Then searches for the specified DeviceInstance - and returns the service name. """ - services = self._dbusmonitor.get_service_list(classfilter=serviceclass) - - for k, v in services.items(): - if v == instance: - return k - return None - - def _determinebatteryservice(self): - auto_battery_service = self._autoselect_battery_service() - auto_battery_measurement = None - auto_selected = False - if auto_battery_service is not None: - services = self._dbusmonitor.get_service_list() - if auto_battery_service in services: - auto_battery_measurement = \ - self._get_instance_service_name(auto_battery_service, services[auto_battery_service]) - auto_battery_measurement = auto_battery_measurement.replace('.', '_').replace('/', '_') + '/Dc/0' - self._dbusservice['/AutoSelectedBatteryMeasurement'] = auto_battery_measurement - - if self._settings['batteryservice'] == self.BATSERVICE_DEFAULT: - auto_selected = True - newbatteryservice = auto_battery_service - self._dbusservice['/AutoSelectedBatteryService'] = ( - 'No battery monitor found' if newbatteryservice is None else - self._get_readable_service_name(newbatteryservice)) - - elif self._settings['batteryservice'] == self.BATSERVICE_NOBATTERY: - self._dbusservice['/AutoSelectedBatteryService'] = None - newbatteryservice = None - - else: - self._dbusservice['/AutoSelectedBatteryService'] = None - - s = self._settings['batteryservice'].split('/') - if len(s) != 2: - logger.error("The battery setting (%s) is invalid!" % self._settings['batteryservice']) - serviceclass = s[0] - instance = int(s[1]) if len(s) == 2 else None - - # newbatteryservice might turn into None if a chosen battery - # monitor no longer exists. Don't auto change the setting (it might - # come back) and don't autoselect another. - newbatteryservice = self._find_device_instance(serviceclass, instance) - - if newbatteryservice != self._batteryservice: - services = self._dbusmonitor.get_service_list() - instance = services.get(newbatteryservice, None) - if instance is None: - battery_service = None - else: - battery_service = self._get_instance_service_name(newbatteryservice, instance) - self._dbusservice['/ActiveBatteryService'] = battery_service - logger.info("Battery service, setting == %s, changed from %s to %s (%s)" % - (self._settings['batteryservice'], self._batteryservice, newbatteryservice, instance)) - - # Battery service has changed. Notify delegates. - for m in self._modules: - m.battery_service_changed(auto_selected, self._batteryservice, newbatteryservice) - self._dbusservice['/Dc/Battery/BatteryService'] = self._batteryservice = newbatteryservice - - def _autoselect_battery_service(self): - # Default setting business logic: - # first try to use a battery service (BMV or Lynx Shunt VE.Can). If there - # is more than one battery service, just use a random one. If no battery service is - # available, check if there are not Solar chargers and no normal chargers. If they are not - # there, assume this is a hub-2, hub-3 or hub-4 system and use VE.Bus SOC. - batteries = self._get_connected_service_list('com.victronenergy.battery') - - # Pick the first battery service - if len(batteries) > 0: - return sorted(batteries)[0] - - # No battery services, and there is a charger in the system. Abandon - # hope. - if self._get_first_connected_service('com.victronenergy.charger') is not None: - return None - - # Also no Multi, then give up. - vebus_service = self._get_service_having_lowest_instance('com.victronenergy.vebus') - if vebus_service is None: - # No VE.Bus, but maybe there is an inverter with built-in SOC - # tracking, eg RS Smart or Multi RS. - inverter = self._get_service_having_lowest_instance('com.victronenergy.multi') - if inverter and self._dbusmonitor.get_value(inverter[0], '/Soc') is not None: - return inverter[0] - - inverter = self._get_service_having_lowest_instance('com.victronenergy.inverter') - if inverter and self._dbusmonitor.get_value(inverter[0], '/Soc') is not None: - return inverter[0] - - return None - - # There is a Multi, it supports tracking external charge current from - # solarchargers, and there are no DC loads. Then use it. - if self._dbusmonitor.get_value( - vebus_service[0], '/ExtraBatteryCurrent') is not None \ - and self._get_first_connected_service('com.victronenergy.dcsystem') is None \ - and self._settings['hasdcsystem'] == 0: - return vebus_service[0] - - # Multi does not support tracking solarcharger current, and we have - # solar chargers. Then we cannot use it. - if self._get_first_connected_service('com.victronenergy.solarcharger') is not None: - return None - - # Only a Multi, no other chargers. Then we can use it. - return vebus_service[0] - - @property - def batteryservice(self): - return self._batteryservice - - # Called on a one second timer - def _handletimertick(self): - if self._changed: - self._updatevalues() - self._changed = False - - return True # keep timer running - - def _updatepvinverterspidlist(self): - # Create list of connected pv inverters id's - pvinverters = self._dbusmonitor.get_service_list('com.victronenergy.pvinverter') - productids = [] - - for pvinverter in pvinverters: - pid = self._dbusmonitor.get_value(pvinverter, '/ProductId') - if pid is not None and pid not in productids: - productids.append(pid) - self._dbusservice['/PvInvertersProductIds'] = productids - - def _updatevalues(self): - # ==== PREPARATIONS ==== - newvalues = {} - - # Set the user timezone - if 'TZ' not in os.environ: - tz = self._dbusmonitor.get_value('com.victronenergy.settings', '/Settings/System/TimeZone') - if tz is not None: - os.environ['TZ'] = tz - time.tzset() - - # Determine values used in logic below - vebusses = self._dbusmonitor.get_service_list('com.victronenergy.vebus') - vebuspower = 0 - for vebus in vebusses: - v = self._dbusmonitor.get_value(vebus, '/Dc/0/Voltage') - i = self._dbusmonitor.get_value(vebus, '/Dc/0/Current') - if v is not None and i is not None: - vebuspower += v * i - - # ==== PVINVERTERS ==== - pvinverters = self._dbusmonitor.get_service_list('com.victronenergy.pvinverter') - pos = {0: '/Ac/PvOnGrid', 1: '/Ac/PvOnOutput', 2: '/Ac/PvOnGenset'} - for pvinverter in pvinverters: - # Position will be None if PV inverter service has just been removed (after retrieving the - # service list). - position = pos.get(self._dbusmonitor.get_value(pvinverter, '/Position')) - if position is not None: - for phase in range(1, 4): - power = self._dbusmonitor.get_value(pvinverter, '/Ac/L%s/Power' % phase) - if power is not None: - path = '%s/L%s/Power' % (position, phase) - newvalues[path] = _safeadd(newvalues.get(path), power) - - current = self._dbusmonitor.get_value(pvinverter, '/Ac/L%s/Current' % phase) - if current is not None: - path = '%s/L%s/Current' % (position, phase) - newvalues[path] = _safeadd(newvalues.get(path), current) - - for path in pos.values(): - self._compute_number_of_phases(path, newvalues) - - # ==== SOLARCHARGERS ==== - solarchargers = self._dbusmonitor.get_service_list('com.victronenergy.solarcharger') - solarcharger_batteryvoltage = None - solarcharger_batteryvoltage_service = None - solarchargers_charge_power = 0 - solarchargers_loadoutput_power = None - - for solarcharger in solarchargers: - v = self._dbusmonitor.get_value(solarcharger, '/Dc/0/Voltage') - if v is None: - continue - i = self._dbusmonitor.get_value(solarcharger, '/Dc/0/Current') - if i is None: - continue - l = self._dbusmonitor.get_value(solarcharger, '/Load/I', 0) - - if l is not None: - if solarchargers_loadoutput_power is None: - solarchargers_loadoutput_power = l * v - else: - solarchargers_loadoutput_power += l * v - - solarchargers_charge_power += v * i - - # Note that this path is not in the _summeditems{}, making for it to not be - # published on D-Bus. Which fine. The only one needing it is the vebussocwriter- - # delegate. - if '/Dc/Pv/ChargeCurrent' not in newvalues: - newvalues['/Dc/Pv/ChargeCurrent'] = i - else: - newvalues['/Dc/Pv/ChargeCurrent'] += i - - if '/Dc/Pv/Power' not in newvalues: - newvalues['/Dc/Pv/Power'] = v * _safeadd(i, l) - newvalues['/Dc/Pv/Current'] = _safeadd(i, l) - solarcharger_batteryvoltage = v - solarcharger_batteryvoltage_service = solarcharger - else: - newvalues['/Dc/Pv/Power'] += v * _safeadd(i, l) - newvalues['/Dc/Pv/Current'] += _safeadd(i, l) - - # ==== FUELCELLS ==== - fuelcells = self._dbusmonitor.get_service_list('com.victronenergy.fuelcell') - fuelcell_batteryvoltage = None - fuelcell_batteryvoltage_service = None - for fuelcell in fuelcells: - # Assume the battery connected to output 0 is the main battery - v = self._dbusmonitor.get_value(fuelcell, '/Dc/0/Voltage') - if v is None: - continue - - fuelcell_batteryvoltage = v - fuelcell_batteryvoltage_service = fuelcell - - i = self._dbusmonitor.get_value(fuelcell, '/Dc/0/Current') - if i is None: - continue - - if '/Dc/FuelCell/Power' not in newvalues: - newvalues['/Dc/FuelCell/Power'] = v * i - else: - newvalues['/Dc/FuelCell/Power'] += v * i - - # ==== ALTERNATOR ==== - alternators = self._dbusmonitor.get_service_list('com.victronenergy.alternator') - for alternator in alternators: - # Assume the battery connected to output 0 is the main battery - p = self._dbusmonitor.get_value(alternator, '/Dc/0/Power') - if p is None: - continue - - if '/Dc/Alternator/Power' not in newvalues: - newvalues['/Dc/Alternator/Power'] = p - else: - newvalues['/Dc/Alternator/Power'] += p - - # ==== CHARGERS ==== - chargers = self._dbusmonitor.get_service_list('com.victronenergy.charger') - charger_batteryvoltage = None - charger_batteryvoltage_service = None - for charger in chargers: - # Assume the battery connected to output 0 is the main battery - v = self._dbusmonitor.get_value(charger, '/Dc/0/Voltage') - if v is None: - continue - - charger_batteryvoltage = v - charger_batteryvoltage_service = charger - - i = self._dbusmonitor.get_value(charger, '/Dc/0/Current') - if i is None: - continue - - if '/Dc/Charger/Power' not in newvalues: - newvalues['/Dc/Charger/Power'] = v * i - else: - newvalues['/Dc/Charger/Power'] += v * i - - # ==== Other Inverters and Inverter/Chargers ==== - _other_inverters = sorted((di, s) for s, di in self._dbusmonitor.get_service_list('com.victronenergy.multi').items()) + \ - sorted((di, s) for s, di in self._dbusmonitor.get_service_list('com.victronenergy.inverter').items()) - non_vebus_inverters = [x[1] for x in _other_inverters] - non_vebus_inverter = None - if non_vebus_inverters: - non_vebus_inverter = non_vebus_inverters[0] - - # For RS Smart and Multi RS, add PV to the yield - for i in non_vebus_inverters: - if (pv_yield := self._dbusmonitor.get_value(i, "/Yield/Power")) is not None: - newvalues['/Dc/Pv/Power'] = newvalues.get('/Dc/Pv/Power', 0) + pv_yield - - # Used lower down, possibly needed for battery values as well - dcsystems = self._dbusmonitor.get_service_list('com.victronenergy.dcsystem') - - # ==== BATTERY ==== - if self._batteryservice is not None: - batteryservicetype = self._batteryservice.split('.')[2] - assert batteryservicetype in ('battery', 'vebus', 'inverter', 'multi') - - newvalues['/Dc/Battery/Soc'] = self._dbusmonitor.get_value(self._batteryservice,'/Soc') - newvalues['/Dc/Battery/TimeToGo'] = self._dbusmonitor.get_value(self._batteryservice,'/TimeToGo') - newvalues['/Dc/Battery/ConsumedAmphours'] = self._dbusmonitor.get_value(self._batteryservice,'/ConsumedAmphours') - newvalues['/Dc/Battery/ProductId'] = self._dbusmonitor.get_value(self._batteryservice, '/ProductId') - - if batteryservicetype in ('battery', 'inverter', 'multi'): - newvalues['/Dc/Battery/Voltage'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Voltage') - newvalues['/Dc/Battery/VoltageService'] = self._batteryservice - newvalues['/Dc/Battery/Current'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Current') - newvalues['/Dc/Battery/Power'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Power') - - elif batteryservicetype == 'vebus': - vebus_voltage = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Voltage') - vebus_current = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Current') - vebus_power = None if vebus_voltage is None or vebus_current is None else vebus_current * vebus_voltage - newvalues['/Dc/Battery/Voltage'] = vebus_voltage - newvalues['/Dc/Battery/VoltageService'] = self._batteryservice - if self._settings['hasdcsystem'] == 1 or dcsystems: - # hasdcsystem will normally disqualify the multi from being - # auto-selected as battery monitor, so the only way we're - # here is if the user explicitly selected the multi as the - # battery service - newvalues['/Dc/Battery/Current'] = vebus_current - if vebus_power is not None: - newvalues['/Dc/Battery/Power'] = vebus_power - else: - battery_power = _safeadd(solarchargers_charge_power, vebus_power) - newvalues['/Dc/Battery/Current'] = battery_power / vebus_voltage if vebus_voltage is not None and vebus_voltage > 0 else None - newvalues['/Dc/Battery/Power'] = battery_power - - - p = newvalues.get('/Dc/Battery/Power', None) - if p is not None: - if p > 30: - newvalues['/Dc/Battery/State'] = self.STATE_CHARGING - elif p < -30: - newvalues['/Dc/Battery/State'] = self.STATE_DISCHARGING - else: - newvalues['/Dc/Battery/State'] = self.STATE_IDLE - - else: - # The battery service is not a BMS/BMV or a suitable vebus. A - # suitable vebus is defined as one explicitly selected by the user, - # or one that was automatically selected for SOC tracking. We may - # however still have a VE.Bus, just not one that can accurately - # track SOC. If we have one, use it as voltage source. Otherwise - # try a solar charger, a charger, a vedirect inverter or a dcsource - # as fallbacks. - batteryservicetype = None - vebusses = self._dbusmonitor.get_service_list('com.victronenergy.vebus') - for vebus in vebusses: - v = self._dbusmonitor.get_value(vebus, '/Dc/0/Voltage') - s = self._dbusmonitor.get_value(vebus, '/State') - if v is not None and s not in (0, None): - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = vebus - break # Skip the else below - else: - # No suitable vebus voltage, try other devices - if non_vebus_inverter is not None and (v := self._dbusmonitor.get_value(non_vebus_inverter, '/Dc/0/Voltage')) is not None: - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = non_vebus_inverter - elif solarcharger_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = solarcharger_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = solarcharger_batteryvoltage_service - elif charger_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = charger_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = charger_batteryvoltage_service - elif fuelcell_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = fuelcell_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = fuelcell_batteryvoltage_service - elif dcsystems: - # Get voltage from first dcsystem - s = next(iter(dcsystems.keys())) - v = self._dbusmonitor.get_value(s, '/Dc/0/Voltage') - if v is not None: - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = s - - # We have no suitable battery monitor, so power and current data - # is not available. We can however calculate it from other values, - # if we have at least a battery voltage. - if '/Dc/Battery/Voltage' in newvalues: - dcsystempower = _safeadd(0, *(self._dbusmonitor.get_value(s, - '/Dc/0/Power', 0) for s in dcsystems)) - if dcsystems or self._settings['hasdcsystem'] == 0: - # Either DC loads are monitored, or there are no - # unmonitored DC loads or chargers: derive battery watts - # and amps from vebus, solarchargers, chargers and measured - # loads. - p = solarchargers_charge_power + newvalues.get('/Dc/Charger/Power', 0) + vebuspower - dcsystempower - voltage = newvalues['/Dc/Battery/Voltage'] - newvalues['/Dc/Battery/Current'] = p / voltage if voltage > 0 else None - newvalues['/Dc/Battery/Power'] = p - - # ==== SYSTEM POWER ==== - # Look for dcsytem devices, add them together. Otherwise, if enabled, - # calculate it - if dcsystems: - newvalues['/Dc/System/MeasurementType'] = 1 # measured - newvalues['/Dc/System/Power'] = 0 - for meter in dcsystems: - newvalues['/Dc/System/Power'] = _safeadd(newvalues['/Dc/System/Power'], - self._dbusmonitor.get_value(meter, '/Dc/0/Power')) - elif self._settings['hasdcsystem'] == 1 and batteryservicetype == 'battery': - # Calculate power being generated/consumed by not measured devices in the network. - # For MPPTs, take all the power, including power going out of the load output. - # /Dc/System: positive: consuming power - # VE.Bus: Positive: current flowing from the Multi to the dc system or battery - # Solarcharger & other chargers: positive: charging - # battery: Positive: charging battery. - # battery = solarcharger + charger + ve.bus - system - - battery_power = newvalues.get('/Dc/Battery/Power') - if battery_power is not None: - dc_pv_power = newvalues.get('/Dc/Pv/Power', 0) - charger_power = newvalues.get('/Dc/Charger/Power', 0) - fuelcell_power = newvalues.get('/Dc/FuelCell/Power', 0) - alternator_power = newvalues.get('/Dc/Alternator/Power', 0) - - # If there are VE.Direct inverters, remove their power from the - # DC estimate. This is done using the AC value when the DC - # power values are not available. - inverter_power = 0 - for i in non_vebus_inverters: - inverter_current = self._dbusmonitor.get_value(i, '/Dc/0/Current') - if inverter_current is not None: - inverter_power += self._dbusmonitor.get_value( - i, '/Dc/0/Voltage', 0) * inverter_current - else: - inverter_power += self._dbusmonitor.get_value( - i, '/Ac/Out/L1/V', 0) * self._dbusmonitor.get_value( - i, '/Ac/Out/L1/I', 0) - newvalues['/Dc/System/MeasurementType'] = 0 # estimated - # FIXME In future we will subtract alternator power from the - # calculated DC power, because it will be individually - # displayed. For now, we leave it out so that in the current - # version of Venus it does not break user's expectations. - #newvalues['/Dc/System/Power'] = dc_pv_power + charger_power + fuelcell_power + vebuspower - inverter_power - battery_power - alternator_power - newvalues['/Dc/System/Power'] = dc_pv_power + charger_power + fuelcell_power + vebuspower - inverter_power - battery_power - - elif self._settings['hasdcsystem'] == 1 and solarchargers_loadoutput_power is not None: - newvalues['/Dc/System/MeasurementType'] = 0 # estimated - newvalues['/Dc/System/Power'] = solarchargers_loadoutput_power - - # ==== Vebus ==== - multi_path = getattr(delegates.Multi.instance.multi, 'service', None) - if multi_path is not None: - dc_current = self._dbusmonitor.get_value(multi_path, '/Dc/0/Current') - newvalues['/Dc/Vebus/Current'] = dc_current - dc_power = self._dbusmonitor.get_value(multi_path, '/Dc/0/Power') - # Just in case /Dc/0/Power is not available - if dc_power == None and dc_current is not None: - dc_voltage = self._dbusmonitor.get_value(multi_path, '/Dc/0/Voltage') - if dc_voltage is not None: - dc_power = dc_voltage * dc_current - # Note that there is also vebuspower, which is the total DC power summed over all multis. - # However, this value cannot be combined with /Dc/Multi/Current, because it does not make sense - # to add the Dc currents of all multis if they do not share the same DC voltage. - newvalues['/Dc/Vebus/Power'] = dc_power - - # ===== AC IN SOURCE ===== - ac_in_source = None - active_input = None - if multi_path is None: - # Check if we have an non-VE.Bus inverter. - if non_vebus_inverter is not None: - if (active_input := self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/ActiveIn/ActiveInput')) is not None and \ - active_input in (0, 1) and \ - (active_type := self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/In/{}/Type'.format(active_input + 1))) is not None: - ac_in_source = active_type - else: - ac_in_source = 240 - else: - active_input = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/ActiveInput') - if active_input == 0xF0: - # Not connected - ac_in_source = 240 - elif active_input is not None: - settings_path = '/Settings/SystemSetup/AcInput%s' % (active_input + 1) - ac_in_source = self._dbusmonitor.get_value('com.victronenergy.settings', settings_path) - newvalues['/Ac/ActiveIn/Source'] = ac_in_source - - # ===== GRID METERS & CONSUMPTION ==== - grid_meter = delegates.AcInputs.instance.gridmeter - genset_meter = delegates.AcInputs.instance.gensetmeter - - # Make an educated guess as to what is being consumed from an AC source. If ac_in_source - # indicates grid, genset or shore, we use that. If the Multi is off, or disconnected through - # a relay assistant or otherwise, then assume the presence of a .grid or .genset service indicates - # presence of that AC source. If both are available, then give up. This decision making is here - # so the GUI has something to present even if the Multi is off. - ac_in_guess = ac_in_source - if ac_in_guess in (None, 0xF0): - if genset_meter is None and grid_meter is not None: - ac_in_guess = 1 - elif grid_meter is None and genset_meter is not None: - ac_in_guess = 2 - - consumption = { "L1" : None, "L2" : None, "L3" : None } - currentconsumption = { "L1" : None, "L2" : None, "L3" : None } - for device_type, em, _types in (('Grid', grid_meter, (1, 3)), ('Genset', genset_meter, (2,))): - # If a grid meter is present we use values from it. If not, we look at the multi. If it has - # AcIn1 or AcIn2 connected to the grid, we use those values. - # com.victronenergy.grid.??? indicates presence of an energy meter used as grid meter. - # com.victronenergy.vebus.???/Ac/ActiveIn/ActiveInput: decides which whether we look at AcIn1 - # or AcIn2 as possible grid connection. - uses_active_input = ac_in_source in _types - for phase in consumption: - p = None - mc = None - pvpower = newvalues.get('/Ac/PvOn%s/%s/Power' % (device_type, phase)) - pvcurrent = newvalues.get('/Ac/PvOn%s/%s/Current' % (device_type, phase)) - if em is not None: - p = self._dbusmonitor.get_value(em.service, '/Ac/%s/Power' % phase) - mc = self._dbusmonitor.get_value(em.service, '/Ac/%s/Current' % phase) - # Compute consumption between energy meter and multi (meter power - multi AC in) and - # add an optional PV inverter on input to the mix. - c = None - cc = None - if uses_active_input: - if multi_path is not None: - try: - c = _safeadd(c, -self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/P' % phase)) - cc = _safeadd(cc, -self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/I' % phase)) - except TypeError: - pass - elif non_vebus_inverter is not None and active_input in (0, 1): - try: - c = _safeadd(c, -self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/In/%d/%s/P' % (active_input+1, phase))) - cc = _safeadd(cc, -self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/In/%d/%s/I' % (active_input+1, phase))) - except TypeError: - pass - - # If there's any power coming from a PV inverter in the inactive AC in (which is unlikely), - # it will still be used, because there may also be a load in the same ACIn consuming - # power, or the power could be fed back to the net. - c = _safeadd(c, p, pvpower) - cc = _safeadd(cc, mc, pvcurrent) - consumption[phase] = _safeadd(consumption[phase], _safemax(0, c)) - currentconsumption[phase] = _safeadd(currentconsumption[phase], _safemax(0, cc)) - else: - if uses_active_input: - if multi_path is not None and ( - p := self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/P' % phase)) is not None: - consumption[phase] = _safeadd(0, consumption[phase]) - currentconsumption[phase] = _safeadd(0, currentconsumption[phase]) - mc = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/I' % phase) - elif non_vebus_inverter is not None and active_input in (0, 1): - p = self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/In/%d/%s/P' % (active_input + 1, phase)) - mc = self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/In/%d/%s/I' % (active_input + 1, phase)) - if p is not None: - consumption[phase] = _safeadd(0, consumption[phase]) - currentconsumption[phase] = _safeadd(0, currentconsumption[phase]) - - # No relevant energy meter present. Assume there is no load between the grid and the multi. - # There may be a PV inverter present though (Hub-3 setup). - try: - p = _safeadd(p, -pvpower) - mc = _safeadd(mc, -pvcurrent) - except TypeError: - pass - - newvalues['/Ac/%s/%s/Power' % (device_type, phase)] = p - newvalues['/Ac/%s/%s/Current' % (device_type, phase)] = mc - if ac_in_guess in _types: - newvalues['/Ac/ActiveIn/%s/Power' % (phase,)] = p - newvalues['/Ac/ActiveIn/%s/Current' % (phase,)] = mc - - self._compute_number_of_phases('/Ac/%s' % device_type, newvalues) - self._compute_number_of_phases('/Ac/ActiveIn', newvalues) - - product_id = None - device_type_id = None - if em is not None: - product_id = em.product_id - device_type_id = em.device_type - if product_id is None and uses_active_input: - if multi_path is not None: - product_id = self._dbusmonitor.get_value(multi_path, '/ProductId') - elif non_vebus_inverter is not None: - product_id = self._dbusmonitor.get_value(non_vebus_inverter, '/ProductId') - newvalues['/Ac/%s/ProductId' % device_type] = product_id - newvalues['/Ac/%s/DeviceType' % device_type] = device_type_id - - # If we have an ESS system and RunWithoutGridMeter is set, there cannot be load on the AC-In, so it - # must be on AC-Out. Hence we do calculate AC-Out consumption even if 'useacout' is disabled. - # Similarly all load are by definition on the output if this is not an ESS system. - use_ac_out = \ - self._settings['useacout'] == 1 or \ - (multi_path is not None and self._dbusmonitor.get_value(multi_path, '/Hub4/AssistantId') not in (4, 5)) or \ - self._dbusmonitor.get_value('com.victronenergy.settings', '/Settings/CGwacs/RunWithoutGridMeter') == 1 - for phase in consumption: - c = None - a = None - if use_ac_out: - c = newvalues.get('/Ac/PvOnOutput/%s/Power' % phase) - a = newvalues.get('/Ac/PvOnOutput/%s/Current' % phase) - if multi_path is None: - for inv in non_vebus_inverters: - ac_out = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/P' % phase) - i = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/I' % phase) - - # Some models don't show power, try apparent power, - # else calculate it - if ac_out is None: - ac_out = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/S' % phase) - if ac_out is None: - u = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/V' % phase) - if None not in (i, u): - ac_out = i * u - c = _safeadd(c, ac_out) - a = _safeadd(a, i) - else: - ac_out = self._dbusmonitor.get_value(multi_path, '/Ac/Out/%s/P' % phase) - c = _safeadd(c, ac_out) - i_out = self._dbusmonitor.get_value(multi_path, '/Ac/Out/%s/I' % phase) - a = _safeadd(a, i_out) - c = _safemax(0, c) - a = _safemax(0, a) - newvalues['/Ac/ConsumptionOnOutput/%s/Power' % phase] = c - newvalues['/Ac/ConsumptionOnOutput/%s/Current' % phase] = a - newvalues['/Ac/ConsumptionOnInput/%s/Power' % phase] = consumption[phase] - newvalues['/Ac/ConsumptionOnInput/%s/Current' % phase] = currentconsumption[phase] - newvalues['/Ac/Consumption/%s/Power' % phase] = _safeadd(consumption[phase], c) - newvalues['/Ac/Consumption/%s/Current' % phase] = _safeadd(currentconsumption[phase], a) - self._compute_number_of_phases('/Ac/Consumption', newvalues) - self._compute_number_of_phases('/Ac/ConsumptionOnOutput', newvalues) - self._compute_number_of_phases('/Ac/ConsumptionOnInput', newvalues) - - for m in self._modules: - m.update_values(newvalues) - - # ==== UPDATE DBUS ITEMS ==== - with self._dbusservice as sss: - for path in self._summeditems.keys(): - # Why the None? Because we want to invalidate things we don't have anymore. - sss[path] = newvalues.get(path, None) - - def _handleservicechange(self): - # Update the available battery monitor services, used to populate the dropdown in the settings. - # Below code makes a dictionary. The key is [dbuserviceclass]/[deviceinstance]. For example - # "battery/245". The value is the name to show to the user in the dropdown. The full dbus- - # servicename, ie 'com.victronenergy.vebus.ttyO1' is not used, since the last part of that is not - # fixed. dbus-serviceclass name and the device instance are already fixed, so best to use those. - - services = self._get_connected_service_list('com.victronenergy.vebus') - services.update(self._get_connected_service_list('com.victronenergy.battery')) - services.update({k: v for k, v in self._get_connected_service_list( - 'com.victronenergy.multi').items() if self._dbusmonitor.get_value(k, '/Soc') is not None}) - services.update({k: v for k, v in self._get_connected_service_list( - 'com.victronenergy.inverter').items() if self._dbusmonitor.get_value(k, '/Soc') is not None}) - - ul = {self.BATSERVICE_DEFAULT: 'Automatic', self.BATSERVICE_NOBATTERY: 'No battery monitor'} - for servicename, instance in services.items(): - key = self._get_instance_service_name(servicename, instance) - ul[key] = self._get_readable_service_name(servicename) - self._dbusservice['/AvailableBatteryServices'] = json.dumps(ul) - - ul = {self.BATSERVICE_DEFAULT: 'Automatic', self.BATSERVICE_NOBATTERY: 'No battery monitor'} - # For later: for device supporting multiple Dc measurement we should add entries for /Dc/1 etc as - # well. - for servicename, instance in services.items(): - key = self._get_instance_service_name(servicename, instance).replace('.', '_').replace('/', '_') + '/Dc/0' - ul[key] = self._get_readable_service_name(servicename) - self._dbusservice['/AvailableBatteryMeasurements'] = ul - - self._determinebatteryservice() - self._updatepvinverterspidlist() - - self._changed = True - - def _get_readable_service_name(self, servicename): - return '%s on %s' % ( - self._dbusmonitor.get_value(servicename, '/ProductName'), - self._dbusmonitor.get_value(servicename, '/Mgmt/Connection')) - - def _get_instance_service_name(self, service, instance): - return '%s/%s' % ('.'.join(service.split('.')[0:3]), instance) - - def _remove_unconnected_services(self, services): - # Workaround: because com.victronenergy.vebus is available even when there is no vebus product - # connected, remove any service that is not connected. Previously we used - # /State since mandatory path /Connected is not implemented in mk2dbus, - # but this has since been resolved. - for servicename in list(services.keys()): - if (self._dbusmonitor.get_value(servicename, '/Connected') != 1 - or self._dbusmonitor.get_value(servicename, '/ProductName') is None - or self._dbusmonitor.get_value(servicename, '/Mgmt/Connection') is None): - del services[servicename] - - def _dbus_value_changed(self, dbusServiceName, dbusPath, dict, changes, deviceInstance): - self._changed = True - - # Workaround because com.victronenergy.vebus is available even when there is no vebus product - # connected. - if (dbusPath in ['/Connected', '/ProductName', '/Mgmt/Connection'] or - (dbusPath == '/State' and dbusServiceName.split('.')[0:3] == ['com', 'victronenergy', 'vebus'])): - self._handleservicechange() - - # Track the timezone changes - if dbusPath == '/Settings/System/TimeZone': - tz = changes.get('Value') - if tz is not None: - os.environ['TZ'] = tz - time.tzset() - - def _device_added(self, service, instance, do_service_change=True): - if do_service_change: - self._handleservicechange() - - for m in self._modules: - m.device_added(service, instance, do_service_change) - - def _device_removed(self, service, instance): - self._handleservicechange() - - for m in self._modules: - m.device_removed(service, instance) - - def _gettext(self, path, value): - if path == '/Dc/Battery/State': - state = {self.STATE_IDLE: 'Idle', self.STATE_CHARGING: 'Charging', - self.STATE_DISCHARGING: 'Discharging'} - return state[value] - item = self._summeditems.get(path) - if item is not None: - return item['gettext'] % value - return str(value) - - def _compute_number_of_phases(self, path, newvalues): - number_of_phases = None - for phase in range(1, 4): - p = newvalues.get('%s/L%s/Power' % (path, phase)) - if p is not None: - number_of_phases = phase - newvalues[path + '/NumberOfPhases'] = number_of_phases - - def _get_connected_service_list(self, classfilter=None): - services = self._dbusmonitor.get_service_list(classfilter=classfilter) - self._remove_unconnected_services(services) - return services - - # returns a servicename string - def _get_first_connected_service(self, classfilter): - services = self._get_connected_service_list(classfilter=classfilter) - if len(services) == 0: - return None - return next(iter(services.items()), (None,))[0] - - # returns a tuple (servicename, instance) - def _get_service_having_lowest_instance(self, classfilter=None): - services = self._get_connected_service_list(classfilter=classfilter) - if len(services) == 0: - return None - - # sort the dict by value; returns list of tuples: (value, key) - s = sorted((value, key) for (key, value) in services.items()) - return (s[0][1], s[0][0]) - - -class DbusSystemCalc(SystemCalc): - def _create_dbus_monitor(self, *args, **kwargs): - return DbusMonitor(*args, **kwargs) - - def _create_settings(self, *args, **kwargs): - bus = dbus.SessionBus() if 'DBUS_SESSION_BUS_ADDRESS' in os.environ else dbus.SystemBus() - return SettingsDevice(bus, *args, timeout=10, **kwargs) - - def _create_dbus_service(self): - venusversion, venusbuildtime = self._get_venus_versioninfo() - - dbusservice = VeDbusService('com.victronenergy.system') - dbusservice.add_mandatory_paths( - processname=__file__, - processversion=softwareVersion, - connection='data from other dbus processes', - deviceinstance=0, - productid=None, - productname=None, - firmwareversion=venusversion, - hardwareversion=None, - connected=1) - dbusservice.add_path('/FirmwareBuild', value=venusbuildtime) - return dbusservice - - def _get_venus_versioninfo(self): - try: - with open("/opt/victronenergy/version", "r") as fp: - version, software, buildtime = fp.read().split('\n')[:3] - major, minor, _, rev = re.compile('v([0-9]*)\.([0-9]*)(~([0-9]*))?').match(version).groups() - return (int(major, 16)<<16)+(int(minor, 16)<<8)+(0 if rev is None else int(rev, 16)), buildtime - except Exception: - pass - return 0, '0' - -if __name__ == "__main__": - # Argument parsing - parser = argparse.ArgumentParser( - description='Converts readings from AC-Sensors connected to a VE.Bus device in a pvinverter ' + - 'D-Bus service.' - ) - - parser.add_argument("-d", "--debug", help="set logging level to debug", - action="store_true") - - args = parser.parse_args() - - print("-------- dbus_systemcalc, v" + softwareVersion + " is starting up --------") - logger = setup_logging(args.debug) - - # Have a mainloop, so we can send/receive asynchronous calls to and from dbus - DBusGMainLoop(set_as_default=True) - - systemcalc = DbusSystemCalc() - - # Start and run the mainloop - logger.info("Starting mainloop, responding only on events") - mainloop = GLib.MainLoop() - mainloop.run() diff --git a/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v3.40~10.orig b/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v3.40~10.orig deleted file mode 100755 index 67737f0d..00000000 --- a/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v3.40~10.orig +++ /dev/null @@ -1,1287 +0,0 @@ -#!/usr/bin/python3 -u -# -*- coding: utf-8 -*- - -from dbus.mainloop.glib import DBusGMainLoop -import dbus -import argparse -import sys -import os -import json -import time -import re -from gi.repository import GLib - -# Victron packages -sys.path.insert(1, os.path.join(os.path.dirname(__file__), 'ext', 'velib_python')) -from vedbus import VeDbusService -from ve_utils import get_vrm_portal_id, exit_on_error -from dbusmonitor import DbusMonitor -from settingsdevice import SettingsDevice -from logger import setup_logging -import delegates -from sc_utils import safeadd as _safeadd, safemax as _safemax - -softwareVersion = '2.162' - -class SystemCalc: - STATE_IDLE = 0 - STATE_CHARGING = 1 - STATE_DISCHARGING = 2 - BATSERVICE_DEFAULT = 'default' - BATSERVICE_NOBATTERY = 'nobattery' - def __init__(self): - # Why this dummy? Because DbusMonitor expects these values to be there, even though we don't - # need them. So just add some dummy data. This can go away when DbusMonitor is more generic. - dummy = {'code': None, 'whenToLog': 'configChange', 'accessLevel': None} - dbus_tree = { - 'com.victronenergy.solarcharger': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Load/I': dummy, - '/FirmwareVersion': dummy}, - 'com.victronenergy.battery': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/DeviceInstance': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/1/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Soc': dummy, - '/Sense/Current': dummy, - '/TimeToGo': dummy, - '/ConsumedAmphours': dummy, - '/ProductId': dummy, - '/CustomName': dummy, - '/Info/MaxChargeVoltage': dummy}, - 'com.victronenergy.vebus' : { - '/Ac/ActiveIn/ActiveInput': dummy, - '/Ac/ActiveIn/L1/P': dummy, - '/Ac/ActiveIn/L2/P': dummy, - '/Ac/ActiveIn/L3/P': dummy, - '/Ac/ActiveIn/L1/I': dummy, - '/Ac/ActiveIn/L2/I': dummy, - '/Ac/ActiveIn/L3/I': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L2/P': dummy, - '/Ac/Out/L3/P': dummy, - '/Ac/Out/L1/I': dummy, - '/Ac/Out/L2/I': dummy, - '/Ac/Out/L3/I': dummy, - '/Connected': dummy, - '/ProductId': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Mode': dummy, - '/State': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.fuelcell': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy}, - 'com.victronenergy.charger': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/1/Voltage': dummy, - '/Dc/1/Current': dummy, - '/Dc/2/Voltage': dummy, - '/Dc/2/Current': dummy}, - 'com.victronenergy.grid' : { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/ProductId' : dummy, - '/DeviceType' : dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, - '/Ac/L1/Current': dummy, - '/Ac/L2/Current': dummy, - '/Ac/L3/Current': dummy}, - 'com.victronenergy.genset' : { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/ProductId' : dummy, - '/DeviceType' : dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, - '/Ac/L1/Current': dummy, - '/Ac/L2/Current': dummy, - '/Ac/L3/Current': dummy, - '/StarterVoltage': dummy}, - 'com.victronenergy.settings' : { - '/Settings/SystemSetup/AcInput1' : dummy, - '/Settings/SystemSetup/AcInput2' : dummy, - '/Settings/CGwacs/RunWithoutGridMeter' : dummy, - '/Settings/System/TimeZone' : dummy}, - 'com.victronenergy.temperature': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy}, - 'com.victronenergy.inverter': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L1/S': dummy, - '/Ac/Out/L1/V': dummy, - '/Ac/Out/L1/I': dummy, - '/Ac/Out/L2/P': dummy, - '/Ac/Out/L2/S': dummy, - '/Ac/Out/L2/V': dummy, - '/Ac/Out/L2/I': dummy, - '/Ac/Out/L3/P': dummy, - '/Ac/Out/L3/S': dummy, - '/Ac/Out/L3/V': dummy, - '/Ac/Out/L3/I': dummy, - '/Yield/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.multi': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Ac/ActiveIn/ActiveInput': dummy, - '/Ac/In/1/Type': dummy, - '/Ac/In/2/Type': dummy, - '/Ac/In/1/L1/P': dummy, - '/Ac/In/1/L1/I': dummy, - '/Ac/In/2/L1/P': dummy, - '/Ac/In/2/L1/I': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L1/V': dummy, - '/Ac/Out/L1/I': dummy, - '/Ac/In/1/L2/P': dummy, - '/Ac/In/1/L2/I': dummy, - '/Ac/In/2/L2/P': dummy, - '/Ac/In/2/L2/I': dummy, - '/Ac/Out/L2/P': dummy, - '/Ac/Out/L2/V': dummy, - '/Ac/Out/L2/I': dummy, - '/Ac/In/1/L3/P': dummy, - '/Ac/In/1/L3/I': dummy, - '/Ac/In/2/L3/P': dummy, - '/Ac/In/2/L3/I': dummy, - '/Ac/Out/L3/P': dummy, - '/Ac/Out/L3/V': dummy, - '/Ac/Out/L3/I': dummy, - '/Yield/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.dcsystem': { - '/Dc/0/Voltage': dummy, - '/Dc/0/Power': dummy - }, - 'com.victronenergy.alternator': { - '/Dc/0/Power': dummy - } - } - - self._modules = [ - delegates.Multi(), - delegates.HubTypeSelect(), - delegates.VebusSocWriter(), - delegates.ServiceMapper(), - delegates.RelayState(), - delegates.BuzzerControl(), - delegates.LgCircuitBreakerDetect(), - delegates.BatterySoc(self), - delegates.Dvcc(self), - delegates.BatterySense(self), - delegates.BatterySettings(self), - delegates.SystemState(self), - delegates.BatteryLife(), - delegates.ScheduledCharging(), - delegates.SourceTimers(), - delegates.BatteryData(), - delegates.Gps(), - delegates.AcInputs(), - delegates.GensetStartStop(), - delegates.SocSync(self), - delegates.PvInverters(), - delegates.BatteryService(self), - delegates.CanBatterySense(), - delegates.InverterCharger(), - delegates.DynamicEss(), - delegates.LoadShedding()] - - for m in self._modules: - for service, paths in m.get_input(): - s = dbus_tree.setdefault(service, {}) - for path in paths: - s[path] = dummy - - self._dbusmonitor = self._create_dbus_monitor(dbus_tree, valueChangedCallback=self._dbus_value_changed, - deviceAddedCallback=self._device_added, deviceRemovedCallback=self._device_removed) - - # Used to store AC output maximum values for the scenarios: - # No AC input connected - # Ac input 1 connected - # Ac input 2 connected - self._acMaxima = { - 'NoAcIn': 0, - 'AcIn1': 0, - 'AcIn2': 0 - } - - self._minMaxPaths = { - '/Ac/In/0/Current/Min': [float(0), -float("inf"), 0], - '/Ac/In/1/Current/Min': [float(0), -float("inf"), 0], - '/Ac/In/0/Current/Max': [float(0), 0, float("inf")], - '/Ac/In/1/Current/Max': [float(0), 0, float("inf")], - '/Dc/Input/Power/Max': [float(0), 0, float("inf")], - '/Dc/System/Power/Max': [float(0), 0, float("inf")], - '/Pv/Power/Max': [float(0), 0, float("inf")] - } - - for p in self._acMaxima.keys(): - self._minMaxPaths['/Ac/%s/Consumption/Current/Max' % p] = [float(0), 0, float("inf")] - - # Connect to localsettings - supported_settings = { - 'batteryservice': ['/Settings/SystemSetup/BatteryService', self.BATSERVICE_DEFAULT, 0, 0], - 'hasdcsystem': ['/Settings/SystemSetup/HasDcSystem', 0, 0, 1], - 'useacout': ['/Settings/SystemSetup/HasAcOutSystem', 1, 0, 1], - 'gaugeautomax': ['/Settings/Gui/Gauges/AutoMax', 1, 0, 1]} - - for p, s in self._minMaxPaths.items(): - supported_settings[p] = ['/Settings/Gui/Gauges' + p, s[0], s[1], s[2]] - - for m in self._modules: - for setting in m.get_settings(): - supported_settings[setting[0]] = list(setting[1:]) - - self._settings = self._create_settings(supported_settings, self._handlechangedsetting) - - self._dbusservice = self._create_dbus_service() - - for m in self._modules: - m.set_sources(self._dbusmonitor, self._settings, self._dbusservice) - - # At this moment, VRM portal ID is the MAC address of the CCGX. Anyhow, it should be string uniquely - # identifying the CCGX. - self._dbusservice.add_path('/Serial', value=get_vrm_portal_id()) - self._dbusservice.add_path( - '/AvailableBatteryServices', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/AvailableBatteryMeasurements', value=None) - self._dbusservice.add_path( - '/AutoSelectedBatteryService', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/AutoSelectedBatteryMeasurement', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/ActiveBatteryService', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/Dc/Battery/BatteryService', value=None) - self._summeditems = { - '/Ac/Grid/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L1/Current': {'gettext': '%.1F A'}, - '/Ac/Grid/L2/Current': {'gettext': '%.1F A'}, - '/Ac/Grid/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Grid/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Grid/ProductId': {'gettext': '%s'}, - '/Ac/Grid/DeviceType': {'gettext': '%s'}, - '/Ac/Genset/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L1/Current': {'gettext': '%.1F A'}, - '/Ac/Genset/L2/Current': {'gettext': '%.1F A'}, - '/Ac/Genset/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Genset/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Genset/ProductId': {'gettext': '%s'}, - '/Ac/Genset/DeviceType': {'gettext': '%s'}, - '/Ac/ConsumptionOnOutput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L1/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnOutput/L2/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnOutput/L3/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnInput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L1/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnInput/L2/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnInput/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Consumption/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L1/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/L2/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/NumberOfPhases': {'gettext': '%.0F W'}, - '/Dc/Pv/Power': {'gettext': '%.0F W'}, - '/Dc/Pv/Current': {'gettext': '%.1F A'}, - '/Dc/Battery/Voltage': {'gettext': '%.2F V'}, - '/Dc/Battery/VoltageService': {'gettext': '%s'}, - '/Dc/Battery/Current': {'gettext': '%.1F A'}, - '/Dc/Battery/Power': {'gettext': '%.0F W'}, - '/Dc/Battery/State': {'gettext': lambda v: ({ - self.STATE_IDLE: 'Idle', - self.STATE_CHARGING: 'Charging', - self.STATE_DISCHARGING: 'Discharging'}.get(v, 'Unknown'))}, - '/Dc/Battery/TimeToGo': {'gettext': '%.0F s'}, - '/Dc/Battery/ConsumedAmphours': {'gettext': '%.1F Ah'}, - '/Dc/Battery/ProductId': {'gettext': '0x%x'}, - '/Dc/Charger/Power': {'gettext': '%.0F %%'}, - '/Dc/FuelCell/Power': {'gettext': '%.0F %%'}, - '/Dc/Alternator/Power': {'gettext': '%.0F W'}, - '/Dc/System/Power': {'gettext': '%.0F W'}, - '/Dc/System/MeasurementType': {'gettext': '%d'}, - '/Ac/ActiveIn/Source': {'gettext': '%s'}, - '/Ac/ActiveIn/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/L3/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/L1/Current': {'gettext': '%.1F A'}, - '/Ac/ActiveIn/L2/Current': {'gettext': '%.1F A'}, - '/Ac/ActiveIn/L3/Current': {'gettext': '%.1F A'}, - '/Ac/ActiveIn/NumberOfPhases': {'gettext': '%d'}, - '/Ac/In/0/Current/Min': {'gettext': '%.1F'}, - '/Ac/In/0/Current/Max': {'gettext': '%.1F'}, - '/Ac/In/1/Current/Min': {'gettext': '%.1F'}, - '/Ac/In/1/Current/Max': {'gettext': '%.1F'}, - '/Ac/Consumption/Current/Max': {'gettext': '%.1F'}, - '/Pv/Power/Max': {'gettext': '%d'}, - '/Dc/Input/Power/Max': {'gettext': '%d'}, - '/Dc/System/Power/Max': {'gettext': '%d'} - } - - for m in self._modules: - self._summeditems.update(m.get_output()) - - for path in self._summeditems.keys(): - self._dbusservice.add_path(path, value=None, gettextcallback=self._gettext) - - self._batteryservice = None - self._determinebatteryservice() - - if self._batteryservice is None: - logger.info("Battery service initialized to None (setting == %s)" % - self._settings['batteryservice']) - - self._changed = True - for service, instance in self._dbusmonitor.get_service_list().items(): - self._device_added(service, instance, do_service_change=False) - - self._handleservicechange() - self._updatevalues() - - GLib.timeout_add(1000, exit_on_error, self._handletimertick) - - 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): - raise Exception("This function should be overridden") - - def _handlechangedsetting(self, setting, oldvalue, newvalue): - self._determinebatteryservice() - self._changed = True - - # Give our delegates a chance to react on a settings change - for m in self._modules: - m.settings_changed(setting, oldvalue, newvalue) - - def _find_device_instance(self, serviceclass, instance): - """ Gets a mapping of services vs DeviceInstance using - get_service_list. Then searches for the specified DeviceInstance - and returns the service name. """ - services = self._dbusmonitor.get_service_list(classfilter=serviceclass) - - for k, v in services.items(): - if v == instance: - return k - return None - - def _determinebatteryservice(self): - auto_battery_service = self._autoselect_battery_service() - auto_battery_measurement = None - auto_selected = False - if auto_battery_service is not None: - services = self._dbusmonitor.get_service_list() - if auto_battery_service in services: - auto_battery_measurement = \ - self._get_instance_service_name(auto_battery_service, services[auto_battery_service]) - auto_battery_measurement = auto_battery_measurement.replace('.', '_').replace('/', '_') + '/Dc/0' - self._dbusservice['/AutoSelectedBatteryMeasurement'] = auto_battery_measurement - - if self._settings['batteryservice'] == self.BATSERVICE_DEFAULT: - auto_selected = True - newbatteryservice = auto_battery_service - self._dbusservice['/AutoSelectedBatteryService'] = ( - 'No battery monitor found' if newbatteryservice is None else - self._get_readable_service_name(newbatteryservice)) - - elif self._settings['batteryservice'] == self.BATSERVICE_NOBATTERY: - self._dbusservice['/AutoSelectedBatteryService'] = None - newbatteryservice = None - - else: - self._dbusservice['/AutoSelectedBatteryService'] = None - - s = self._settings['batteryservice'].split('/') - if len(s) != 2: - logger.error("The battery setting (%s) is invalid!" % self._settings['batteryservice']) - serviceclass = s[0] - instance = int(s[1]) if len(s) == 2 else None - - # newbatteryservice might turn into None if a chosen battery - # monitor no longer exists. Don't auto change the setting (it might - # come back) and don't autoselect another. - newbatteryservice = self._find_device_instance(serviceclass, instance) - - if newbatteryservice != self._batteryservice: - services = self._dbusmonitor.get_service_list() - instance = services.get(newbatteryservice, None) - if instance is None: - battery_service = None - else: - battery_service = self._get_instance_service_name(newbatteryservice, instance) - self._dbusservice['/ActiveBatteryService'] = battery_service - logger.info("Battery service, setting == %s, changed from %s to %s (%s)" % - (self._settings['batteryservice'], self._batteryservice, newbatteryservice, instance)) - - # Battery service has changed. Notify delegates. - self._dbusservice['/Dc/Battery/BatteryService'] = self._batteryservice = newbatteryservice - for m in self._modules: - m.battery_service_changed(auto_selected, self._batteryservice, newbatteryservice) - - def _autoselect_battery_service(self): - # Default setting business logic: - # first try to use a battery service (BMV or Lynx Shunt VE.Can). If there - # is more than one battery service, just use a random one. If no battery service is - # available, check if there are not Solar chargers and no normal chargers. If they are not - # there, assume this is a hub-2, hub-3 or hub-4 system and use VE.Bus SOC. - batteries = self._get_connected_service_list('com.victronenergy.battery') - - # Pick the battery service that has the lowest DeviceInstance, giving - # preference to those with a BMS. - if len(batteries) > 0: - batteries = [ - (not self._dbusmonitor.seen(s, '/Info/MaxChargeVoltage'), i, s) - for s, i in batteries.items()] - return sorted(batteries, key=lambda x: x[:2])[0][2] - - # No battery services, and there is a charger in the system. Abandon - # hope. - if self._get_first_connected_service('com.victronenergy.charger') is not None: - return None - - # Also no Multi, then give up. - vebus_service = self._get_service_having_lowest_instance('com.victronenergy.vebus') - if vebus_service is None: - # No VE.Bus, but maybe there is an inverter with built-in SOC - # tracking, eg RS Smart or Multi RS. - inverter = self._get_service_having_lowest_instance('com.victronenergy.multi') - if inverter and self._dbusmonitor.get_value(inverter[0], '/Soc') is not None: - return inverter[0] - - inverter = self._get_service_having_lowest_instance('com.victronenergy.inverter') - if inverter and self._dbusmonitor.get_value(inverter[0], '/Soc') is not None: - return inverter[0] - - return None - - # There is a Multi, it supports tracking external charge current from - # solarchargers, and there are no DC loads. Then use it. - if self._dbusmonitor.get_value( - vebus_service[0], '/ExtraBatteryCurrent') is not None \ - and self._get_first_connected_service('com.victronenergy.dcsystem') is None \ - and self._settings['hasdcsystem'] == 0: - return vebus_service[0] - - # Multi does not support tracking solarcharger current, and we have - # solar chargers. Then we cannot use it. - if self._get_first_connected_service('com.victronenergy.solarcharger') is not None: - return None - - # Only a Multi, no other chargers. Then we can use it. - return vebus_service[0] - - @property - def batteryservice(self): - return self._batteryservice - - # Called on a one second timer - def _handletimertick(self): - if self._changed: - self._updatevalues() - self._changed = False - - return True # keep timer running - - def _updatevalues(self): - # ==== PREPARATIONS ==== - newvalues = {} - - # Set the user timezone - if 'TZ' not in os.environ: - tz = self._dbusmonitor.get_value('com.victronenergy.settings', '/Settings/System/TimeZone') - if tz is not None: - os.environ['TZ'] = tz - time.tzset() - - # Determine values used in logic below - vebusses = self._dbusmonitor.get_service_list('com.victronenergy.vebus') - vebuspower = 0 - for vebus in vebusses: - v = self._dbusmonitor.get_value(vebus, '/Dc/0/Voltage') - i = self._dbusmonitor.get_value(vebus, '/Dc/0/Current') - if v is not None and i is not None: - vebuspower += v * i - - # ==== PVINVERTERS ==== - # Work is done in pv-inverter delegate. Ideally all of this should - # happen in update_values in the delegate, but these values are - # used below in calculating consumption, so until this is less - # unwieldy this has to stay here. - # TODO this can go away once consumption below no longer relies - # on these values, or has moved to its own delegate. - newvalues.update(delegates.PvInverters.instance.get_totals()) - self._compute_number_of_phases('/Ac/PvOnGrid', newvalues) - self._compute_number_of_phases('/Ac/PvOnOutput', newvalues) - self._compute_number_of_phases('/Ac/PvOnGenset', newvalues) - - # ==== SOLARCHARGERS ==== - solarchargers = self._dbusmonitor.get_service_list('com.victronenergy.solarcharger') - solarcharger_batteryvoltage = None - solarcharger_batteryvoltage_service = None - solarchargers_charge_power = 0 - solarchargers_loadoutput_power = None - - for solarcharger in solarchargers: - v = self._dbusmonitor.get_value(solarcharger, '/Dc/0/Voltage') - if v is None: - continue - i = self._dbusmonitor.get_value(solarcharger, '/Dc/0/Current') - if i is None: - continue - l = self._dbusmonitor.get_value(solarcharger, '/Load/I', 0) - - if l is not None: - if solarchargers_loadoutput_power is None: - solarchargers_loadoutput_power = l * v - else: - solarchargers_loadoutput_power += l * v - - solarchargers_charge_power += v * i - - # Note that this path is not in the _summeditems{}, making for it to not be - # published on D-Bus. Which fine. The only one needing it is the vebussocwriter- - # delegate. - if '/Dc/Pv/ChargeCurrent' not in newvalues: - newvalues['/Dc/Pv/ChargeCurrent'] = i - else: - newvalues['/Dc/Pv/ChargeCurrent'] += i - - if '/Dc/Pv/Power' not in newvalues: - newvalues['/Dc/Pv/Power'] = v * _safeadd(i, l) - newvalues['/Dc/Pv/Current'] = _safeadd(i, l) - solarcharger_batteryvoltage = v - solarcharger_batteryvoltage_service = solarcharger - else: - newvalues['/Dc/Pv/Power'] += v * _safeadd(i, l) - newvalues['/Dc/Pv/Current'] += _safeadd(i, l) - - # ==== FUELCELLS ==== - fuelcells = self._dbusmonitor.get_service_list('com.victronenergy.fuelcell') - fuelcell_batteryvoltage = None - fuelcell_batteryvoltage_service = None - for fuelcell in fuelcells: - # Assume the battery connected to output 0 is the main battery - v = self._dbusmonitor.get_value(fuelcell, '/Dc/0/Voltage') - if v is None: - continue - - fuelcell_batteryvoltage = v - fuelcell_batteryvoltage_service = fuelcell - - i = self._dbusmonitor.get_value(fuelcell, '/Dc/0/Current') - if i is None: - continue - - if '/Dc/FuelCell/Power' not in newvalues: - newvalues['/Dc/FuelCell/Power'] = v * i - else: - newvalues['/Dc/FuelCell/Power'] += v * i - - # ==== ALTERNATOR ==== - alternators = self._dbusmonitor.get_service_list('com.victronenergy.alternator') - for alternator in alternators: - # Assume the battery connected to output 0 is the main battery - p = self._dbusmonitor.get_value(alternator, '/Dc/0/Power') - if p is None: - continue - - if '/Dc/Alternator/Power' not in newvalues: - newvalues['/Dc/Alternator/Power'] = p - else: - newvalues['/Dc/Alternator/Power'] += p - - # ==== CHARGERS ==== - chargers = self._dbusmonitor.get_service_list('com.victronenergy.charger') - charger_batteryvoltage = None - charger_batteryvoltage_service = None - for charger in chargers: - # Assume the battery connected to output 0 is the main battery - v = self._dbusmonitor.get_value(charger, '/Dc/0/Voltage') - if v is None: - continue - - charger_batteryvoltage = v - charger_batteryvoltage_service = charger - - i = self._dbusmonitor.get_value(charger, '/Dc/0/Current') - if i is None: - continue - - if '/Dc/Charger/Power' not in newvalues: - newvalues['/Dc/Charger/Power'] = v * i - else: - newvalues['/Dc/Charger/Power'] += v * i - - # ==== Other Inverters and Inverter/Chargers ==== - _other_inverters = sorted((di, s) for s, di in self._dbusmonitor.get_service_list('com.victronenergy.multi').items()) + \ - sorted((di, s) for s, di in self._dbusmonitor.get_service_list('com.victronenergy.inverter').items()) - non_vebus_inverters = [x[1] for x in _other_inverters] - non_vebus_inverter = None - if non_vebus_inverters: - non_vebus_inverter = non_vebus_inverters[0] - - # For RS Smart and Multi RS, add PV to the yield - for i in non_vebus_inverters: - if (pv_yield := self._dbusmonitor.get_value(i, "/Yield/Power")) is not None: - newvalues['/Dc/Pv/Power'] = newvalues.get('/Dc/Pv/Power', 0) + pv_yield - - # Used lower down, possibly needed for battery values as well - dcsystems = self._dbusmonitor.get_service_list('com.victronenergy.dcsystem') - - # ==== BATTERY ==== - if self._batteryservice is not None: - batteryservicetype = self._batteryservice.split('.')[2] - assert batteryservicetype in ('battery', 'vebus', 'inverter', 'multi') - - newvalues['/Dc/Battery/TimeToGo'] = self._dbusmonitor.get_value(self._batteryservice,'/TimeToGo') - newvalues['/Dc/Battery/ConsumedAmphours'] = self._dbusmonitor.get_value(self._batteryservice,'/ConsumedAmphours') - newvalues['/Dc/Battery/ProductId'] = self._dbusmonitor.get_value(self._batteryservice, '/ProductId') - - if batteryservicetype in ('battery', 'inverter', 'multi'): - newvalues['/Dc/Battery/Voltage'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Voltage') - newvalues['/Dc/Battery/VoltageService'] = self._batteryservice - newvalues['/Dc/Battery/Current'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Current') - newvalues['/Dc/Battery/Power'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Power') - - elif batteryservicetype == 'vebus': - vebus_voltage = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Voltage') - vebus_current = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Current') - vebus_power = None if vebus_voltage is None or vebus_current is None else vebus_current * vebus_voltage - newvalues['/Dc/Battery/Voltage'] = vebus_voltage - newvalues['/Dc/Battery/VoltageService'] = self._batteryservice - if self._settings['hasdcsystem'] == 1 or dcsystems: - # hasdcsystem will normally disqualify the multi from being - # auto-selected as battery monitor, so the only way we're - # here is if the user explicitly selected the multi as the - # battery service - newvalues['/Dc/Battery/Current'] = vebus_current - if vebus_power is not None: - newvalues['/Dc/Battery/Power'] = vebus_power - else: - battery_power = _safeadd(solarchargers_charge_power, vebus_power) - newvalues['/Dc/Battery/Current'] = battery_power / vebus_voltage if vebus_voltage is not None and vebus_voltage > 0 else None - newvalues['/Dc/Battery/Power'] = battery_power - - - p = newvalues.get('/Dc/Battery/Power', None) - if p is not None: - if p > 30: - newvalues['/Dc/Battery/State'] = self.STATE_CHARGING - elif p < -30: - newvalues['/Dc/Battery/State'] = self.STATE_DISCHARGING - else: - newvalues['/Dc/Battery/State'] = self.STATE_IDLE - - else: - # The battery service is not a BMS/BMV or a suitable vebus. A - # suitable vebus is defined as one explicitly selected by the user, - # or one that was automatically selected for SOC tracking. We may - # however still have a VE.Bus, just not one that can accurately - # track SOC. If we have one, use it as voltage source. Otherwise - # try a solar charger, a charger, a vedirect inverter or a dcsource - # as fallbacks. - batteryservicetype = None - vebusses = self._dbusmonitor.get_service_list('com.victronenergy.vebus') - for vebus in vebusses: - v = self._dbusmonitor.get_value(vebus, '/Dc/0/Voltage') - s = self._dbusmonitor.get_value(vebus, '/State') - if v is not None and s not in (0, None): - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = vebus - break # Skip the else below - else: - # No suitable vebus voltage, try other devices - if non_vebus_inverter is not None and (v := self._dbusmonitor.get_value(non_vebus_inverter, '/Dc/0/Voltage')) is not None: - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = non_vebus_inverter - elif solarcharger_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = solarcharger_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = solarcharger_batteryvoltage_service - elif charger_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = charger_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = charger_batteryvoltage_service - elif fuelcell_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = fuelcell_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = fuelcell_batteryvoltage_service - elif dcsystems: - # Get voltage from first dcsystem - s = next(iter(dcsystems.keys())) - v = self._dbusmonitor.get_value(s, '/Dc/0/Voltage') - if v is not None: - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = s - - # We have no suitable battery monitor, so power and current data - # is not available. We can however calculate it from other values, - # if we have at least a battery voltage. - if '/Dc/Battery/Voltage' in newvalues: - dcsystempower = _safeadd(0, *(self._dbusmonitor.get_value(s, - '/Dc/0/Power', 0) for s in dcsystems)) - if dcsystems or self._settings['hasdcsystem'] == 0: - # Either DC loads are monitored, or there are no - # unmonitored DC loads or chargers: derive battery watts - # and amps from vebus, solarchargers, chargers and measured - # loads. - p = solarchargers_charge_power + newvalues.get('/Dc/Charger/Power', 0) + vebuspower - dcsystempower - voltage = newvalues['/Dc/Battery/Voltage'] - newvalues['/Dc/Battery/Current'] = p / voltage if voltage > 0 else None - newvalues['/Dc/Battery/Power'] = p - - # ==== SYSTEM POWER ==== - # Look for dcsytem devices, add them together. Otherwise, if enabled, - # calculate it - if dcsystems: - newvalues['/Dc/System/MeasurementType'] = 1 # measured - newvalues['/Dc/System/Power'] = 0 - for meter in dcsystems: - newvalues['/Dc/System/Power'] = _safeadd(newvalues['/Dc/System/Power'], - self._dbusmonitor.get_value(meter, '/Dc/0/Power')) - elif self._settings['hasdcsystem'] == 1 and batteryservicetype == 'battery': - # Calculate power being generated/consumed by not measured devices in the network. - # For MPPTs, take all the power, including power going out of the load output. - # /Dc/System: positive: consuming power - # VE.Bus: Positive: current flowing from the Multi to the dc system or battery - # Solarcharger & other chargers: positive: charging - # battery: Positive: charging battery. - # battery = solarcharger + charger + ve.bus - system - - battery_power = newvalues.get('/Dc/Battery/Power') - if battery_power is not None: - dc_pv_power = newvalues.get('/Dc/Pv/Power', 0) - charger_power = newvalues.get('/Dc/Charger/Power', 0) - fuelcell_power = newvalues.get('/Dc/FuelCell/Power', 0) - alternator_power = newvalues.get('/Dc/Alternator/Power', 0) - - # If there are VE.Direct inverters, remove their power from the - # DC estimate. This is done using the AC value when the DC - # power values are not available. - inverter_power = 0 - for i in non_vebus_inverters: - inverter_current = self._dbusmonitor.get_value(i, '/Dc/0/Current') - if inverter_current is not None: - inverter_power += self._dbusmonitor.get_value( - i, '/Dc/0/Voltage', 0) * inverter_current - else: - inverter_power -= self._dbusmonitor.get_value( - i, '/Ac/Out/L1/V', 0) * self._dbusmonitor.get_value( - i, '/Ac/Out/L1/I', 0) - newvalues['/Dc/System/MeasurementType'] = 0 # estimated - # FIXME In future we will subtract alternator power from the - # calculated DC power, because it will be individually - # displayed. For now, we leave it out so that in the current - # version of Venus it does not break user's expectations. - #newvalues['/Dc/System/Power'] = dc_pv_power + charger_power + fuelcell_power + vebuspower + inverter_power - battery_power - alternator_power - newvalues['/Dc/System/Power'] = dc_pv_power + charger_power + fuelcell_power + vebuspower + inverter_power - battery_power - - elif self._settings['hasdcsystem'] == 1 and solarchargers_loadoutput_power is not None: - newvalues['/Dc/System/MeasurementType'] = 0 # estimated - newvalues['/Dc/System/Power'] = solarchargers_loadoutput_power - - # ===== AC IN SOURCE ===== - multi_path = getattr(delegates.Multi.instance.multi, 'service', None) - ac_in_source = None - active_input = None - if multi_path is None: - # Check if we have an non-VE.Bus inverter. - if non_vebus_inverter is not None: - if (active_input := self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/ActiveIn/ActiveInput')) is not None and \ - active_input in (0, 1) and \ - (active_type := self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/In/{}/Type'.format(active_input + 1))) is not None: - ac_in_source = active_type - else: - ac_in_source = 240 - else: - active_input = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/ActiveInput') - if active_input == 0xF0: - # Not connected - ac_in_source = 240 - elif active_input is not None: - settings_path = '/Settings/SystemSetup/AcInput%s' % (active_input + 1) - ac_in_source = self._dbusmonitor.get_value('com.victronenergy.settings', settings_path) - newvalues['/Ac/ActiveIn/Source'] = ac_in_source - - # ===== GRID METERS & CONSUMPTION ==== - grid_meter = delegates.AcInputs.instance.gridmeter - genset_meter = delegates.AcInputs.instance.gensetmeter - - # Make an educated guess as to what is being consumed from an AC source. If ac_in_source - # indicates grid, genset or shore, we use that. If the Multi is off, or disconnected through - # a relay assistant or otherwise, then assume the presence of a .grid or .genset service indicates - # presence of that AC source. If both are available, then give up. This decision making is here - # so the GUI has something to present even if the Multi is off. - ac_in_guess = ac_in_source - if ac_in_guess in (None, 0xF0): - if genset_meter is None and grid_meter is not None: - ac_in_guess = 1 - elif grid_meter is None and genset_meter is not None: - ac_in_guess = 2 - - consumption = { "L1" : None, "L2" : None, "L3" : None } - currentconsumption = { "L1" : None, "L2" : None, "L3" : None } - for device_type, em, _types in (('Grid', grid_meter, (1, 3)), ('Genset', genset_meter, (2,))): - # If a grid meter is present we use values from it. If not, we look at the multi. If it has - # AcIn1 or AcIn2 connected to the grid, we use those values. - # com.victronenergy.grid.??? indicates presence of an energy meter used as grid meter. - # com.victronenergy.vebus.???/Ac/ActiveIn/ActiveInput: decides which whether we look at AcIn1 - # or AcIn2 as possible grid connection. - uses_active_input = ac_in_source in _types - for phase in consumption: - p = None - mc = None - pvpower = newvalues.get('/Ac/PvOn%s/%s/Power' % (device_type, phase)) - pvcurrent = newvalues.get('/Ac/PvOn%s/%s/Current' % (device_type, phase)) - if em is not None: - p = self._dbusmonitor.get_value(em.service, '/Ac/%s/Power' % phase) - mc = self._dbusmonitor.get_value(em.service, '/Ac/%s/Current' % phase) - # Compute consumption between energy meter and multi (meter power - multi AC in) and - # add an optional PV inverter on input to the mix. - c = None - cc = None - if uses_active_input: - if multi_path is not None: - try: - c = _safeadd(c, -self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/P' % phase)) - cc = _safeadd(cc, -self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/I' % phase)) - except TypeError: - pass - elif non_vebus_inverter is not None and active_input in (0, 1): - for i in non_vebus_inverters: - try: - c = _safeadd(c, -self._dbusmonitor.get_value(i, '/Ac/In/%d/%s/P' % (active_input+1, phase))) - cc = _safeadd(cc, -self._dbusmonitor.get_value(i, '/Ac/In/%d/%s/I' % (active_input+1, phase))) - except TypeError: - pass - - # If there's any power coming from a PV inverter in the inactive AC in (which is unlikely), - # it will still be used, because there may also be a load in the same ACIn consuming - # power, or the power could be fed back to the net. - c = _safeadd(c, p, pvpower) - cc = _safeadd(cc, mc, pvcurrent) - consumption[phase] = _safeadd(consumption[phase], _safemax(0, c)) - currentconsumption[phase] = _safeadd(currentconsumption[phase], _safemax(0, cc)) - else: - if uses_active_input: - if multi_path is not None and ( - p := self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/P' % phase)) is not None: - consumption[phase] = _safeadd(0, consumption[phase]) - currentconsumption[phase] = _safeadd(0, currentconsumption[phase]) - mc = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/I' % phase) - elif non_vebus_inverter is not None and active_input in (0, 1): - for i in non_vebus_inverters: - p = _safeadd(p, - self._dbusmonitor.get_value(i, '/Ac/In/%d/%s/P' % (active_input + 1, phase))) - mc = _safeadd(mc, - self._dbusmonitor.get_value(i, '/Ac/In/%d/%s/I' % (active_input + 1, phase))) - if p is not None: - consumption[phase] = _safeadd(0, consumption[phase]) - currentconsumption[phase] = _safeadd(0, currentconsumption[phase]) - - # No relevant energy meter present. Assume there is no load between the grid and the multi. - # There may be a PV inverter present though (Hub-3 setup). - try: - p = _safeadd(p, -pvpower) - mc = _safeadd(mc, -pvcurrent) - except TypeError: - pass - - newvalues['/Ac/%s/%s/Power' % (device_type, phase)] = p - newvalues['/Ac/%s/%s/Current' % (device_type, phase)] = mc - if ac_in_guess in _types: - newvalues['/Ac/ActiveIn/%s/Power' % (phase,)] = p - newvalues['/Ac/ActiveIn/%s/Current' % (phase,)] = mc - - self._compute_number_of_phases('/Ac/%s' % device_type, newvalues) - self._compute_number_of_phases('/Ac/ActiveIn', newvalues) - - product_id = None - device_type_id = None - if em is not None: - product_id = em.product_id - device_type_id = em.device_type - if product_id is None and uses_active_input: - if multi_path is not None: - product_id = self._dbusmonitor.get_value(multi_path, '/ProductId') - elif non_vebus_inverter is not None: - product_id = self._dbusmonitor.get_value(non_vebus_inverter, '/ProductId') - newvalues['/Ac/%s/ProductId' % device_type] = product_id - newvalues['/Ac/%s/DeviceType' % device_type] = device_type_id - - # If we have an ESS system and RunWithoutGridMeter is set, there cannot be load on the AC-In, so it - # must be on AC-Out. Hence we do calculate AC-Out consumption even if 'useacout' is disabled. - # Similarly all load are by definition on the output if this is not an ESS system. - use_ac_out = \ - self._settings['useacout'] == 1 or \ - (multi_path is not None and self._dbusmonitor.get_value(multi_path, '/Hub4/AssistantId') not in (4, 5)) or \ - self._dbusmonitor.get_value('com.victronenergy.settings', '/Settings/CGwacs/RunWithoutGridMeter') == 1 - for phase in consumption: - c = None - a = None - if use_ac_out: - c = newvalues.get('/Ac/PvOnOutput/%s/Power' % phase) - a = newvalues.get('/Ac/PvOnOutput/%s/Current' % phase) - if multi_path is None: - for inv in non_vebus_inverters: - ac_out = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/P' % phase) - i = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/I' % phase) - - # Some models don't show power, try apparent power, - # else calculate it - if ac_out is None: - ac_out = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/S' % phase) - if ac_out is None: - u = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/V' % phase) - if None not in (i, u): - ac_out = i * u - c = _safeadd(c, ac_out) - a = _safeadd(a, i) - else: - ac_out = self._dbusmonitor.get_value(multi_path, '/Ac/Out/%s/P' % phase) - c = _safeadd(c, ac_out) - i_out = self._dbusmonitor.get_value(multi_path, '/Ac/Out/%s/I' % phase) - a = _safeadd(a, i_out) - c = _safemax(0, c) - a = _safemax(0, a) - newvalues['/Ac/ConsumptionOnOutput/%s/Power' % phase] = c - newvalues['/Ac/ConsumptionOnOutput/%s/Current' % phase] = a - newvalues['/Ac/ConsumptionOnInput/%s/Power' % phase] = consumption[phase] - newvalues['/Ac/ConsumptionOnInput/%s/Current' % phase] = currentconsumption[phase] - newvalues['/Ac/Consumption/%s/Power' % phase] = _safeadd(consumption[phase], c) - newvalues['/Ac/Consumption/%s/Current' % phase] = _safeadd(currentconsumption[phase], a) - self._compute_number_of_phases('/Ac/Consumption', newvalues) - self._compute_number_of_phases('/Ac/ConsumptionOnOutput', newvalues) - self._compute_number_of_phases('/Ac/ConsumptionOnInput', newvalues) - - for m in self._modules: - m.update_values(newvalues) - - # ==== UPDATE MINIMUM AND MAXIMUM LEVELS ==== - # min/max values are stored in localsettings and synched once in a while. - # values are stored under /Settings/Gui/Briefview - # /Settings/Gui/Gauges/AutoMax: - # 1-> Automatic: Maxima are updated automatically and synched to localsettings - # 0-> Manual: Maxima are pulled from localsettings. - # min/max computations are done here because the _updatevalues method abstracts them - # away from the delegates. - - # AC output - # This maximum is maintained for 3 situations: - # 1: AC input 1 is connected - # 2: AC input 2 is connected - # 3: No AC input is connected - # All 3 scenarios may lead to different maximum values since the capabilities of the system changes. - # So 3 different maxima are stored and relayed to /Ac/Consumption/Current/Max based on the active scenario. - activeIn = 'AcIn1' if (self._dbusservice['/Ac/In/0/Connected'] == 1) else \ - 'AcIn2' if (self._dbusservice['/Ac/In/1/Connected'] == 1) else \ - 'NoAcIn' - - # Quattro has 2 AC inputs which cannot be active simultaneously. - # activeIn needs to 1 when 'Ac/In/1/Connected' is 1 and can be 0 otherwise. - if (self._settings['gaugeautomax']): - activeInNr = int(activeIn[-1]) -1 if activeIn != 'NoAcIn' else None - - # AC input - # Minimum values occur when feeding back to the grid. - # For the minimum value, make sure it is 0 at its maximum. - # Update correct '/Ac/In/..' based on the current active input. - # When no inputs are active, paths '/Ac/In/[0/1]/Current/[Min/Max] will all be invalidated. - if(activeInNr != None): - newvalues['/Ac/In/%s/Current/Min' % activeInNr] = min(0, - self._dbusservice['/Ac/In/%s/Current/Min' % activeInNr] or float("inf"), - newvalues.get('/Ac/ActiveIn/L1/Current') or float("inf"), - newvalues.get('/Ac/ActiveIn/L2/Current') or float("inf"), - newvalues.get('/Ac/ActiveIn/L3/Current') or float("inf")) - - newvalues['/Ac/In/%s/Current/Max' % activeInNr] = max(self._dbusservice['/Ac/In/%s/Current/Min' % activeInNr] or 0, - newvalues.get('/Ac/ActiveIn/L1/Current') or 0, - newvalues.get('/Ac/ActiveIn/L2/Current') or 0, - newvalues.get('/Ac/ActiveIn/L3/Current') or 0) - - self._acMaxima[activeIn] = max(self._acMaxima[activeIn], - newvalues.get('/Ac/Consumption/L1/Current') or 0, - newvalues.get('/Ac/Consumption/L2/Current') or 0, - newvalues.get('/Ac/Consumption/L3/Current') or 0) - - newvalues['/Ac/Consumption/Current/Max'] = self._acMaxima[activeIn] - - # DC input - newvalues['/Dc/Input/Power/Max'] = max(self._dbusservice['/Dc/Input/Power/Max'] or 0, - sum([newvalues.get('/Dc/Charger/Power') or 0, - newvalues.get('/Dc/FuelCell/Power') or 0, - newvalues.get('/Dc/Alternator/Power') or 0])) - - # DC output - newvalues['/Dc/System/Power/Max'] = _safemax(self._dbusservice['/Dc/System/Power/Max'] or 0, - newvalues.get('/Dc/System/Power') or 0) - - # PV power - newvalues['/Pv/Power/Max'] = _safemax(self._dbusservice['/Pv/Power/Max'] or 0, - _safeadd(newvalues.get('/Dc/Pv/Power') or 0, - self._dbusservice['/Ac/PvOnGrid/L1/Power'], - self._dbusservice['/Ac/PvOnGrid/L2/Power'], - self._dbusservice['/Ac/PvOnGrid/L3/Power'], - self._dbusservice['/Ac/PvOnGenset/L1/Power'], - self._dbusservice['/Ac/PvOnGenset/L2/Power'], - self._dbusservice['/Ac/PvOnGenset/L3/Power'], - self._dbusservice['/Ac/PvOnOutput/L1/Power'], - self._dbusservice['/Ac/PvOnOutput/L2/Power'], - self._dbusservice['/Ac/PvOnOutput/L3/Power'])) - - # Sync max values to localsettings (once each second) - for p in self._minMaxPaths.keys(): - if (p in newvalues and newvalues[p] != self._settings[p]): - self._settings[p] = newvalues[p] - - # Store the ac maxima values for the 3 different scenarios. These aren't in newvalues. - if(self._acMaxima[activeIn] != self._settings['/Ac/%s/Consumption/Current/Max' % activeIn]): - self._settings['/Ac/%s/Consumption/Current/Max' % activeIn] = self._acMaxima[activeIn] - - # Manual mode: relay min/max settings from localsettings to newvalues - # We have to fill newvalues on every iteration here because if we don't the value in dbusservice is invalidated - else: - for p in self._minMaxPaths.keys(): - newvalues[p] = self._settings[p] - - newvalues['/Ac/Consumption/Current/Max'] = self._settings['/Ac/%s/Consumption/Current/Max' % activeIn] - - # ==== UPDATE DBUS ITEMS ==== - with self._dbusservice as sss: - for path in self._summeditems.keys(): - # Why the None? Because we want to invalidate things we don't have anymore. - sss[path] = newvalues.get(path, None) - - def _handleservicechange(self): - # Update the available battery monitor services, used to populate the dropdown in the settings. - # Below code makes a dictionary. The key is [dbuserviceclass]/[deviceinstance]. For example - # "battery/245". The value is the name to show to the user in the dropdown. The full dbus- - # servicename, ie 'com.victronenergy.vebus.ttyO1' is not used, since the last part of that is not - # fixed. dbus-serviceclass name and the device instance are already fixed, so best to use those. - - services = self._get_connected_service_list('com.victronenergy.vebus') - services.update(self._get_connected_service_list('com.victronenergy.battery')) - services.update({k: v for k, v in self._get_connected_service_list( - 'com.victronenergy.multi').items() if self._dbusmonitor.get_value(k, '/Soc') is not None}) - services.update({k: v for k, v in self._get_connected_service_list( - 'com.victronenergy.inverter').items() if self._dbusmonitor.get_value(k, '/Soc') is not None}) - - ul = {self.BATSERVICE_DEFAULT: 'Automatic', self.BATSERVICE_NOBATTERY: 'No battery monitor'} - for servicename, instance in services.items(): - key = self._get_instance_service_name(servicename, instance) - ul[key] = self._get_readable_service_name(servicename) - self._dbusservice['/AvailableBatteryServices'] = json.dumps(ul) - - ul = {self.BATSERVICE_DEFAULT: 'Automatic', self.BATSERVICE_NOBATTERY: 'No battery monitor'} - # For later: for device supporting multiple Dc measurement we should add entries for /Dc/1 etc as - # well. - for servicename, instance in services.items(): - key = self._get_instance_service_name(servicename, instance).replace('.', '_').replace('/', '_') + '/Dc/0' - ul[key] = self._get_readable_service_name(servicename) - self._dbusservice['/AvailableBatteryMeasurements'] = ul - - self._determinebatteryservice() - - self._changed = True - - def _get_readable_service_name(self, servicename): - return '%s on %s' % ( - self._dbusmonitor.get_value(servicename, '/ProductName'), - self._dbusmonitor.get_value(servicename, '/Mgmt/Connection')) - - def _get_instance_service_name(self, service, instance): - return '%s/%s' % ('.'.join(service.split('.')[0:3]), instance) - - def _remove_unconnected_services(self, services): - # Workaround: because com.victronenergy.vebus is available even when there is no vebus product - # connected, remove any service that is not connected. Previously we used - # /State since mandatory path /Connected is not implemented in mk2dbus, - # but this has since been resolved. - for servicename in list(services.keys()): - if (self._dbusmonitor.get_value(servicename, '/Connected') != 1 - or self._dbusmonitor.get_value(servicename, '/ProductName') is None - or self._dbusmonitor.get_value(servicename, '/Mgmt/Connection') is None): - del services[servicename] - - def _dbus_value_changed(self, dbusServiceName, dbusPath, dict, changes, deviceInstance): - self._changed = True - - # Workaround because com.victronenergy.vebus is available even when there is no vebus product - # connected. - if (dbusPath in ['/Connected', '/ProductName', '/Mgmt/Connection'] or - (dbusPath == '/State' and dbusServiceName.split('.')[0:3] == ['com', 'victronenergy', 'vebus'])): - self._handleservicechange() - - # Track the timezone changes - if dbusPath == '/Settings/System/TimeZone': - tz = changes.get('Value') - if tz is not None: - os.environ['TZ'] = tz - time.tzset() - - def _device_added(self, service, instance, do_service_change=True): - if do_service_change: - self._handleservicechange() - - for m in self._modules: - m.device_added(service, instance, do_service_change) - - def _device_removed(self, service, instance): - self._handleservicechange() - - for m in self._modules: - m.device_removed(service, instance) - - def _gettext(self, path, value): - item = self._summeditems.get(path) - if item is not None: - try: - gettext = item['gettext'] - except KeyError: - pass - else: - if callable(gettext): - return gettext(value) - return gettext % value - return str(value) - - def _compute_number_of_phases(self, path, newvalues): - number_of_phases = None - for phase in range(1, 4): - p = newvalues.get('%s/L%s/Power' % (path, phase)) - if p is not None: - number_of_phases = phase - newvalues[path + '/NumberOfPhases'] = number_of_phases - - def _get_connected_service_list(self, classfilter=None): - services = self._dbusmonitor.get_service_list(classfilter=classfilter) - self._remove_unconnected_services(services) - return services - - # returns a servicename string - def _get_first_connected_service(self, classfilter): - services = self._get_connected_service_list(classfilter=classfilter) - if len(services) == 0: - return None - return next(iter(services.items()), (None,))[0] - - # returns a tuple (servicename, instance) - def _get_service_having_lowest_instance(self, classfilter=None): - services = self._get_connected_service_list(classfilter=classfilter) - if len(services) == 0: - return None - - # sort the dict by value; returns list of tuples: (value, key) - s = sorted((value, key) for (key, value) in services.items()) - return (s[0][1], s[0][0]) - - -class DbusSystemCalc(SystemCalc): - def _create_dbus_monitor(self, *args, **kwargs): - return DbusMonitor(*args, **kwargs) - - def _create_settings(self, *args, **kwargs): - bus = dbus.SessionBus() if 'DBUS_SESSION_BUS_ADDRESS' in os.environ else dbus.SystemBus() - return SettingsDevice(bus, *args, timeout=10, **kwargs) - - def _create_dbus_service(self): - venusversion, venusbuildtime = self._get_venus_versioninfo() - - dbusservice = VeDbusService('com.victronenergy.system') - dbusservice.add_mandatory_paths( - processname=__file__, - processversion=softwareVersion, - connection='data from other dbus processes', - deviceinstance=0, - productid=None, - productname=None, - firmwareversion=venusversion, - hardwareversion=None, - connected=1) - dbusservice.add_path('/FirmwareBuild', value=venusbuildtime) - return dbusservice - - def _get_venus_versioninfo(self): - try: - with open("/opt/victronenergy/version", "r") as fp: - version, software, buildtime = fp.read().split('\n')[:3] - major, minor, _, rev = re.compile('v([0-9]*)\.([0-9]*)(~([0-9]*))?').match(version).groups() - return (int(major, 16)<<16)+(int(minor, 16)<<8)+(0 if rev is None else int(rev, 16)), buildtime - except Exception: - pass - return 0, '0' - -if __name__ == "__main__": - # Argument parsing - parser = argparse.ArgumentParser( - description='Converts readings from AC-Sensors connected to a VE.Bus device in a pvinverter ' + - 'D-Bus service.' - ) - - parser.add_argument("-d", "--debug", help="set logging level to debug", - action="store_true") - - args = parser.parse_args() - - print("-------- dbus_systemcalc, v" + softwareVersion + " is starting up --------") - logger = setup_logging(args.debug) - - # Have a mainloop, so we can send/receive asynchronous calls to and from dbus - DBusGMainLoop(set_as_default=True) - - systemcalc = DbusSystemCalc() - - # Start and run the mainloop - logger.info("Starting mainloop, responding only on events") - mainloop = GLib.MainLoop() - mainloop.run() diff --git a/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v3.40~15 b/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v3.40~15 deleted file mode 100755 index 97b0cbee..00000000 --- a/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v3.40~15 +++ /dev/null @@ -1,1489 +0,0 @@ -#!/usr/bin/python3 -u -# -*- coding: utf-8 -*- - -#### modified for GuiMods - -from dbus.mainloop.glib import DBusGMainLoop -import dbus -import argparse -import sys -import os -import json -import time -import re -from gi.repository import GLib - -# Victron packages -sys.path.insert(1, os.path.join(os.path.dirname(__file__), 'ext', 'velib_python')) -from vedbus import VeDbusService -from ve_utils import get_vrm_portal_id, exit_on_error -from dbusmonitor import DbusMonitor -from settingsdevice import SettingsDevice -from logger import setup_logging -import delegates -from sc_utils import safeadd as _safeadd, safemax as _safemax - -softwareVersion = '2.171' - -class SystemCalc: - STATE_IDLE = 0 - STATE_CHARGING = 1 - STATE_DISCHARGING = 2 - BATSERVICE_DEFAULT = 'default' - BATSERVICE_NOBATTERY = 'nobattery' - def __init__(self): - # Why this dummy? Because DbusMonitor expects these values to be there, even though we don't - # need them. So just add some dummy data. This can go away when DbusMonitor is more generic. - dummy = {'code': None, 'whenToLog': 'configChange', 'accessLevel': None} - dbus_tree = { - 'com.victronenergy.solarcharger': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Load/I': dummy, - '/FirmwareVersion': dummy}, - 'com.victronenergy.battery': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/DeviceInstance': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/1/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Soc': dummy, - '/Sense/Current': dummy, - '/TimeToGo': dummy, - '/ConsumedAmphours': dummy, - '/ProductId': dummy, - '/CustomName': dummy, - '/Info/MaxChargeVoltage': dummy}, - 'com.victronenergy.vebus' : { - '/Ac/ActiveIn/ActiveInput': dummy, - '/Ac/ActiveIn/L1/P': dummy, - '/Ac/ActiveIn/L2/P': dummy, - '/Ac/ActiveIn/L3/P': dummy, - '/Ac/ActiveIn/L1/I': dummy, - '/Ac/ActiveIn/L2/I': dummy, - '/Ac/ActiveIn/L3/I': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L2/P': dummy, - '/Ac/Out/L3/P': dummy, - '/Ac/Out/L1/I': dummy, - '/Ac/Out/L2/I': dummy, - '/Ac/Out/L3/I': dummy, -#### add for GuiMods - '/Ac/Out/L1/V': dummy, - '/Ac/Out/L2/V': dummy, - '/Ac/Out/L3/V': dummy, - '/Ac/Out/L1/F': dummy, - '/Ac/Out/L2/F': dummy, - '/Ac/Out/L3/F': dummy, - '/Ac/ActiveIn/L1/V': dummy, - '/Ac/ActiveIn/L2/V': dummy, - '/Ac/ActiveIn/L3/V': dummy, - '/Ac/ActiveIn/L1/F': dummy, - '/Ac/ActiveIn/L2/F': dummy, - '/Ac/ActiveIn/L3/F': dummy, - - '/Connected': dummy, - '/ProductId': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Mode': dummy, - '/State': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.fuelcell': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy}, - 'com.victronenergy.charger': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/1/Voltage': dummy, - '/Dc/1/Current': dummy, - '/Dc/2/Voltage': dummy, - '/Dc/2/Current': dummy}, - 'com.victronenergy.grid' : { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/ProductId' : dummy, - '/DeviceType' : dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, - '/Ac/L1/Current': dummy, - '/Ac/L2/Current': dummy, - '/Ac/L3/Current': dummy, -#### add for GuiMods - '/Ac/L1/Voltage': dummy, - '/Ac/L2/Voltage': dummy, - '/Ac/L3/Voltage': dummy, - '/Ac/L1/Frequency': dummy, - '/Ac/L2/Frequency': dummy, - '/Ac/L3/Frequency': dummy}, - 'com.victronenergy.genset' : { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/ProductId' : dummy, - '/DeviceType' : dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, - '/Ac/L1/Current': dummy, - '/Ac/L2/Current': dummy, - '/Ac/L3/Current': dummy, -#### add for GuiMods - '/Ac/L1/Voltage': dummy, - '/Ac/L2/Voltage': dummy, - '/Ac/L3/Voltage': dummy, - '/Ac/L1/Frequency': dummy, - '/Ac/L2/Frequency': dummy, - '/Ac/L3/Frequency': dummy, - - '/StarterVoltage': dummy}, - 'com.victronenergy.settings' : { - '/Settings/SystemSetup/AcInput1' : dummy, - '/Settings/SystemSetup/AcInput2' : dummy, - '/Settings/CGwacs/RunWithoutGridMeter' : dummy, - '/Settings/System/TimeZone' : dummy}, - 'com.victronenergy.temperature': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy}, - 'com.victronenergy.inverter': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L1/S': dummy, - '/Ac/Out/L1/V': dummy, - '/Ac/Out/L1/I': dummy, - '/Ac/Out/L2/P': dummy, - '/Ac/Out/L2/S': dummy, - '/Ac/Out/L2/V': dummy, - '/Ac/Out/L2/I': dummy, - '/Ac/Out/L3/P': dummy, - '/Ac/Out/L3/S': dummy, - '/Ac/Out/L3/V': dummy, - '/Ac/Out/L3/I': dummy, -#### add for GuiMods - '/Ac/Out/L1/F': dummy, - - '/Yield/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.multi': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Ac/ActiveIn/ActiveInput': dummy, - '/Ac/In/1/Type': dummy, - '/Ac/In/2/Type': dummy, - '/Ac/In/1/L1/P': dummy, - '/Ac/In/1/L1/I': dummy, - '/Ac/In/2/L1/P': dummy, - '/Ac/In/2/L1/I': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L1/V': dummy, - '/Ac/Out/L1/I': dummy, - '/Ac/In/1/L2/P': dummy, - '/Ac/In/1/L2/I': dummy, - '/Ac/In/2/L2/P': dummy, - '/Ac/In/2/L2/I': dummy, - '/Ac/Out/L2/P': dummy, - '/Ac/Out/L2/V': dummy, - '/Ac/Out/L2/I': dummy, - '/Ac/In/1/L3/P': dummy, - '/Ac/In/1/L3/I': dummy, - '/Ac/In/2/L3/P': dummy, - '/Ac/In/2/L3/I': dummy, - '/Ac/Out/L3/P': dummy, - '/Ac/Out/L3/V': dummy, - '/Ac/Out/L3/I': dummy, -#### add for GuiMods - '/Ac/Out/L1/F': dummy, - - '/Yield/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.dcsystem': { - '/Dc/0/Voltage': dummy, - '/Dc/0/Power': dummy - }, - 'com.victronenergy.alternator': { - '/Dc/0/Power': dummy - }, -#### added for GuiMods - 'com.victronenergy.dcsource': { - '/Dc/0/Power': dummy, - '/Settings/MonitorMode': dummy - }, - 'com.victronenergy.motordrive': - { - '/Dc/0/Power': dummy - } - } - - self._modules = [ - delegates.Multi(), - delegates.HubTypeSelect(), - delegates.VebusSocWriter(), - delegates.ServiceMapper(), - delegates.RelayState(), - delegates.BuzzerControl(), - delegates.LgCircuitBreakerDetect(), - delegates.BatterySoc(self), - delegates.Dvcc(self), - delegates.BatterySense(self), - delegates.BatterySettings(self), - delegates.SystemState(self), - delegates.BatteryLife(), - delegates.ScheduledCharging(), - delegates.SourceTimers(), - delegates.BatteryData(), - delegates.Gps(), - delegates.AcInputs(), - delegates.GensetStartStop(), - delegates.SocSync(self), - delegates.PvInverters(), - delegates.BatteryService(self), - delegates.CanBatterySense(), - delegates.InverterCharger(), - delegates.DynamicEss(), - delegates.LoadShedding()] - - for m in self._modules: - for service, paths in m.get_input(): - s = dbus_tree.setdefault(service, {}) - for path in paths: - s[path] = dummy - - self._dbusmonitor = self._create_dbus_monitor(dbus_tree, valueChangedCallback=self._dbus_value_changed, - deviceAddedCallback=self._device_added, deviceRemovedCallback=self._device_removed) - - # Used to store AC output maximum values for the scenarios: - # No AC input connected - # Ac input 1 connected - # Ac input 2 connected - self._acMaxima = { - 'NoAcIn': 0, - 'AcIn1': 0, - 'AcIn2': 0 - } - - self._minMaxPaths = { - '/Ac/In/0/Current/Min': [float(0), -float("inf"), 0], - '/Ac/In/1/Current/Min': [float(0), -float("inf"), 0], - '/Ac/In/0/Current/Max': [float(0), 0, float("inf")], - '/Ac/In/1/Current/Max': [float(0), 0, float("inf")], - '/Dc/Input/Power/Max': [float(0), 0, float("inf")], - '/Dc/System/Power/Max': [float(0), 0, float("inf")], - '/Pv/Power/Max': [float(0), 0, float("inf")] - } - - for p in self._acMaxima.keys(): - self._minMaxPaths['/Ac/%s/Consumption/Current/Max' % p] = [float(0), 0, float("inf")] - - # Connect to localsettings - supported_settings = { - 'batteryservice': ['/Settings/SystemSetup/BatteryService', self.BATSERVICE_DEFAULT, 0, 0], - 'hasdcsystem': ['/Settings/SystemSetup/HasDcSystem', 0, 0, 1], - 'useacout': ['/Settings/SystemSetup/HasAcOutSystem', 1, 0, 1], - 'gaugeautomax': ['/Settings/Gui/Gauges/AutoMax', 1, 0, 1]} - - for p, s in self._minMaxPaths.items(): - supported_settings[p] = ['/Settings/Gui/Gauges' + p, s[0], s[1], s[2]] - - for m in self._modules: - for setting in m.get_settings(): - supported_settings[setting[0]] = list(setting[1:]) - - self._settings = self._create_settings(supported_settings, self._handlechangedsetting) - - self._dbusservice = self._create_dbus_service() - - for m in self._modules: - m.set_sources(self._dbusmonitor, self._settings, self._dbusservice) - - # At this moment, VRM portal ID is the MAC address of the CCGX. Anyhow, it should be string uniquely - # identifying the CCGX. - self._dbusservice.add_path('/Serial', value=get_vrm_portal_id()) - self._dbusservice.add_path( - '/AvailableBatteryServices', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/AvailableBatteryMeasurements', value=None) - self._dbusservice.add_path( - '/AutoSelectedBatteryService', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/AutoSelectedBatteryMeasurement', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/ActiveBatteryService', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/Dc/Battery/BatteryService', value=None) - self._summeditems = { - '/Ac/Grid/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L1/Current': {'gettext': '%.1F A'}, - '/Ac/Grid/L2/Current': {'gettext': '%.1F A'}, - '/Ac/Grid/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Grid/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Grid/ProductId': {'gettext': '%s'}, - '/Ac/Grid/DeviceType': {'gettext': '%s'}, - '/Ac/Genset/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L1/Current': {'gettext': '%.1F A'}, - '/Ac/Genset/L2/Current': {'gettext': '%.1F A'}, - '/Ac/Genset/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Genset/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Genset/ProductId': {'gettext': '%s'}, - '/Ac/Genset/DeviceType': {'gettext': '%s'}, - '/Ac/ConsumptionOnOutput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L1/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnOutput/L2/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnOutput/L3/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnInput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L1/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnInput/L2/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnInput/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Consumption/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L1/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/L2/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/NumberOfPhases': {'gettext': '%.0F W'}, - '/Dc/Pv/Power': {'gettext': '%.0F W'}, - '/Dc/Pv/Current': {'gettext': '%.1F A'}, - '/Dc/Battery/Voltage': {'gettext': '%.2F V'}, - '/Dc/Battery/VoltageService': {'gettext': '%s'}, - '/Dc/Battery/Current': {'gettext': '%.1F A'}, - '/Dc/Battery/Power': {'gettext': '%.0F W'}, - '/Dc/Battery/State': {'gettext': lambda v: ({ - self.STATE_IDLE: 'Idle', - self.STATE_CHARGING: 'Charging', - self.STATE_DISCHARGING: 'Discharging'}.get(v, 'Unknown'))}, - '/Dc/Battery/TimeToGo': {'gettext': '%.0F s'}, - '/Dc/Battery/ConsumedAmphours': {'gettext': '%.1F Ah'}, - '/Dc/Battery/ProductId': {'gettext': '0x%x'}, - '/Dc/Charger/Power': {'gettext': '%.0F %%'}, - '/Dc/FuelCell/Power': {'gettext': '%.0F %%'}, - '/Dc/Alternator/Power': {'gettext': '%.0F W'}, - '/Dc/Vebus/Current': {'gettext': '%.1F A'}, - '/Dc/Vebus/Power': {'gettext': '%.0F W'}, - '/Dc/System/Power': {'gettext': '%.0F W'}, - '/Dc/System/MeasurementType': {'gettext': '%d'}, - '/Ac/ActiveIn/Source': {'gettext': '%s'}, - '/Ac/ActiveIn/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/L3/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/L1/Current': {'gettext': '%.1F A'}, - '/Ac/ActiveIn/L2/Current': {'gettext': '%.1F A'}, - '/Ac/ActiveIn/L3/Current': {'gettext': '%.1F A'}, - '/Ac/ActiveIn/NumberOfPhases': {'gettext': '%d'}, - '/Ac/In/0/Current/Min': {'gettext': '%.1F'}, - '/Ac/In/0/Current/Max': {'gettext': '%.1F'}, - '/Ac/In/1/Current/Min': {'gettext': '%.1F'}, - '/Ac/In/1/Current/Max': {'gettext': '%.1F'}, - '/Ac/Consumption/Current/Max': {'gettext': '%.1F'}, - '/Pv/Power/Max': {'gettext': '%d'}, - '/Dc/Input/Power/Max': {'gettext': '%d'}, - '/Dc/System/Power/Max': {'gettext': '%d'}, -#### added for GuiMods - '/Dc/WindGenerator/Power': {'gettext': '%.0F W'}, - '/Dc/MotorDrive/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/Grid/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/Grid/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/Grid/Frequency': {'gettext': '%.1F Hz'}, - '/Ac/Genset/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/Genset/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/Genset/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/Genset/Frequency': {'gettext': '%.1F Hz'}, - '/Ac/ConsumptionOnOutput/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnOutput/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnOutput/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnOutput/Frequency': {'gettext': '%.1F Hz'}, - '/Ac/ConsumptionOnInput/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnInput/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnInput/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/ConsumptionOnInput/Frequency': {'gettext': '%.1F Hz'}, - '/Ac/Consumption/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/Consumption/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/Consumption/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/Consumption/Frequency': {'gettext': '%.1F Hz'}, - '/Ac/ActiveIn/L1/Voltage': {'gettext': '%.1F V'}, - '/Ac/ActiveIn/L2/Voltage': {'gettext': '%.1F V'}, - '/Ac/ActiveIn/L3/Voltage': {'gettext': '%.1F V'}, - '/Ac/ActiveIn/Frequency': {'gettext': '%.1F Hz'}, - } - - for m in self._modules: - self._summeditems.update(m.get_output()) - - for path in self._summeditems.keys(): - self._dbusservice.add_path(path, value=None, gettextcallback=self._gettext) - - self._batteryservice = None - self._determinebatteryservice() - - if self._batteryservice is None: - logger.info("Battery service initialized to None (setting == %s)" % - self._settings['batteryservice']) - - self._changed = True - for service, instance in self._dbusmonitor.get_service_list().items(): - self._device_added(service, instance, do_service_change=False) - -#### added for GuiMods - self.dcSystemPower = [0, 0, 0] - - self._handleservicechange() - self._updatevalues() - - GLib.timeout_add(1000, exit_on_error, self._handletimertick) - - 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): - raise Exception("This function should be overridden") - - def _handlechangedsetting(self, setting, oldvalue, newvalue): - self._determinebatteryservice() - self._changed = True - - # Give our delegates a chance to react on a settings change - for m in self._modules: - m.settings_changed(setting, oldvalue, newvalue) - - def _find_device_instance(self, serviceclass, instance): - """ Gets a mapping of services vs DeviceInstance using - get_service_list. Then searches for the specified DeviceInstance - and returns the service name. """ - services = self._dbusmonitor.get_service_list(classfilter=serviceclass) - - for k, v in services.items(): - if v == instance: - return k - return None - - def _determinebatteryservice(self): - auto_battery_service = self._autoselect_battery_service() - auto_battery_measurement = None - auto_selected = False - if auto_battery_service is not None: - services = self._dbusmonitor.get_service_list() - if auto_battery_service in services: - auto_battery_measurement = \ - self._get_instance_service_name(auto_battery_service, services[auto_battery_service]) - auto_battery_measurement = auto_battery_measurement.replace('.', '_').replace('/', '_') + '/Dc/0' - self._dbusservice['/AutoSelectedBatteryMeasurement'] = auto_battery_measurement - - if self._settings['batteryservice'] == self.BATSERVICE_DEFAULT: - auto_selected = True - newbatteryservice = auto_battery_service - self._dbusservice['/AutoSelectedBatteryService'] = ( - 'No battery monitor found' if newbatteryservice is None else - self._get_readable_service_name(newbatteryservice)) - - elif self._settings['batteryservice'] == self.BATSERVICE_NOBATTERY: - self._dbusservice['/AutoSelectedBatteryService'] = None - newbatteryservice = None - - else: - self._dbusservice['/AutoSelectedBatteryService'] = None - - s = self._settings['batteryservice'].split('/') - if len(s) != 2: - logger.error("The battery setting (%s) is invalid!" % self._settings['batteryservice']) - serviceclass = s[0] - instance = int(s[1]) if len(s) == 2 else None - - # newbatteryservice might turn into None if a chosen battery - # monitor no longer exists. Don't auto change the setting (it might - # come back) and don't autoselect another. - newbatteryservice = self._find_device_instance(serviceclass, instance) - - if newbatteryservice != self._batteryservice: - services = self._dbusmonitor.get_service_list() - instance = services.get(newbatteryservice, None) - if instance is None: - battery_service = None - else: - battery_service = self._get_instance_service_name(newbatteryservice, instance) - self._dbusservice['/ActiveBatteryService'] = battery_service - logger.info("Battery service, setting == %s, changed from %s to %s (%s)" % - (self._settings['batteryservice'], self._batteryservice, newbatteryservice, instance)) - - # Battery service has changed. Notify delegates. - self._dbusservice['/Dc/Battery/BatteryService'] = self._batteryservice = newbatteryservice - for m in self._modules: - m.battery_service_changed(auto_selected, self._batteryservice, newbatteryservice) - - def _autoselect_battery_service(self): - # Default setting business logic: - # first try to use a battery service (BMV or Lynx Shunt VE.Can). If there - # is more than one battery service, just use a random one. If no battery service is - # available, check if there are not Solar chargers and no normal chargers. If they are not - # there, assume this is a hub-2, hub-3 or hub-4 system and use VE.Bus SOC. - batteries = self._get_connected_service_list('com.victronenergy.battery') - - # Pick the battery service that has the lowest DeviceInstance, giving - # preference to those with a BMS. Instances of 'lynxparallel' are preferred over regular BMSes. - if len(batteries) > 0: - batteries = [ - (not self._dbusmonitor.seen(s, '/Info/MaxChargeVoltage'), - not s.startswith('com.victronenergy.battery.lynxparallel'), i, s) - for s, i in batteries.items()] - return sorted(batteries, key=lambda x: x[:3])[0][3] - - # No battery services, and there is a charger in the system. Abandon - # hope. - if self._get_first_connected_service('com.victronenergy.charger') is not None: - return None - - # Also no Multi, then give up. - vebus_service = self._get_service_having_lowest_instance('com.victronenergy.vebus') - if vebus_service is None: - # No VE.Bus, but maybe there is an inverter with built-in SOC - # tracking, eg RS Smart or Multi RS. - inverter = self._get_service_having_lowest_instance('com.victronenergy.multi') - if inverter and self._dbusmonitor.get_value(inverter[0], '/Soc') is not None: - return inverter[0] - - inverter = self._get_service_having_lowest_instance('com.victronenergy.inverter') - if inverter and self._dbusmonitor.get_value(inverter[0], '/Soc') is not None: - return inverter[0] - - return None - - # There is a Multi, it supports tracking external charge current from - # solarchargers, and there are no DC loads. Then use it. - if self._dbusmonitor.get_value( - vebus_service[0], '/ExtraBatteryCurrent') is not None \ - and self._get_first_connected_service('com.victronenergy.dcsystem') is None \ - and self._settings['hasdcsystem'] == 0: - return vebus_service[0] - - # Multi does not support tracking solarcharger current, and we have - # solar chargers. Then we cannot use it. - if self._get_first_connected_service('com.victronenergy.solarcharger') is not None: - return None - - # Only a Multi, no other chargers. Then we can use it. - return vebus_service[0] - - @property - def batteryservice(self): - return self._batteryservice - - # Called on a one second timer - def _handletimertick(self): - if self._changed: - self._updatevalues() - self._changed = False - - return True # keep timer running - - def _updatevalues(self): - # ==== PREPARATIONS ==== - newvalues = {} - - # Set the user timezone - if 'TZ' not in os.environ: - tz = self._dbusmonitor.get_value('com.victronenergy.settings', '/Settings/System/TimeZone') - if tz is not None: - os.environ['TZ'] = tz - time.tzset() - - # Determine values used in logic below - vebusses = self._dbusmonitor.get_service_list('com.victronenergy.vebus') - vebuspower = 0 - for vebus in vebusses: - v = self._dbusmonitor.get_value(vebus, '/Dc/0/Voltage') - i = self._dbusmonitor.get_value(vebus, '/Dc/0/Current') - if v is not None and i is not None: - vebuspower += v * i - - # ==== PVINVERTERS ==== - # Work is done in pv-inverter delegate. Ideally all of this should - # happen in update_values in the delegate, but these values are - # used below in calculating consumption, so until this is less - # unwieldy this has to stay here. - # TODO this can go away once consumption below no longer relies - # on these values, or has moved to its own delegate. - newvalues.update(delegates.PvInverters.instance.get_totals()) - self._compute_number_of_phases('/Ac/PvOnGrid', newvalues) - self._compute_number_of_phases('/Ac/PvOnOutput', newvalues) - self._compute_number_of_phases('/Ac/PvOnGenset', newvalues) - - # ==== SOLARCHARGERS ==== - solarchargers = self._dbusmonitor.get_service_list('com.victronenergy.solarcharger') - solarcharger_batteryvoltage = None - solarcharger_batteryvoltage_service = None - solarchargers_charge_power = 0 - solarchargers_loadoutput_power = None - - for solarcharger in solarchargers: - v = self._dbusmonitor.get_value(solarcharger, '/Dc/0/Voltage') - if v is None: - continue - i = self._dbusmonitor.get_value(solarcharger, '/Dc/0/Current') - if i is None: - continue - l = self._dbusmonitor.get_value(solarcharger, '/Load/I', 0) - - if l is not None: - if solarchargers_loadoutput_power is None: - solarchargers_loadoutput_power = l * v - else: - solarchargers_loadoutput_power += l * v - - solarchargers_charge_power += v * i - - # Note that this path is not in the _summeditems{}, making for it to not be - # published on D-Bus. Which fine. The only one needing it is the vebussocwriter- - # delegate. - if '/Dc/Pv/ChargeCurrent' not in newvalues: - newvalues['/Dc/Pv/ChargeCurrent'] = i - else: - newvalues['/Dc/Pv/ChargeCurrent'] += i - - if '/Dc/Pv/Power' not in newvalues: - newvalues['/Dc/Pv/Power'] = v * _safeadd(i, l) - newvalues['/Dc/Pv/Current'] = _safeadd(i, l) - solarcharger_batteryvoltage = v - solarcharger_batteryvoltage_service = solarcharger - else: - newvalues['/Dc/Pv/Power'] += v * _safeadd(i, l) - newvalues['/Dc/Pv/Current'] += _safeadd(i, l) - - # ==== FUELCELLS ==== - fuelcells = self._dbusmonitor.get_service_list('com.victronenergy.fuelcell') - fuelcell_batteryvoltage = None - fuelcell_batteryvoltage_service = None - for fuelcell in fuelcells: - # Assume the battery connected to output 0 is the main battery - v = self._dbusmonitor.get_value(fuelcell, '/Dc/0/Voltage') - if v is None: - continue - - fuelcell_batteryvoltage = v - fuelcell_batteryvoltage_service = fuelcell - - i = self._dbusmonitor.get_value(fuelcell, '/Dc/0/Current') - if i is None: - continue - - if '/Dc/FuelCell/Power' not in newvalues: - newvalues['/Dc/FuelCell/Power'] = v * i - else: - newvalues['/Dc/FuelCell/Power'] += v * i - - # ==== ALTERNATOR ==== - alternators = self._dbusmonitor.get_service_list('com.victronenergy.alternator') - for alternator in alternators: -#### modified for GuiMods - # some alternators do not provide a valid power value if not running - # or below a minimum power/current - # so fill in a zero power so that the systemcalc power becomes valid - # Assume the battery connected to output 0 is the main battery - p = self._dbusmonitor.get_value(alternator, '/Dc/0/Power') - if p is None: - continue - p = 0 - - if '/Dc/Alternator/Power' not in newvalues: - newvalues['/Dc/Alternator/Power'] = p - else: - newvalues['/Dc/Alternator/Power'] += p - - -#### added for GuiMods - # ==== MOTOR DRIVE ==== - motordrives = self._dbusmonitor.get_service_list('com.victronenergy.motordrive') - for motordrive in motordrives: - p = self._dbusmonitor.get_value(motordrive, '/Dc/0/Power') - if p is None: - p = 0 - - if '/Dc/MotorDrive/Power' not in newvalues: - newvalues['/Dc/MotorDrive/Power'] = p - else: - newvalues['/Dc/MotorDrive/Power'] += p - -#### added for GuiMods - # ==== DC SOURCES ==== - dcSources = self._dbusmonitor.get_service_list('com.victronenergy.dcsource') - for dcSource in dcSources: - monitorMode = self._dbusmonitor.get_value(dcSource,'/Settings/MonitorMode') - # ==== WIND GENERATOR ==== - if monitorMode == -8: - p = self._dbusmonitor.get_value(dcSource, '/Dc/0/Power') - if p is None: - continue - if '/Dc/WindGenerator/Power' not in newvalues: - newvalues['/Dc/WindGenerator/Power'] = p - else: - newvalues['/Dc/WindGenerator/Power'] += p - - # ==== CHARGERS ==== - chargers = self._dbusmonitor.get_service_list('com.victronenergy.charger') - charger_batteryvoltage = None - charger_batteryvoltage_service = None - for charger in chargers: - # Assume the battery connected to output 0 is the main battery - v = self._dbusmonitor.get_value(charger, '/Dc/0/Voltage') - if v is None: - continue - - charger_batteryvoltage = v - charger_batteryvoltage_service = charger - - i = self._dbusmonitor.get_value(charger, '/Dc/0/Current') - if i is None: - continue - - if '/Dc/Charger/Power' not in newvalues: - newvalues['/Dc/Charger/Power'] = v * i - else: - newvalues['/Dc/Charger/Power'] += v * i - - # ==== Other Inverters and Inverter/Chargers ==== - _other_inverters = sorted((di, s) for s, di in self._dbusmonitor.get_service_list('com.victronenergy.multi').items()) + \ - sorted((di, s) for s, di in self._dbusmonitor.get_service_list('com.victronenergy.inverter').items()) - non_vebus_inverters = [x[1] for x in _other_inverters] - non_vebus_inverter = None - if non_vebus_inverters: - non_vebus_inverter = non_vebus_inverters[0] - - # For RS Smart and Multi RS, add PV to the yield - for i in non_vebus_inverters: - if (pv_yield := self._dbusmonitor.get_value(i, "/Yield/Power")) is not None: - newvalues['/Dc/Pv/Power'] = newvalues.get('/Dc/Pv/Power', 0) + pv_yield - - # Used lower down, possibly needed for battery values as well - dcsystems = self._dbusmonitor.get_service_list('com.victronenergy.dcsystem') - - # ==== BATTERY ==== - if self._batteryservice is not None: - batteryservicetype = self._batteryservice.split('.')[2] - assert batteryservicetype in ('battery', 'vebus', 'inverter', 'multi') - - newvalues['/Dc/Battery/TimeToGo'] = self._dbusmonitor.get_value(self._batteryservice,'/TimeToGo') - newvalues['/Dc/Battery/ConsumedAmphours'] = self._dbusmonitor.get_value(self._batteryservice,'/ConsumedAmphours') - newvalues['/Dc/Battery/ProductId'] = self._dbusmonitor.get_value(self._batteryservice, '/ProductId') - - if batteryservicetype in ('battery', 'inverter', 'multi'): - newvalues['/Dc/Battery/Voltage'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Voltage') - newvalues['/Dc/Battery/VoltageService'] = self._batteryservice - newvalues['/Dc/Battery/Current'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Current') - newvalues['/Dc/Battery/Power'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Power') - - elif batteryservicetype == 'vebus': - vebus_voltage = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Voltage') - vebus_current = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Current') - vebus_power = None if vebus_voltage is None or vebus_current is None else vebus_current * vebus_voltage - newvalues['/Dc/Battery/Voltage'] = vebus_voltage - newvalues['/Dc/Battery/VoltageService'] = self._batteryservice - if self._settings['hasdcsystem'] == 1 or dcsystems: - # hasdcsystem will normally disqualify the multi from being - # auto-selected as battery monitor, so the only way we're - # here is if the user explicitly selected the multi as the - # battery service - newvalues['/Dc/Battery/Current'] = vebus_current - if vebus_power is not None: - newvalues['/Dc/Battery/Power'] = vebus_power - else: - battery_power = _safeadd(solarchargers_charge_power, vebus_power) - newvalues['/Dc/Battery/Current'] = battery_power / vebus_voltage if vebus_voltage is not None and vebus_voltage > 0 else None - newvalues['/Dc/Battery/Power'] = battery_power - - - p = newvalues.get('/Dc/Battery/Power', None) - if p is not None: - if p > 30: - newvalues['/Dc/Battery/State'] = self.STATE_CHARGING - elif p < -30: - newvalues['/Dc/Battery/State'] = self.STATE_DISCHARGING - else: - newvalues['/Dc/Battery/State'] = self.STATE_IDLE - - else: - # The battery service is not a BMS/BMV or a suitable vebus. A - # suitable vebus is defined as one explicitly selected by the user, - # or one that was automatically selected for SOC tracking. We may - # however still have a VE.Bus, just not one that can accurately - # track SOC. If we have one, use it as voltage source. Otherwise - # try a solar charger, a charger, a vedirect inverter or a dcsource - # as fallbacks. - batteryservicetype = None - vebusses = self._dbusmonitor.get_service_list('com.victronenergy.vebus') - for vebus in vebusses: - v = self._dbusmonitor.get_value(vebus, '/Dc/0/Voltage') - s = self._dbusmonitor.get_value(vebus, '/State') - if v is not None and s not in (0, None): - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = vebus - break # Skip the else below - else: - # No suitable vebus voltage, try other devices - if non_vebus_inverter is not None and (v := self._dbusmonitor.get_value(non_vebus_inverter, '/Dc/0/Voltage')) is not None: - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = non_vebus_inverter - elif solarcharger_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = solarcharger_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = solarcharger_batteryvoltage_service - elif charger_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = charger_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = charger_batteryvoltage_service - elif fuelcell_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = fuelcell_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = fuelcell_batteryvoltage_service - elif dcsystems: - # Get voltage from first dcsystem - s = next(iter(dcsystems.keys())) - v = self._dbusmonitor.get_value(s, '/Dc/0/Voltage') - if v is not None: - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = s - - # We have no suitable battery monitor, so power and current data - # is not available. We can however calculate it from other values, - # if we have at least a battery voltage. - if '/Dc/Battery/Voltage' in newvalues: - dcsystempower = _safeadd(0, *(self._dbusmonitor.get_value(s, - '/Dc/0/Power', 0) for s in dcsystems)) - if dcsystems or self._settings['hasdcsystem'] == 0: - # Either DC loads are monitored, or there are no - # unmonitored DC loads or chargers: derive battery watts - # and amps from vebus, solarchargers, chargers and measured - # loads. - p = solarchargers_charge_power + newvalues.get('/Dc/Charger/Power', 0) + vebuspower - dcsystempower - voltage = newvalues['/Dc/Battery/Voltage'] - newvalues['/Dc/Battery/Current'] = p / voltage if voltage > 0 else None - newvalues['/Dc/Battery/Power'] = p - - # ==== SYSTEM POWER ==== - # Look for dcsytem devices, add them together. Otherwise, if enabled, - # calculate it - if dcsystems: - newvalues['/Dc/System/MeasurementType'] = 1 # measured - newvalues['/Dc/System/Power'] = 0 - for meter in dcsystems: - newvalues['/Dc/System/Power'] = _safeadd(newvalues['/Dc/System/Power'], - self._dbusmonitor.get_value(meter, '/Dc/0/Power')) - elif self._settings['hasdcsystem'] == 1 and batteryservicetype == 'battery': - # Calculate power being generated/consumed by not measured devices in the network. - # For MPPTs, take all the power, including power going out of the load output. - # /Dc/System: positive: consuming power - # VE.Bus: Positive: current flowing from the Multi to the dc system or battery - # Solarcharger & other chargers: positive: charging - # battery: Positive: charging battery. - # battery = solarcharger + charger + ve.bus - system - - battery_power = newvalues.get('/Dc/Battery/Power') - if battery_power is not None: - dc_pv_power = newvalues.get('/Dc/Pv/Power', 0) - charger_power = newvalues.get('/Dc/Charger/Power', 0) - fuelcell_power = newvalues.get('/Dc/FuelCell/Power', 0) - alternator_power = newvalues.get('/Dc/Alternator/Power', 0) -#### added for GuiMods - windgen_power = newvalues.get('/Dc/WindGenerator/Power', 0) - motordrive_power = newvalues.get('/Dc/MotorDrive/Power', 0) - - # If there are VE.Direct inverters, remove their power from the - # DC estimate. This is done using the AC value when the DC - # power values are not available. - inverter_power = 0 - for i in non_vebus_inverters: - inverter_current = self._dbusmonitor.get_value(i, '/Dc/0/Current') - if inverter_current is not None: - inverter_power += self._dbusmonitor.get_value( - i, '/Dc/0/Voltage', 0) * inverter_current - else: - inverter_power -= self._dbusmonitor.get_value( - i, '/Ac/Out/L1/V', 0) * self._dbusmonitor.get_value( - i, '/Ac/Out/L1/I', 0) - newvalues['/Dc/System/MeasurementType'] = 0 # estimated - # FIXME In future we will subtract alternator power from the - # calculated DC power, because it will be individually - # displayed. For now, we leave it out so that in the current - # version of Venus it does not break user's expectations. - #newvalues['/Dc/System/Power'] = dc_pv_power + charger_power + fuelcell_power + vebuspower + inverter_power - battery_power - alternator_power -#### changed for GuiMods - # average DC system power over 3 passes (seconds) to minimize wild swings in displayed value - self.dcSystemPower[2] = self.dcSystemPower[1] - self.dcSystemPower[1] = self.dcSystemPower[0] - self.dcSystemPower[0] = dc_pv_power + charger_power + fuelcell_power + vebuspower + inverter_power - battery_power + alternator_power + windgen_power - motordrive_power - newvalues['/Dc/System/Power'] = (self.dcSystemPower[0] + self.dcSystemPower[1] + self.dcSystemPower[2]) / 3 - - elif self._settings['hasdcsystem'] == 1 and solarchargers_loadoutput_power is not None: - newvalues['/Dc/System/MeasurementType'] = 0 # estimated - newvalues['/Dc/System/Power'] = solarchargers_loadoutput_power - - # ===== AC IN SOURCE ===== - multi_path = getattr(delegates.Multi.instance.multi, 'service', None) - ac_in_source = None - active_input = None - if multi_path is None: - # Check if we have an non-VE.Bus inverter. - if non_vebus_inverter is not None: - if (active_input := self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/ActiveIn/ActiveInput')) is not None and \ - active_input in (0, 1) and \ - (active_type := self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/In/{}/Type'.format(active_input + 1))) is not None: - ac_in_source = active_type - else: - ac_in_source = 240 - else: - active_input = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/ActiveInput') - if active_input == 0xF0: - # Not connected - ac_in_source = 240 - elif active_input is not None: - settings_path = '/Settings/SystemSetup/AcInput%s' % (active_input + 1) - ac_in_source = self._dbusmonitor.get_value('com.victronenergy.settings', settings_path) - newvalues['/Ac/ActiveIn/Source'] = ac_in_source - - # ===== GRID METERS & CONSUMPTION ==== - grid_meter = delegates.AcInputs.instance.gridmeter - genset_meter = delegates.AcInputs.instance.gensetmeter - - # Make an educated guess as to what is being consumed from an AC source. If ac_in_source - # indicates grid, genset or shore, we use that. If the Multi is off, or disconnected through - # a relay assistant or otherwise, then assume the presence of a .grid or .genset service indicates - # presence of that AC source. If both are available, then give up. This decision making is here - # so the GUI has something to present even if the Multi is off. - ac_in_guess = ac_in_source - if ac_in_guess in (None, 0xF0): - if genset_meter is None and grid_meter is not None: - ac_in_guess = 1 - elif grid_meter is None and genset_meter is not None: - ac_in_guess = 2 - - consumption = { "L1" : None, "L2" : None, "L3" : None } - currentconsumption = { "L1" : None, "L2" : None, "L3" : None } - -#### added for GuiMods - voltageIn = { "L1" : None, "L2" : None, "L3" : None } - voltageOut = { "L1" : None, "L2" : None, "L3" : None } - frequencyIn = None - frequencyOut = None - - for device_type, em, _types in (('Grid', grid_meter, (1, 3)), ('Genset', genset_meter, (2,))): - # If a grid meter is present we use values from it. If not, we look at the multi. If it has - # AcIn1 or AcIn2 connected to the grid, we use those values. - # com.victronenergy.grid.??? indicates presence of an energy meter used as grid meter. - # com.victronenergy.vebus.???/Ac/ActiveIn/ActiveInput: decides which whether we look at AcIn1 - # or AcIn2 as possible grid connection. - uses_active_input = ac_in_source in _types - for phase in consumption: - p = None - mc = None - pvpower = newvalues.get('/Ac/PvOn%s/%s/Power' % (device_type, phase)) - pvcurrent = newvalues.get('/Ac/PvOn%s/%s/Current' % (device_type, phase)) - if em is not None: - p = self._dbusmonitor.get_value(em.service, '/Ac/%s/Power' % phase) - mc = self._dbusmonitor.get_value(em.service, '/Ac/%s/Current' % phase) -#### added for GuiMods - if voltageIn[phase] == None: - voltageIn[phase] = self._dbusmonitor.get_value(em.service, '/Ac/%s/Voltage' % phase) - if frequencyIn == None: - frequencyIn = self._dbusmonitor.get_value(em.service, '/Ac/%s/Frequency' % phase) - - # Compute consumption between energy meter and multi (meter power - multi AC in) and - # add an optional PV inverter on input to the mix. - c = None - cc = None - if uses_active_input: - if multi_path is not None: - try: - c = _safeadd(c, -self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/P' % phase)) - cc = _safeadd(cc, -self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/I' % phase)) -#### added for GuiMods - if voltageIn[phase] == None: - voltageIn[phase] = self._dbusmonitor.get_value(em.service, '/Ac/ActiveIn/%s/V' % phase) - if frequencyIn == None: - frequencyIn = self._dbusmonitor.get_value(em.service, '/Ac/ActiveIn/%s/F' % phase) - - except TypeError: - pass - elif non_vebus_inverter is not None and active_input in (0, 1): - for i in non_vebus_inverters: - try: - c = _safeadd(c, -self._dbusmonitor.get_value(i, '/Ac/In/%d/%s/P' % (active_input+1, phase))) - cc = _safeadd(cc, -self._dbusmonitor.get_value(i, '/Ac/In/%d/%s/I' % (active_input+1, phase))) -#### added for GuiMods - if voltageIn[phase] == None: - voltageIn[phase] = self._dbusmonitor.get_value(i, '/Ac/In/%d/%s/V' % (active_input+1, phase)) - if frequencyIn == None: - frequencyIn = self._dbusmonitor.get_value(i, '/Ac/In/%d/%s/F' % (active_input+1, phase)) - - except TypeError: - pass - - # If there's any power coming from a PV inverter in the inactive AC in (which is unlikely), - # it will still be used, because there may also be a load in the same ACIn consuming - # power, or the power could be fed back to the net. - c = _safeadd(c, p, pvpower) - cc = _safeadd(cc, mc, pvcurrent) - consumption[phase] = _safeadd(consumption[phase], _safemax(0, c)) - currentconsumption[phase] = _safeadd(currentconsumption[phase], _safemax(0, cc)) - else: - if uses_active_input: - if multi_path is not None and ( - p := self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/P' % phase)) is not None: - consumption[phase] = _safeadd(0, consumption[phase]) - currentconsumption[phase] = _safeadd(0, currentconsumption[phase]) - mc = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/I' % phase) -#### added for GuiMods - if voltageIn[phase] == None: - voltageIn[phase] = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/V' % phase) - if frequencyIn == None: - freq = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/F' % phase) - if freq != None: - frequencyIn = freq - - elif non_vebus_inverter is not None and active_input in (0, 1): - for i in non_vebus_inverters: - p = _safeadd(p, - self._dbusmonitor.get_value(i, '/Ac/In/%d/%s/P' % (active_input + 1, phase))) - mc = _safeadd(mc, - self._dbusmonitor.get_value(i, '/Ac/In/%d/%s/I' % (active_input + 1, phase))) -#### added for GuiMods - if voltageIn[phase] == None: - voltageIn[phase] = self._dbusmonitor.get_value(i, '/Ac/In/%d/%s/V' % (active_input + 1, phase)) - if frequencyIn == None: - frequencyIn = self._dbusmonitor.get_value(i, '/Ac/In/%d/%s/F' % (active_input + 1, phase)) - - if p is not None: - consumption[phase] = _safeadd(0, consumption[phase]) - currentconsumption[phase] = _safeadd(0, currentconsumption[phase]) - - # No relevant energy meter present. Assume there is no load between the grid and the multi. - # There may be a PV inverter present though (Hub-3 setup). - try: - p = _safeadd(p, -pvpower) - mc = _safeadd(mc, -pvcurrent) - except TypeError: - pass - - newvalues['/Ac/%s/%s/Power' % (device_type, phase)] = p - newvalues['/Ac/%s/%s/Current' % (device_type, phase)] = mc -#### added for GuiMods - if p != None: - newvalues['/Ac/%s/%s/Voltage' % (device_type, phase)] = voltageIn[phase] - newvalues['/Ac/%s/Frequency' % (device_type)] = frequencyIn - - if ac_in_guess in _types: - newvalues['/Ac/ActiveIn/%s/Power' % (phase,)] = p - newvalues['/Ac/ActiveIn/%s/Current' % (phase,)] = mc -#### added for GuiMods - if p != None: - newvalues['/Ac/ActiveIn/%s/Voltage' % (phase,)] = voltageIn[phase] - newvalues['/Ac/ActiveIn/Frequency'] = frequencyIn - - self._compute_number_of_phases('/Ac/%s' % device_type, newvalues) - self._compute_number_of_phases('/Ac/ActiveIn', newvalues) - - product_id = None - device_type_id = None - if em is not None: - product_id = em.product_id - device_type_id = em.device_type - if product_id is None and uses_active_input: - if multi_path is not None: - product_id = self._dbusmonitor.get_value(multi_path, '/ProductId') - elif non_vebus_inverter is not None: - product_id = self._dbusmonitor.get_value(non_vebus_inverter, '/ProductId') - newvalues['/Ac/%s/ProductId' % device_type] = product_id - newvalues['/Ac/%s/DeviceType' % device_type] = device_type_id - - # If we have an ESS system and RunWithoutGridMeter is set, there cannot be load on the AC-In, so it - # must be on AC-Out. Hence we do calculate AC-Out consumption even if 'useacout' is disabled. - # Similarly all load are by definition on the output if this is not an ESS system. - use_ac_out = \ - self._settings['useacout'] == 1 or \ - (multi_path is not None and self._dbusmonitor.get_value(multi_path, '/Hub4/AssistantId') not in (4, 5)) or \ - self._dbusmonitor.get_value('com.victronenergy.settings', '/Settings/CGwacs/RunWithoutGridMeter') == 1 - for phase in consumption: - c = None - a = None - if use_ac_out: - c = newvalues.get('/Ac/PvOnOutput/%s/Power' % phase) - a = newvalues.get('/Ac/PvOnOutput/%s/Current' % phase) -#### added for GuiMods - if voltageOut[phase] == None: - voltageOut[phase] = newvalues.get('/Ac/PvOnOutput/%s/Voltage' % phase) - if frequencyOut == None: - frequencyOut = newvalues.get('/Ac/PvOnOutput/%s/Frequency' % phase) - - if multi_path is None: - for inv in non_vebus_inverters: - ac_out = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/P' % phase) - i = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/I' % phase) -#### added for GuiMods - if voltageOut[phase] == None: - voltageOut[phase] = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/V' % phase) - if frequencyOut == None: - frequencyOut = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/F' % phase) - - # Some models don't show power, try apparent power, - # else calculate it - if ac_out is None: - ac_out = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/S' % phase) - if ac_out is None: -#### modified for GuiMods - u = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/V' % phase) - if None not in (i, u): - ac_out = i * u - c = _safeadd(c, ac_out) - a = _safeadd(a, i) - else: - ac_out = self._dbusmonitor.get_value(multi_path, '/Ac/Out/%s/P' % phase) - c = _safeadd(c, ac_out) - i_out = self._dbusmonitor.get_value(multi_path, '/Ac/Out/%s/I' % phase) - a = _safeadd(a, i_out) -#### added for GuiMods - if voltageOut[phase] == None: - voltageOut[phase] = self._dbusmonitor.get_value(multi_path, '/Ac/Out/%s/V' % phase) - if frequencyOut == None: - frequencyOut = self._dbusmonitor.get_value(multi_path, '/Ac/Out/%s/F' % phase) - c = _safemax(0, c) - a = _safemax(0, a) - newvalues['/Ac/ConsumptionOnOutput/%s/Power' % phase] = c - newvalues['/Ac/ConsumptionOnOutput/%s/Current' % phase] = a - newvalues['/Ac/ConsumptionOnInput/%s/Power' % phase] = consumption[phase] - newvalues['/Ac/ConsumptionOnInput/%s/Current' % phase] = currentconsumption[phase] - newvalues['/Ac/Consumption/%s/Power' % phase] = _safeadd(consumption[phase], c) - newvalues['/Ac/Consumption/%s/Current' % phase] = _safeadd(currentconsumption[phase], a) -#### added for GuiMods - newvalues['/Ac/ConsumptionOnOutput/%s/Voltage' % phase] = voltageOut[phase] - newvalues['/Ac/ConsumptionOnInput/%s/Voltage' % phase] = voltageIn[phase] - if voltageOut[phase] != None: - newvalues['/Ac/Consumption/%s/Voltage' % phase] = voltageOut[phase] - elif voltageIn[phase] != None: - newvalues['/Ac/Consumption/%s/Voltage' % phase] = voltageIn[phase] - if frequencyIn != None: - newvalues['/Ac/ConsumptionOnInput/Frequency'] = frequencyIn - if frequencyOut != None: - newvalues['/Ac/ConsumptionOnOutput/Frequency'] = frequencyOut - if frequencyOut != None: - newvalues['/Ac/Consumption/Frequency'] = frequencyOut - elif frequencyIn != None: - newvalues['/Ac/Consumption/Frequency'] = frequencyIn - - self._compute_number_of_phases('/Ac/Consumption', newvalues) - self._compute_number_of_phases('/Ac/ConsumptionOnOutput', newvalues) - self._compute_number_of_phases('/Ac/ConsumptionOnInput', newvalues) - - for m in self._modules: - m.update_values(newvalues) - - # ==== UPDATE MINIMUM AND MAXIMUM LEVELS ==== - # min/max values are stored in localsettings and synched once in a while. - # values are stored under /Settings/Gui/Briefview - # /Settings/Gui/Gauges/AutoMax: - # 1-> Automatic: Maxima are updated automatically and synched to localsettings - # 0-> Manual: Maxima are pulled from localsettings. - # min/max computations are done here because the _updatevalues method abstracts them - # away from the delegates. - - # AC output - # This maximum is maintained for 3 situations: - # 1: AC input 1 is connected - # 2: AC input 2 is connected - # 3: No AC input is connected - # All 3 scenarios may lead to different maximum values since the capabilities of the system changes. - # So 3 different maxima are stored and relayed to /Ac/Consumption/Current/Max based on the active scenario. - activeIn = 'AcIn1' if (self._dbusservice['/Ac/In/0/Connected'] == 1) else \ - 'AcIn2' if (self._dbusservice['/Ac/In/1/Connected'] == 1) else \ - 'NoAcIn' - - # Quattro has 2 AC inputs which cannot be active simultaneously. - # activeIn needs to 1 when 'Ac/In/1/Connected' is 1 and can be 0 otherwise. - if (self._settings['gaugeautomax']): - activeInNr = int(activeIn[-1]) -1 if activeIn != 'NoAcIn' else None - - # AC input - # Minimum values occur when feeding back to the grid. - # For the minimum value, make sure it is 0 at its maximum. - # Update correct '/Ac/In/..' based on the current active input. - # When no inputs are active, paths '/Ac/In/[0/1]/Current/[Min/Max] will all be invalidated. - if(activeInNr != None): - newvalues['/Ac/In/%s/Current/Min' % activeInNr] = min(0, - self._dbusservice['/Ac/In/%s/Current/Min' % activeInNr] or float("inf"), - newvalues.get('/Ac/ActiveIn/L1/Current') or float("inf"), - newvalues.get('/Ac/ActiveIn/L2/Current') or float("inf"), - newvalues.get('/Ac/ActiveIn/L3/Current') or float("inf")) - - newvalues['/Ac/In/%s/Current/Max' % activeInNr] = max(self._dbusservice['/Ac/In/%s/Current/Min' % activeInNr] or 0, - newvalues.get('/Ac/ActiveIn/L1/Current') or 0, - newvalues.get('/Ac/ActiveIn/L2/Current') or 0, - newvalues.get('/Ac/ActiveIn/L3/Current') or 0) - - self._acMaxima[activeIn] = max(self._acMaxima[activeIn], - newvalues.get('/Ac/Consumption/L1/Current') or 0, - newvalues.get('/Ac/Consumption/L2/Current') or 0, - newvalues.get('/Ac/Consumption/L3/Current') or 0) - - newvalues['/Ac/Consumption/Current/Max'] = self._acMaxima[activeIn] - - # DC input - newvalues['/Dc/Input/Power/Max'] = max(self._dbusservice['/Dc/Input/Power/Max'] or 0, - sum([newvalues.get('/Dc/Charger/Power') or 0, - newvalues.get('/Dc/FuelCell/Power') or 0, - newvalues.get('/Dc/Alternator/Power') or 0])) - - # DC output - newvalues['/Dc/System/Power/Max'] = _safemax(self._dbusservice['/Dc/System/Power/Max'] or 0, - newvalues.get('/Dc/System/Power') or 0) - - # PV power - newvalues['/Pv/Power/Max'] = _safemax(self._dbusservice['/Pv/Power/Max'] or 0, - _safeadd(newvalues.get('/Dc/Pv/Power') or 0, - self._dbusservice['/Ac/PvOnGrid/L1/Power'], - self._dbusservice['/Ac/PvOnGrid/L2/Power'], - self._dbusservice['/Ac/PvOnGrid/L3/Power'], - self._dbusservice['/Ac/PvOnGenset/L1/Power'], - self._dbusservice['/Ac/PvOnGenset/L2/Power'], - self._dbusservice['/Ac/PvOnGenset/L3/Power'], - self._dbusservice['/Ac/PvOnOutput/L1/Power'], - self._dbusservice['/Ac/PvOnOutput/L2/Power'], - self._dbusservice['/Ac/PvOnOutput/L3/Power'])) - - # Sync max values to localsettings (once each second) - for p in self._minMaxPaths.keys(): - if (p in newvalues and newvalues[p] != self._settings[p]): - self._settings[p] = newvalues[p] - - # Store the ac maxima values for the 3 different scenarios. These aren't in newvalues. - if(self._acMaxima[activeIn] != self._settings['/Ac/%s/Consumption/Current/Max' % activeIn]): - self._settings['/Ac/%s/Consumption/Current/Max' % activeIn] = self._acMaxima[activeIn] - - # Manual mode: relay min/max settings from localsettings to newvalues - # We have to fill newvalues on every iteration here because if we don't the value in dbusservice is invalidated - else: - for p in self._minMaxPaths.keys(): - newvalues[p] = self._settings[p] - - newvalues['/Ac/Consumption/Current/Max'] = self._settings['/Ac/%s/Consumption/Current/Max' % activeIn] - - # ==== UPDATE DBUS ITEMS ==== - with self._dbusservice as sss: - for path in self._summeditems.keys(): - # Why the None? Because we want to invalidate things we don't have anymore. - sss[path] = newvalues.get(path, None) - - def _handleservicechange(self): - # Update the available battery monitor services, used to populate the dropdown in the settings. - # Below code makes a dictionary. The key is [dbuserviceclass]/[deviceinstance]. For example - # "battery/245". The value is the name to show to the user in the dropdown. The full dbus- - # servicename, ie 'com.victronenergy.vebus.ttyO1' is not used, since the last part of that is not - # fixed. dbus-serviceclass name and the device instance are already fixed, so best to use those. - - services = self._get_connected_service_list('com.victronenergy.vebus') - services.update(self._get_connected_service_list('com.victronenergy.battery')) - services.update({k: v for k, v in self._get_connected_service_list( - 'com.victronenergy.multi').items() if self._dbusmonitor.get_value(k, '/Soc') is not None}) - services.update({k: v for k, v in self._get_connected_service_list( - 'com.victronenergy.inverter').items() if self._dbusmonitor.get_value(k, '/Soc') is not None}) - - ul = {self.BATSERVICE_DEFAULT: 'Automatic', self.BATSERVICE_NOBATTERY: 'No battery monitor'} - for servicename, instance in services.items(): - key = self._get_instance_service_name(servicename, instance) - ul[key] = self._get_readable_service_name(servicename) - self._dbusservice['/AvailableBatteryServices'] = json.dumps(ul) - - ul = {self.BATSERVICE_DEFAULT: 'Automatic', self.BATSERVICE_NOBATTERY: 'No battery monitor'} - # For later: for device supporting multiple Dc measurement we should add entries for /Dc/1 etc as - # well. - for servicename, instance in services.items(): - key = self._get_instance_service_name(servicename, instance).replace('.', '_').replace('/', '_') + '/Dc/0' - ul[key] = self._get_readable_service_name(servicename) - self._dbusservice['/AvailableBatteryMeasurements'] = ul - - self._determinebatteryservice() - - self._changed = True - - def _get_readable_service_name(self, servicename): - return '%s on %s' % ( - self._dbusmonitor.get_value(servicename, '/ProductName'), - self._dbusmonitor.get_value(servicename, '/Mgmt/Connection')) - - def _get_instance_service_name(self, service, instance): - return '%s/%s' % ('.'.join(service.split('.')[0:3]), instance) - - def _remove_unconnected_services(self, services): - # Workaround: because com.victronenergy.vebus is available even when there is no vebus product - # connected, remove any service that is not connected. Previously we used - # /State since mandatory path /Connected is not implemented in mk2dbus, - # but this has since been resolved. - for servicename in list(services.keys()): - if (self._dbusmonitor.get_value(servicename, '/Connected') != 1 - or self._dbusmonitor.get_value(servicename, '/ProductName') is None - or self._dbusmonitor.get_value(servicename, '/Mgmt/Connection') is None): - del services[servicename] - - def _dbus_value_changed(self, dbusServiceName, dbusPath, dict, changes, deviceInstance): - self._changed = True - - # Workaround because com.victronenergy.vebus is available even when there is no vebus product - # connected. - if (dbusPath in ['/Connected', '/ProductName', '/Mgmt/Connection'] or - (dbusPath == '/State' and dbusServiceName.split('.')[0:3] == ['com', 'victronenergy', 'vebus'])): - self._handleservicechange() - - # Track the timezone changes - if dbusPath == '/Settings/System/TimeZone': - tz = changes.get('Value') - if tz is not None: - os.environ['TZ'] = tz - time.tzset() - - def _device_added(self, service, instance, do_service_change=True): - if do_service_change: - self._handleservicechange() - - for m in self._modules: - m.device_added(service, instance, do_service_change) - - def _device_removed(self, service, instance): - self._handleservicechange() - - for m in self._modules: - m.device_removed(service, instance) - - def _gettext(self, path, value): - item = self._summeditems.get(path) - if item is not None: - try: - gettext = item['gettext'] - except KeyError: - pass - else: - if callable(gettext): - return gettext(value) - return gettext % value - return str(value) - - def _compute_number_of_phases(self, path, newvalues): - number_of_phases = None - for phase in range(1, 4): - p = newvalues.get('%s/L%s/Power' % (path, phase)) - if p is not None: - number_of_phases = phase - newvalues[path + '/NumberOfPhases'] = number_of_phases - - def _get_connected_service_list(self, classfilter=None): - services = self._dbusmonitor.get_service_list(classfilter=classfilter) - self._remove_unconnected_services(services) - return services - - # returns a servicename string - def _get_first_connected_service(self, classfilter): - services = self._get_connected_service_list(classfilter=classfilter) - if len(services) == 0: - return None - return next(iter(services.items()), (None,))[0] - - # returns a tuple (servicename, instance) - def _get_service_having_lowest_instance(self, classfilter=None): - services = self._get_connected_service_list(classfilter=classfilter) - if len(services) == 0: - return None - - # sort the dict by value; returns list of tuples: (value, key) - s = sorted((value, key) for (key, value) in services.items()) - return (s[0][1], s[0][0]) - - -class DbusSystemCalc(SystemCalc): - def _create_dbus_monitor(self, *args, **kwargs): - return DbusMonitor(*args, **kwargs) - - def _create_settings(self, *args, **kwargs): - bus = dbus.SessionBus() if 'DBUS_SESSION_BUS_ADDRESS' in os.environ else dbus.SystemBus() - return SettingsDevice(bus, *args, timeout=10, **kwargs) - - def _create_dbus_service(self): - venusversion, venusbuildtime = self._get_venus_versioninfo() - - dbusservice = VeDbusService('com.victronenergy.system') - dbusservice.add_mandatory_paths( - processname=__file__, - processversion=softwareVersion, - connection='data from other dbus processes', - deviceinstance=0, - productid=None, - productname=None, - firmwareversion=venusversion, - hardwareversion=None, - connected=1) - dbusservice.add_path('/FirmwareBuild', value=venusbuildtime) - return dbusservice - - def _get_venus_versioninfo(self): - try: - with open("/opt/victronenergy/version", "r") as fp: - version, software, buildtime = fp.read().split('\n')[:3] - major, minor, _, rev = re.compile('v([0-9]*)\.([0-9]*)(~([0-9]*))?').match(version).groups() - return (int(major, 16)<<16)+(int(minor, 16)<<8)+(0 if rev is None else int(rev, 16)), buildtime - except Exception: - pass - return 0, '0' - -if __name__ == "__main__": - # Argument parsing - parser = argparse.ArgumentParser( - description='Converts readings from AC-Sensors connected to a VE.Bus device in a pvinverter ' + - 'D-Bus service.' - ) - - parser.add_argument("-d", "--debug", help="set logging level to debug", - action="store_true") - - args = parser.parse_args() - - print("-------- dbus_systemcalc, v" + softwareVersion + " is starting up --------") - logger = setup_logging(args.debug) - - # Have a mainloop, so we can send/receive asynchronous calls to and from dbus - DBusGMainLoop(set_as_default=True) - - systemcalc = DbusSystemCalc() - - # Start and run the mainloop - logger.info("Starting mainloop, responding only on events") - mainloop = GLib.MainLoop() - mainloop.run() diff --git a/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v3.40~15.orig b/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v3.40~15.orig deleted file mode 100755 index 0fe07852..00000000 --- a/FileSets/PatchSource/obsoletePatches/dbus_systemcalc.py-v3.40~15.orig +++ /dev/null @@ -1,1246 +0,0 @@ -#!/usr/bin/python3 -u -# -*- coding: utf-8 -*- - -from dbus.mainloop.glib import DBusGMainLoop -import dbus -import argparse -import sys -import os -import json -import time -import re -from gi.repository import GLib - -# Victron packages -sys.path.insert(1, os.path.join(os.path.dirname(__file__), 'ext', 'velib_python')) -from vedbus import VeDbusService -from ve_utils import get_vrm_portal_id, exit_on_error -from dbusmonitor import DbusMonitor -from settingsdevice import SettingsDevice -from logger import setup_logging -import delegates -from sc_utils import safeadd as _safeadd, safemax as _safemax - -softwareVersion = '2.171' - -class SystemCalc: - STATE_IDLE = 0 - STATE_CHARGING = 1 - STATE_DISCHARGING = 2 - BATSERVICE_DEFAULT = 'default' - BATSERVICE_NOBATTERY = 'nobattery' - def __init__(self): - # Why this dummy? Because DbusMonitor expects these values to be there, even though we don't - # need them. So just add some dummy data. This can go away when DbusMonitor is more generic. - dummy = {'code': None, 'whenToLog': 'configChange', 'accessLevel': None} - dbus_tree = { - 'com.victronenergy.solarcharger': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Load/I': dummy, - '/FirmwareVersion': dummy}, - 'com.victronenergy.battery': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/DeviceInstance': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/1/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Soc': dummy, - '/Sense/Current': dummy, - '/TimeToGo': dummy, - '/ConsumedAmphours': dummy, - '/ProductId': dummy, - '/CustomName': dummy, - '/Info/MaxChargeVoltage': dummy}, - 'com.victronenergy.vebus' : { - '/Ac/ActiveIn/ActiveInput': dummy, - '/Ac/ActiveIn/L1/P': dummy, - '/Ac/ActiveIn/L2/P': dummy, - '/Ac/ActiveIn/L3/P': dummy, - '/Ac/ActiveIn/L1/I': dummy, - '/Ac/ActiveIn/L2/I': dummy, - '/Ac/ActiveIn/L3/I': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L2/P': dummy, - '/Ac/Out/L3/P': dummy, - '/Ac/Out/L1/I': dummy, - '/Ac/Out/L2/I': dummy, - '/Ac/Out/L3/I': dummy, - '/Connected': dummy, - '/ProductId': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Mode': dummy, - '/State': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.fuelcell': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy}, - 'com.victronenergy.charger': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/1/Voltage': dummy, - '/Dc/1/Current': dummy, - '/Dc/2/Voltage': dummy, - '/Dc/2/Current': dummy}, - 'com.victronenergy.grid' : { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/ProductId' : dummy, - '/DeviceType' : dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, - '/Ac/L1/Current': dummy, - '/Ac/L2/Current': dummy, - '/Ac/L3/Current': dummy}, - 'com.victronenergy.genset' : { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/ProductId' : dummy, - '/DeviceType' : dummy, - '/Ac/L1/Power': dummy, - '/Ac/L2/Power': dummy, - '/Ac/L3/Power': dummy, - '/Ac/L1/Current': dummy, - '/Ac/L2/Current': dummy, - '/Ac/L3/Current': dummy, - '/StarterVoltage': dummy}, - 'com.victronenergy.settings' : { - '/Settings/SystemSetup/AcInput1' : dummy, - '/Settings/SystemSetup/AcInput2' : dummy, - '/Settings/CGwacs/RunWithoutGridMeter' : dummy, - '/Settings/System/TimeZone' : dummy}, - 'com.victronenergy.temperature': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy}, - 'com.victronenergy.inverter': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L1/S': dummy, - '/Ac/Out/L1/V': dummy, - '/Ac/Out/L1/I': dummy, - '/Ac/Out/L2/P': dummy, - '/Ac/Out/L2/S': dummy, - '/Ac/Out/L2/V': dummy, - '/Ac/Out/L2/I': dummy, - '/Ac/Out/L3/P': dummy, - '/Ac/Out/L3/S': dummy, - '/Ac/Out/L3/V': dummy, - '/Ac/Out/L3/I': dummy, - '/Yield/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.multi': { - '/Connected': dummy, - '/ProductName': dummy, - '/Mgmt/Connection': dummy, - '/Dc/0/Voltage': dummy, - '/Dc/0/Current': dummy, - '/Dc/0/Power': dummy, - '/Ac/ActiveIn/ActiveInput': dummy, - '/Ac/In/1/Type': dummy, - '/Ac/In/2/Type': dummy, - '/Ac/In/1/L1/P': dummy, - '/Ac/In/1/L1/I': dummy, - '/Ac/In/2/L1/P': dummy, - '/Ac/In/2/L1/I': dummy, - '/Ac/Out/L1/P': dummy, - '/Ac/Out/L1/V': dummy, - '/Ac/Out/L1/I': dummy, - '/Ac/In/1/L2/P': dummy, - '/Ac/In/1/L2/I': dummy, - '/Ac/In/2/L2/P': dummy, - '/Ac/In/2/L2/I': dummy, - '/Ac/Out/L2/P': dummy, - '/Ac/Out/L2/V': dummy, - '/Ac/Out/L2/I': dummy, - '/Ac/In/1/L3/P': dummy, - '/Ac/In/1/L3/I': dummy, - '/Ac/In/2/L3/P': dummy, - '/Ac/In/2/L3/I': dummy, - '/Ac/Out/L3/P': dummy, - '/Ac/Out/L3/V': dummy, - '/Ac/Out/L3/I': dummy, - '/Yield/Power': dummy, - '/Soc': dummy}, - 'com.victronenergy.dcsystem': { - '/Dc/0/Voltage': dummy, - '/Dc/0/Power': dummy - }, - 'com.victronenergy.alternator': { - '/Dc/0/Power': dummy - } - } - - self._modules = [ - delegates.Multi(), - delegates.HubTypeSelect(), - delegates.VebusSocWriter(), - delegates.ServiceMapper(), - delegates.RelayState(), - delegates.BuzzerControl(), - delegates.LgCircuitBreakerDetect(), - delegates.BatterySoc(self), - delegates.Dvcc(self), - delegates.BatterySense(self), - delegates.BatterySettings(self), - delegates.SystemState(self), - delegates.BatteryLife(), - delegates.ScheduledCharging(), - delegates.SourceTimers(), - delegates.BatteryData(), - delegates.Gps(), - delegates.AcInputs(), - delegates.GensetStartStop(), - delegates.SocSync(self), - delegates.PvInverters(), - delegates.BatteryService(self), - delegates.CanBatterySense(), - delegates.InverterCharger(), - delegates.DynamicEss(), - delegates.LoadShedding()] - - for m in self._modules: - for service, paths in m.get_input(): - s = dbus_tree.setdefault(service, {}) - for path in paths: - s[path] = dummy - - self._dbusmonitor = self._create_dbus_monitor(dbus_tree, valueChangedCallback=self._dbus_value_changed, - deviceAddedCallback=self._device_added, deviceRemovedCallback=self._device_removed) - - # Connect to localsettings - supported_settings = { - 'batteryservice': ['/Settings/SystemSetup/BatteryService', self.BATSERVICE_DEFAULT, 0, 0], - 'hasdcsystem': ['/Settings/SystemSetup/HasDcSystem', 0, 0, 1], - 'useacout': ['/Settings/SystemSetup/HasAcOutSystem', 1, 0, 1], - 'gaugeautomax': ['/Settings/Gui/Gauges/AutoMax', 1, 0, 1], - 'acin0min': ['/Settings/Gui/Gauges/Ac/In/0/Current/Min', float(0), -float("inf"), 0], - 'acin1min': ['/Settings/Gui/Gauges/Ac/In/1/Current/Min', float(0), -float("inf"), 0], - 'acin0max': ['/Settings/Gui/Gauges/Ac/In/0/Current/Max', float(0), 0, float("inf")], - 'acin1max': ['/Settings/Gui/Gauges/Ac/In/1/Current/Max', float(0), 0, float("inf")], - 'dcinmax': ['/Settings/Gui/Gauges/Dc/Input/Power/Max', float(0), 0, float("inf")], - 'dcsysmax': ['/Settings/Gui/Gauges/Dc/System/Power/Max', float(0), 0, float("inf")], - 'pvmax': ['/Settings/Gui/Gauges/Pv/Power/Max', float(0), 0, float("inf")], - 'noacinmax': ['/Settings/Gui/Gauges/Ac/NoAcIn/Consumption/Current/Max', float(0), 0, float("inf")], - 'acin1max': ['/Settings/Gui/Gauges/Ac/AcIn1/Consumption/Current/Max', float(0), 0, float("inf")], - 'acin2max': ['/Settings/Gui/Gauges/Ac/AcIn2/Consumption/Current/Max', float(0), 0, float("inf")], - } - - for m in self._modules: - for setting in m.get_settings(): - supported_settings[setting[0]] = list(setting[1:]) - - self._settings = self._create_settings(supported_settings, self._handlechangedsetting) - - self._dbusservice = self._create_dbus_service() - - for m in self._modules: - m.set_sources(self._dbusmonitor, self._settings, self._dbusservice) - - # At this moment, VRM portal ID is the MAC address of the CCGX. Anyhow, it should be string uniquely - # identifying the CCGX. - self._dbusservice.add_path('/Serial', value=get_vrm_portal_id()) - self._dbusservice.add_path( - '/AvailableBatteryServices', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/AvailableBatteryMeasurements', value=None) - self._dbusservice.add_path( - '/AutoSelectedBatteryService', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/AutoSelectedBatteryMeasurement', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/ActiveBatteryService', value=None, gettextcallback=self._gettext) - self._dbusservice.add_path( - '/Dc/Battery/BatteryService', value=None) - self._summeditems = { - '/Ac/Grid/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Grid/L1/Current': {'gettext': '%.1F A'}, - '/Ac/Grid/L2/Current': {'gettext': '%.1F A'}, - '/Ac/Grid/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Grid/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Grid/ProductId': {'gettext': '%s'}, - '/Ac/Grid/DeviceType': {'gettext': '%s'}, - '/Ac/Genset/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Genset/L1/Current': {'gettext': '%.1F A'}, - '/Ac/Genset/L2/Current': {'gettext': '%.1F A'}, - '/Ac/Genset/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Genset/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Genset/ProductId': {'gettext': '%s'}, - '/Ac/Genset/DeviceType': {'gettext': '%s'}, - '/Ac/ConsumptionOnOutput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnOutput/L1/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnOutput/L2/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnOutput/L3/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnInput/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L3/Power': {'gettext': '%.0F W'}, - '/Ac/ConsumptionOnInput/L1/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnInput/L2/Current': {'gettext': '%.1F A'}, - '/Ac/ConsumptionOnInput/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/NumberOfPhases': {'gettext': '%.0F W'}, - '/Ac/Consumption/L1/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L2/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L3/Power': {'gettext': '%.0F W'}, - '/Ac/Consumption/L1/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/L2/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/L3/Current': {'gettext': '%.1F A'}, - '/Ac/Consumption/NumberOfPhases': {'gettext': '%.0F W'}, - '/Dc/Pv/Power': {'gettext': '%.0F W'}, - '/Dc/Pv/Current': {'gettext': '%.1F A'}, - '/Dc/Battery/Voltage': {'gettext': '%.2F V'}, - '/Dc/Battery/VoltageService': {'gettext': '%s'}, - '/Dc/Battery/Current': {'gettext': '%.1F A'}, - '/Dc/Battery/Power': {'gettext': '%.0F W'}, - '/Dc/Battery/State': {'gettext': lambda v: ({ - self.STATE_IDLE: 'Idle', - self.STATE_CHARGING: 'Charging', - self.STATE_DISCHARGING: 'Discharging'}.get(v, 'Unknown'))}, - '/Dc/Battery/TimeToGo': {'gettext': '%.0F s'}, - '/Dc/Battery/ConsumedAmphours': {'gettext': '%.1F Ah'}, - '/Dc/Battery/ProductId': {'gettext': '0x%x'}, - '/Dc/Charger/Power': {'gettext': '%.0F %%'}, - '/Dc/FuelCell/Power': {'gettext': '%.0F %%'}, - '/Dc/Alternator/Power': {'gettext': '%.0F W'}, - '/Dc/System/Power': {'gettext': '%.0F W'}, - '/Dc/System/MeasurementType': {'gettext': '%d'}, - '/Ac/ActiveIn/Source': {'gettext': '%s'}, - '/Ac/ActiveIn/L1/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/L2/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/L3/Power': {'gettext': '%.0F W'}, - '/Ac/ActiveIn/L1/Current': {'gettext': '%.1F A'}, - '/Ac/ActiveIn/L2/Current': {'gettext': '%.1F A'}, - '/Ac/ActiveIn/L3/Current': {'gettext': '%.1F A'}, - '/Ac/ActiveIn/NumberOfPhases': {'gettext': '%d'}, - } - - for m in self._modules: - self._summeditems.update(m.get_output()) - - for path in self._summeditems.keys(): - self._dbusservice.add_path(path, value=None, gettextcallback=self._gettext) - - self._batteryservice = None - self._determinebatteryservice() - - if self._batteryservice is None: - logger.info("Battery service initialized to None (setting == %s)" % - self._settings['batteryservice']) - - self._changed = True - for service, instance in self._dbusmonitor.get_service_list().items(): - self._device_added(service, instance, do_service_change=False) - - self._handleservicechange() - self._updatevalues() - - GLib.timeout_add(1000, exit_on_error, self._handletimertick) - - 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): - raise Exception("This function should be overridden") - - def _handlechangedsetting(self, setting, oldvalue, newvalue): - self._determinebatteryservice() - self._changed = True - - # Give our delegates a chance to react on a settings change - for m in self._modules: - m.settings_changed(setting, oldvalue, newvalue) - - def _find_device_instance(self, serviceclass, instance): - """ Gets a mapping of services vs DeviceInstance using - get_service_list. Then searches for the specified DeviceInstance - and returns the service name. """ - services = self._dbusmonitor.get_service_list(classfilter=serviceclass) - - for k, v in services.items(): - if v == instance: - return k - return None - - def _determinebatteryservice(self): - auto_battery_service = self._autoselect_battery_service() - auto_battery_measurement = None - auto_selected = False - if auto_battery_service is not None: - services = self._dbusmonitor.get_service_list() - if auto_battery_service in services: - auto_battery_measurement = \ - self._get_instance_service_name(auto_battery_service, services[auto_battery_service]) - auto_battery_measurement = auto_battery_measurement.replace('.', '_').replace('/', '_') + '/Dc/0' - self._dbusservice['/AutoSelectedBatteryMeasurement'] = auto_battery_measurement - - if self._settings['batteryservice'] == self.BATSERVICE_DEFAULT: - auto_selected = True - newbatteryservice = auto_battery_service - self._dbusservice['/AutoSelectedBatteryService'] = ( - 'No battery monitor found' if newbatteryservice is None else - self._get_readable_service_name(newbatteryservice)) - - elif self._settings['batteryservice'] == self.BATSERVICE_NOBATTERY: - self._dbusservice['/AutoSelectedBatteryService'] = None - newbatteryservice = None - - else: - self._dbusservice['/AutoSelectedBatteryService'] = None - - s = self._settings['batteryservice'].split('/') - if len(s) != 2: - logger.error("The battery setting (%s) is invalid!" % self._settings['batteryservice']) - serviceclass = s[0] - instance = int(s[1]) if len(s) == 2 else None - - # newbatteryservice might turn into None if a chosen battery - # monitor no longer exists. Don't auto change the setting (it might - # come back) and don't autoselect another. - newbatteryservice = self._find_device_instance(serviceclass, instance) - - if newbatteryservice != self._batteryservice: - services = self._dbusmonitor.get_service_list() - instance = services.get(newbatteryservice, None) - if instance is None: - battery_service = None - else: - battery_service = self._get_instance_service_name(newbatteryservice, instance) - self._dbusservice['/ActiveBatteryService'] = battery_service - logger.info("Battery service, setting == %s, changed from %s to %s (%s)" % - (self._settings['batteryservice'], self._batteryservice, newbatteryservice, instance)) - - # Battery service has changed. Notify delegates. - self._dbusservice['/Dc/Battery/BatteryService'] = self._batteryservice = newbatteryservice - for m in self._modules: - m.battery_service_changed(auto_selected, self._batteryservice, newbatteryservice) - - def _autoselect_battery_service(self): - # Default setting business logic: - # first try to use a battery service (BMV or Lynx Shunt VE.Can). If there - # is more than one battery service, just use a random one. If no battery service is - # available, check if there are not Solar chargers and no normal chargers. If they are not - # there, assume this is a hub-2, hub-3 or hub-4 system and use VE.Bus SOC. - batteries = self._get_connected_service_list('com.victronenergy.battery') - - # Pick the battery service that has the lowest DeviceInstance, giving - # preference to those with a BMS. Instances of 'lynxparallel' are preferred over regular BMSes. - if len(batteries) > 0: - batteries = [ - (not self._dbusmonitor.seen(s, '/Info/MaxChargeVoltage'), - not s.startswith('com.victronenergy.battery.lynxparallel'), i, s) - for s, i in batteries.items()] - return sorted(batteries, key=lambda x: x[:3])[0][3] - - # No battery services, and there is a charger in the system. Abandon - # hope. - if self._get_first_connected_service('com.victronenergy.charger') is not None: - return None - - # Also no Multi, then give up. - vebus_service = self._get_service_having_lowest_instance('com.victronenergy.vebus') - if vebus_service is None: - # No VE.Bus, but maybe there is an inverter with built-in SOC - # tracking, eg RS Smart or Multi RS. - inverter = self._get_service_having_lowest_instance('com.victronenergy.multi') - if inverter and self._dbusmonitor.get_value(inverter[0], '/Soc') is not None: - return inverter[0] - - inverter = self._get_service_having_lowest_instance('com.victronenergy.inverter') - if inverter and self._dbusmonitor.get_value(inverter[0], '/Soc') is not None: - return inverter[0] - - return None - - # There is a Multi, it supports tracking external charge current from - # solarchargers, and there are no DC loads. Then use it. - if self._dbusmonitor.get_value( - vebus_service[0], '/ExtraBatteryCurrent') is not None \ - and self._get_first_connected_service('com.victronenergy.dcsystem') is None \ - and self._settings['hasdcsystem'] == 0: - return vebus_service[0] - - # Multi does not support tracking solarcharger current, and we have - # solar chargers. Then we cannot use it. - if self._get_first_connected_service('com.victronenergy.solarcharger') is not None: - return None - - # Only a Multi, no other chargers. Then we can use it. - return vebus_service[0] - - @property - def batteryservice(self): - return self._batteryservice - - # Called on a one second timer - def _handletimertick(self): - if self._changed: - self._updatevalues() - self._changed = False - - return True # keep timer running - - def _updatevalues(self): - # ==== PREPARATIONS ==== - newvalues = {} - - # Set the user timezone - if 'TZ' not in os.environ: - tz = self._dbusmonitor.get_value('com.victronenergy.settings', '/Settings/System/TimeZone') - if tz is not None: - os.environ['TZ'] = tz - time.tzset() - - # Determine values used in logic below - vebusses = self._dbusmonitor.get_service_list('com.victronenergy.vebus') - vebuspower = 0 - for vebus in vebusses: - v = self._dbusmonitor.get_value(vebus, '/Dc/0/Voltage') - i = self._dbusmonitor.get_value(vebus, '/Dc/0/Current') - if v is not None and i is not None: - vebuspower += v * i - - # ==== PVINVERTERS ==== - # Work is done in pv-inverter delegate. Ideally all of this should - # happen in update_values in the delegate, but these values are - # used below in calculating consumption, so until this is less - # unwieldy this has to stay here. - # TODO this can go away once consumption below no longer relies - # on these values, or has moved to its own delegate. - newvalues.update(delegates.PvInverters.instance.get_totals()) - self._compute_number_of_phases('/Ac/PvOnGrid', newvalues) - self._compute_number_of_phases('/Ac/PvOnOutput', newvalues) - self._compute_number_of_phases('/Ac/PvOnGenset', newvalues) - - # ==== SOLARCHARGERS ==== - solarchargers = self._dbusmonitor.get_service_list('com.victronenergy.solarcharger') - solarcharger_batteryvoltage = None - solarcharger_batteryvoltage_service = None - solarchargers_charge_power = 0 - solarchargers_loadoutput_power = None - - for solarcharger in solarchargers: - v = self._dbusmonitor.get_value(solarcharger, '/Dc/0/Voltage') - if v is None: - continue - i = self._dbusmonitor.get_value(solarcharger, '/Dc/0/Current') - if i is None: - continue - l = self._dbusmonitor.get_value(solarcharger, '/Load/I', 0) - - if l is not None: - if solarchargers_loadoutput_power is None: - solarchargers_loadoutput_power = l * v - else: - solarchargers_loadoutput_power += l * v - - solarchargers_charge_power += v * i - - # Note that this path is not in the _summeditems{}, making for it to not be - # published on D-Bus. Which fine. The only one needing it is the vebussocwriter- - # delegate. - if '/Dc/Pv/ChargeCurrent' not in newvalues: - newvalues['/Dc/Pv/ChargeCurrent'] = i - else: - newvalues['/Dc/Pv/ChargeCurrent'] += i - - if '/Dc/Pv/Power' not in newvalues: - newvalues['/Dc/Pv/Power'] = v * _safeadd(i, l) - newvalues['/Dc/Pv/Current'] = _safeadd(i, l) - solarcharger_batteryvoltage = v - solarcharger_batteryvoltage_service = solarcharger - else: - newvalues['/Dc/Pv/Power'] += v * _safeadd(i, l) - newvalues['/Dc/Pv/Current'] += _safeadd(i, l) - - # ==== FUELCELLS ==== - fuelcells = self._dbusmonitor.get_service_list('com.victronenergy.fuelcell') - fuelcell_batteryvoltage = None - fuelcell_batteryvoltage_service = None - for fuelcell in fuelcells: - # Assume the battery connected to output 0 is the main battery - v = self._dbusmonitor.get_value(fuelcell, '/Dc/0/Voltage') - if v is None: - continue - - fuelcell_batteryvoltage = v - fuelcell_batteryvoltage_service = fuelcell - - i = self._dbusmonitor.get_value(fuelcell, '/Dc/0/Current') - if i is None: - continue - - if '/Dc/FuelCell/Power' not in newvalues: - newvalues['/Dc/FuelCell/Power'] = v * i - else: - newvalues['/Dc/FuelCell/Power'] += v * i - - # ==== ALTERNATOR ==== - alternators = self._dbusmonitor.get_service_list('com.victronenergy.alternator') - for alternator in alternators: - # Assume the battery connected to output 0 is the main battery - p = self._dbusmonitor.get_value(alternator, '/Dc/0/Power') - if p is None: - continue - - if '/Dc/Alternator/Power' not in newvalues: - newvalues['/Dc/Alternator/Power'] = p - else: - newvalues['/Dc/Alternator/Power'] += p - - # ==== CHARGERS ==== - chargers = self._dbusmonitor.get_service_list('com.victronenergy.charger') - charger_batteryvoltage = None - charger_batteryvoltage_service = None - for charger in chargers: - # Assume the battery connected to output 0 is the main battery - v = self._dbusmonitor.get_value(charger, '/Dc/0/Voltage') - if v is None: - continue - - charger_batteryvoltage = v - charger_batteryvoltage_service = charger - - i = self._dbusmonitor.get_value(charger, '/Dc/0/Current') - if i is None: - continue - - if '/Dc/Charger/Power' not in newvalues: - newvalues['/Dc/Charger/Power'] = v * i - else: - newvalues['/Dc/Charger/Power'] += v * i - - # ==== Other Inverters and Inverter/Chargers ==== - _other_inverters = sorted((di, s) for s, di in self._dbusmonitor.get_service_list('com.victronenergy.multi').items()) + \ - sorted((di, s) for s, di in self._dbusmonitor.get_service_list('com.victronenergy.inverter').items()) - non_vebus_inverters = [x[1] for x in _other_inverters] - non_vebus_inverter = None - if non_vebus_inverters: - non_vebus_inverter = non_vebus_inverters[0] - - # For RS Smart and Multi RS, add PV to the yield - for i in non_vebus_inverters: - if (pv_yield := self._dbusmonitor.get_value(i, "/Yield/Power")) is not None: - newvalues['/Dc/Pv/Power'] = newvalues.get('/Dc/Pv/Power', 0) + pv_yield - - # Used lower down, possibly needed for battery values as well - dcsystems = self._dbusmonitor.get_service_list('com.victronenergy.dcsystem') - - # ==== BATTERY ==== - if self._batteryservice is not None: - batteryservicetype = self._batteryservice.split('.')[2] - assert batteryservicetype in ('battery', 'vebus', 'inverter', 'multi') - - newvalues['/Dc/Battery/TimeToGo'] = self._dbusmonitor.get_value(self._batteryservice,'/TimeToGo') - newvalues['/Dc/Battery/ConsumedAmphours'] = self._dbusmonitor.get_value(self._batteryservice,'/ConsumedAmphours') - newvalues['/Dc/Battery/ProductId'] = self._dbusmonitor.get_value(self._batteryservice, '/ProductId') - - if batteryservicetype in ('battery', 'inverter', 'multi'): - newvalues['/Dc/Battery/Voltage'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Voltage') - newvalues['/Dc/Battery/VoltageService'] = self._batteryservice - newvalues['/Dc/Battery/Current'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Current') - newvalues['/Dc/Battery/Power'] = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Power') - - elif batteryservicetype == 'vebus': - vebus_voltage = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Voltage') - vebus_current = self._dbusmonitor.get_value(self._batteryservice, '/Dc/0/Current') - vebus_power = None if vebus_voltage is None or vebus_current is None else vebus_current * vebus_voltage - newvalues['/Dc/Battery/Voltage'] = vebus_voltage - newvalues['/Dc/Battery/VoltageService'] = self._batteryservice - if self._settings['hasdcsystem'] == 1 or dcsystems: - # hasdcsystem will normally disqualify the multi from being - # auto-selected as battery monitor, so the only way we're - # here is if the user explicitly selected the multi as the - # battery service - newvalues['/Dc/Battery/Current'] = vebus_current - if vebus_power is not None: - newvalues['/Dc/Battery/Power'] = vebus_power - else: - battery_power = _safeadd(solarchargers_charge_power, vebus_power) - newvalues['/Dc/Battery/Current'] = battery_power / vebus_voltage if vebus_voltage is not None and vebus_voltage > 0 else None - newvalues['/Dc/Battery/Power'] = battery_power - - - p = newvalues.get('/Dc/Battery/Power', None) - if p is not None: - if p > 30: - newvalues['/Dc/Battery/State'] = self.STATE_CHARGING - elif p < -30: - newvalues['/Dc/Battery/State'] = self.STATE_DISCHARGING - else: - newvalues['/Dc/Battery/State'] = self.STATE_IDLE - - else: - # The battery service is not a BMS/BMV or a suitable vebus. A - # suitable vebus is defined as one explicitly selected by the user, - # or one that was automatically selected for SOC tracking. We may - # however still have a VE.Bus, just not one that can accurately - # track SOC. If we have one, use it as voltage source. Otherwise - # try a solar charger, a charger, a vedirect inverter or a dcsource - # as fallbacks. - batteryservicetype = None - vebusses = self._dbusmonitor.get_service_list('com.victronenergy.vebus') - for vebus in vebusses: - v = self._dbusmonitor.get_value(vebus, '/Dc/0/Voltage') - s = self._dbusmonitor.get_value(vebus, '/State') - if v is not None and s not in (0, None): - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = vebus - break # Skip the else below - else: - # No suitable vebus voltage, try other devices - if non_vebus_inverter is not None and (v := self._dbusmonitor.get_value(non_vebus_inverter, '/Dc/0/Voltage')) is not None: - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = non_vebus_inverter - elif solarcharger_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = solarcharger_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = solarcharger_batteryvoltage_service - elif charger_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = charger_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = charger_batteryvoltage_service - elif fuelcell_batteryvoltage is not None: - newvalues['/Dc/Battery/Voltage'] = fuelcell_batteryvoltage - newvalues['/Dc/Battery/VoltageService'] = fuelcell_batteryvoltage_service - elif dcsystems: - # Get voltage from first dcsystem - s = next(iter(dcsystems.keys())) - v = self._dbusmonitor.get_value(s, '/Dc/0/Voltage') - if v is not None: - newvalues['/Dc/Battery/Voltage'] = v - newvalues['/Dc/Battery/VoltageService'] = s - - # We have no suitable battery monitor, so power and current data - # is not available. We can however calculate it from other values, - # if we have at least a battery voltage. - if '/Dc/Battery/Voltage' in newvalues: - dcsystempower = _safeadd(0, *(self._dbusmonitor.get_value(s, - '/Dc/0/Power', 0) for s in dcsystems)) - if dcsystems or self._settings['hasdcsystem'] == 0: - # Either DC loads are monitored, or there are no - # unmonitored DC loads or chargers: derive battery watts - # and amps from vebus, solarchargers, chargers and measured - # loads. - p = solarchargers_charge_power + newvalues.get('/Dc/Charger/Power', 0) + vebuspower - dcsystempower - voltage = newvalues['/Dc/Battery/Voltage'] - newvalues['/Dc/Battery/Current'] = p / voltage if voltage > 0 else None - newvalues['/Dc/Battery/Power'] = p - - # ==== SYSTEM POWER ==== - # Look for dcsytem devices, add them together. Otherwise, if enabled, - # calculate it - if dcsystems: - newvalues['/Dc/System/MeasurementType'] = 1 # measured - newvalues['/Dc/System/Power'] = 0 - for meter in dcsystems: - newvalues['/Dc/System/Power'] = _safeadd(newvalues['/Dc/System/Power'], - self._dbusmonitor.get_value(meter, '/Dc/0/Power')) - elif self._settings['hasdcsystem'] == 1 and batteryservicetype == 'battery': - # Calculate power being generated/consumed by not measured devices in the network. - # For MPPTs, take all the power, including power going out of the load output. - # /Dc/System: positive: consuming power - # VE.Bus: Positive: current flowing from the Multi to the dc system or battery - # Solarcharger & other chargers: positive: charging - # battery: Positive: charging battery. - # battery = solarcharger + charger + ve.bus - system - - battery_power = newvalues.get('/Dc/Battery/Power') - if battery_power is not None: - dc_pv_power = newvalues.get('/Dc/Pv/Power', 0) - charger_power = newvalues.get('/Dc/Charger/Power', 0) - fuelcell_power = newvalues.get('/Dc/FuelCell/Power', 0) - alternator_power = newvalues.get('/Dc/Alternator/Power', 0) - - # If there are VE.Direct inverters, remove their power from the - # DC estimate. This is done using the AC value when the DC - # power values are not available. - inverter_power = 0 - for i in non_vebus_inverters: - inverter_current = self._dbusmonitor.get_value(i, '/Dc/0/Current') - if inverter_current is not None: - inverter_power += self._dbusmonitor.get_value( - i, '/Dc/0/Voltage', 0) * inverter_current - else: - inverter_power -= self._dbusmonitor.get_value( - i, '/Ac/Out/L1/V', 0) * self._dbusmonitor.get_value( - i, '/Ac/Out/L1/I', 0) - newvalues['/Dc/System/MeasurementType'] = 0 # estimated - # FIXME In future we will subtract alternator power from the - # calculated DC power, because it will be individually - # displayed. For now, we leave it out so that in the current - # version of Venus it does not break user's expectations. - #newvalues['/Dc/System/Power'] = dc_pv_power + charger_power + fuelcell_power + vebuspower + inverter_power - battery_power - alternator_power - newvalues['/Dc/System/Power'] = dc_pv_power + charger_power + fuelcell_power + vebuspower + inverter_power - battery_power - - elif self._settings['hasdcsystem'] == 1 and solarchargers_loadoutput_power is not None: - newvalues['/Dc/System/MeasurementType'] = 0 # estimated - newvalues['/Dc/System/Power'] = solarchargers_loadoutput_power - - # ===== AC IN SOURCE ===== - multi_path = getattr(delegates.Multi.instance.multi, 'service', None) - ac_in_source = None - active_input = None - if multi_path is None: - # Check if we have an non-VE.Bus inverter. - if non_vebus_inverter is not None: - if (active_input := self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/ActiveIn/ActiveInput')) is not None and \ - active_input in (0, 1) and \ - (active_type := self._dbusmonitor.get_value(non_vebus_inverter, '/Ac/In/{}/Type'.format(active_input + 1))) is not None: - ac_in_source = active_type - else: - ac_in_source = 240 - else: - active_input = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/ActiveInput') - if active_input == 0xF0: - # Not connected - ac_in_source = 240 - elif active_input is not None: - settings_path = '/Settings/SystemSetup/AcInput%s' % (active_input + 1) - ac_in_source = self._dbusmonitor.get_value('com.victronenergy.settings', settings_path) - newvalues['/Ac/ActiveIn/Source'] = ac_in_source - - # ===== GRID METERS & CONSUMPTION ==== - grid_meter = delegates.AcInputs.instance.gridmeter - genset_meter = delegates.AcInputs.instance.gensetmeter - - # Make an educated guess as to what is being consumed from an AC source. If ac_in_source - # indicates grid, genset or shore, we use that. If the Multi is off, or disconnected through - # a relay assistant or otherwise, then assume the presence of a .grid or .genset service indicates - # presence of that AC source. If both are available, then give up. This decision making is here - # so the GUI has something to present even if the Multi is off. - ac_in_guess = ac_in_source - if ac_in_guess in (None, 0xF0): - if genset_meter is None and grid_meter is not None: - ac_in_guess = 1 - elif grid_meter is None and genset_meter is not None: - ac_in_guess = 2 - - consumption = { "L1" : None, "L2" : None, "L3" : None } - currentconsumption = { "L1" : None, "L2" : None, "L3" : None } - for device_type, em, _types in (('Grid', grid_meter, (1, 3)), ('Genset', genset_meter, (2,))): - # If a grid meter is present we use values from it. If not, we look at the multi. If it has - # AcIn1 or AcIn2 connected to the grid, we use those values. - # com.victronenergy.grid.??? indicates presence of an energy meter used as grid meter. - # com.victronenergy.vebus.???/Ac/ActiveIn/ActiveInput: decides which whether we look at AcIn1 - # or AcIn2 as possible grid connection. - uses_active_input = ac_in_source in _types - for phase in consumption: - p = None - mc = None - pvpower = newvalues.get('/Ac/PvOn%s/%s/Power' % (device_type, phase)) - pvcurrent = newvalues.get('/Ac/PvOn%s/%s/Current' % (device_type, phase)) - if em is not None: - p = self._dbusmonitor.get_value(em.service, '/Ac/%s/Power' % phase) - mc = self._dbusmonitor.get_value(em.service, '/Ac/%s/Current' % phase) - # Compute consumption between energy meter and multi (meter power - multi AC in) and - # add an optional PV inverter on input to the mix. - c = None - cc = None - if uses_active_input: - if multi_path is not None: - try: - c = _safeadd(c, -self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/P' % phase)) - cc = _safeadd(cc, -self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/I' % phase)) - except TypeError: - pass - elif non_vebus_inverter is not None and active_input in (0, 1): - for i in non_vebus_inverters: - try: - c = _safeadd(c, -self._dbusmonitor.get_value(i, '/Ac/In/%d/%s/P' % (active_input+1, phase))) - cc = _safeadd(cc, -self._dbusmonitor.get_value(i, '/Ac/In/%d/%s/I' % (active_input+1, phase))) - except TypeError: - pass - - # If there's any power coming from a PV inverter in the inactive AC in (which is unlikely), - # it will still be used, because there may also be a load in the same ACIn consuming - # power, or the power could be fed back to the net. - c = _safeadd(c, p, pvpower) - cc = _safeadd(cc, mc, pvcurrent) - consumption[phase] = _safeadd(consumption[phase], _safemax(0, c)) - currentconsumption[phase] = _safeadd(currentconsumption[phase], _safemax(0, cc)) - else: - if uses_active_input: - if multi_path is not None and ( - p := self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/P' % phase)) is not None: - consumption[phase] = _safeadd(0, consumption[phase]) - currentconsumption[phase] = _safeadd(0, currentconsumption[phase]) - mc = self._dbusmonitor.get_value(multi_path, '/Ac/ActiveIn/%s/I' % phase) - elif non_vebus_inverter is not None and active_input in (0, 1): - for i in non_vebus_inverters: - p = _safeadd(p, - self._dbusmonitor.get_value(i, '/Ac/In/%d/%s/P' % (active_input + 1, phase))) - mc = _safeadd(mc, - self._dbusmonitor.get_value(i, '/Ac/In/%d/%s/I' % (active_input + 1, phase))) - if p is not None: - consumption[phase] = _safeadd(0, consumption[phase]) - currentconsumption[phase] = _safeadd(0, currentconsumption[phase]) - - # No relevant energy meter present. Assume there is no load between the grid and the multi. - # There may be a PV inverter present though (Hub-3 setup). - try: - p = _safeadd(p, -pvpower) - mc = _safeadd(mc, -pvcurrent) - except TypeError: - pass - - newvalues['/Ac/%s/%s/Power' % (device_type, phase)] = p - newvalues['/Ac/%s/%s/Current' % (device_type, phase)] = mc - if ac_in_guess in _types: - newvalues['/Ac/ActiveIn/%s/Power' % (phase,)] = p - newvalues['/Ac/ActiveIn/%s/Current' % (phase,)] = mc - - self._compute_number_of_phases('/Ac/%s' % device_type, newvalues) - self._compute_number_of_phases('/Ac/ActiveIn', newvalues) - - product_id = None - device_type_id = None - if em is not None: - product_id = em.product_id - device_type_id = em.device_type - if product_id is None and uses_active_input: - if multi_path is not None: - product_id = self._dbusmonitor.get_value(multi_path, '/ProductId') - elif non_vebus_inverter is not None: - product_id = self._dbusmonitor.get_value(non_vebus_inverter, '/ProductId') - newvalues['/Ac/%s/ProductId' % device_type] = product_id - newvalues['/Ac/%s/DeviceType' % device_type] = device_type_id - - # If we have an ESS system and RunWithoutGridMeter is set, there cannot be load on the AC-In, so it - # must be on AC-Out. Hence we do calculate AC-Out consumption even if 'useacout' is disabled. - # Similarly all load are by definition on the output if this is not an ESS system. - use_ac_out = \ - self._settings['useacout'] == 1 or \ - (multi_path is not None and self._dbusmonitor.get_value(multi_path, '/Hub4/AssistantId') not in (4, 5)) or \ - self._dbusmonitor.get_value('com.victronenergy.settings', '/Settings/CGwacs/RunWithoutGridMeter') == 1 - for phase in consumption: - c = None - a = None - if use_ac_out: - c = newvalues.get('/Ac/PvOnOutput/%s/Power' % phase) - a = newvalues.get('/Ac/PvOnOutput/%s/Current' % phase) - if multi_path is None: - for inv in non_vebus_inverters: - ac_out = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/P' % phase) - i = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/I' % phase) - - # Some models don't show power, try apparent power, - # else calculate it - if ac_out is None: - ac_out = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/S' % phase) - if ac_out is None: - u = self._dbusmonitor.get_value(inv, '/Ac/Out/%s/V' % phase) - if None not in (i, u): - ac_out = i * u - c = _safeadd(c, ac_out) - a = _safeadd(a, i) - else: - ac_out = self._dbusmonitor.get_value(multi_path, '/Ac/Out/%s/P' % phase) - c = _safeadd(c, ac_out) - i_out = self._dbusmonitor.get_value(multi_path, '/Ac/Out/%s/I' % phase) - a = _safeadd(a, i_out) - c = _safemax(0, c) - a = _safemax(0, a) - newvalues['/Ac/ConsumptionOnOutput/%s/Power' % phase] = c - newvalues['/Ac/ConsumptionOnOutput/%s/Current' % phase] = a - newvalues['/Ac/ConsumptionOnInput/%s/Power' % phase] = consumption[phase] - newvalues['/Ac/ConsumptionOnInput/%s/Current' % phase] = currentconsumption[phase] - newvalues['/Ac/Consumption/%s/Power' % phase] = _safeadd(consumption[phase], c) - newvalues['/Ac/Consumption/%s/Current' % phase] = _safeadd(currentconsumption[phase], a) - self._compute_number_of_phases('/Ac/Consumption', newvalues) - self._compute_number_of_phases('/Ac/ConsumptionOnOutput', newvalues) - self._compute_number_of_phases('/Ac/ConsumptionOnInput', newvalues) - - for m in self._modules: - m.update_values(newvalues) - - # ==== UPDATE MINIMUM AND MAXIMUM LEVELS ==== - if (self._settings['gaugeautomax']): - # min/max values are stored and updated in localsettings - # values are stored under /Settings/Gui/Briefview - # /Settings/Gui/Gauges/AutoMax: - # 1-> Automatic: Gauge limits are updated automatically and stored in localsettings - # 0-> Manual: Gauge limits are entered manually by the user - # The gui pulls the gauge limits from localsettings and provides - # a means for the user to set them if Automax is off. - - # AC output - # This maximum is maintained for 3 situations: - # 1: AC input 1 is connected - # 2: AC input 2 is connected - # 3: No AC input is connected - # All 3 scenarios may lead to different maximum values since the capabilities of the system changes. - # So 3 different maxima are stored and relayed to /Ac/Consumption/Current/Max based on the active scenario. - activeIn = 'acin1' if (self._dbusservice['/Ac/In/0/Connected'] == 1) else \ - 'acin2' if (self._dbusservice['/Ac/In/1/Connected'] == 1) else \ - 'noacin' - - # Quattro has 2 AC inputs which cannot be active simultaneously. - # activeIn needs to 1 when 'Ac/In/1/Connected' is 1 and can be 0 otherwise. - activeInNr = int(activeIn[-1]) -1 if activeIn != 'noacin' else None - - # AC input - # Minimum values occur when feeding back to the grid. - # For the minimum value, make sure it is 0 at its maximum. - # Update correct '/Ac/In/..' based on the current active input. - # When no inputs are active, paths '/Ac/In/[0/1]/Current/[Min/Max] will all be invalidated. - if(activeInNr != None): - self._settings['acin%smin' % activeInNr] = min(0, - self._settings['acin%smin' % activeInNr] or float("inf"), - newvalues.get('/Ac/ActiveIn/L1/Current') or float("inf"), - newvalues.get('/Ac/ActiveIn/L2/Current') or float("inf"), - newvalues.get('/Ac/ActiveIn/L3/Current') or float("inf")) - - self._settings['acin%smax' % activeInNr] = max(self._settings['acin%smax' % activeInNr] or 0, - newvalues.get('/Ac/ActiveIn/L1/Current') or 0, - newvalues.get('/Ac/ActiveIn/L2/Current') or 0, - newvalues.get('/Ac/ActiveIn/L3/Current') or 0) - - self._settings['%smax' % activeIn] = max(self._settings['%smax' % activeIn], - newvalues.get('/Ac/Consumption/L1/Current') or 0, - newvalues.get('/Ac/Consumption/L2/Current') or 0, - newvalues.get('/Ac/Consumption/L3/Current') or 0) - - # DC input - self._settings['dcinmax'] = max(self._settings['dcinmax'] or 0, - sum([newvalues.get('/Dc/Charger/Power') or 0, - newvalues.get('/Dc/FuelCell/Power') or 0, - newvalues.get('/Dc/Alternator/Power') or 0])) - - # DC output - self._settings['dcsysmax'] = _safemax(self._settings['dcsysmax'] or 0, - newvalues.get('/Dc/System/Power') or 0) - - # PV power - self._settings['pvmax'] = _safemax(self._settings['pvmax'] or 0, - _safeadd(newvalues.get('/Dc/Pv/Power') or 0, - self._dbusservice['/Ac/PvOnGrid/L1/Power'], - self._dbusservice['/Ac/PvOnGrid/L2/Power'], - self._dbusservice['/Ac/PvOnGrid/L3/Power'], - self._dbusservice['/Ac/PvOnGenset/L1/Power'], - self._dbusservice['/Ac/PvOnGenset/L2/Power'], - self._dbusservice['/Ac/PvOnGenset/L3/Power'], - self._dbusservice['/Ac/PvOnOutput/L1/Power'], - self._dbusservice['/Ac/PvOnOutput/L2/Power'], - self._dbusservice['/Ac/PvOnOutput/L3/Power'])) - - # ==== UPDATE DBUS ITEMS ==== - with self._dbusservice as sss: - for path in self._summeditems.keys(): - # Why the None? Because we want to invalidate things we don't have anymore. - sss[path] = newvalues.get(path, None) - - def _handleservicechange(self): - # Update the available battery monitor services, used to populate the dropdown in the settings. - # Below code makes a dictionary. The key is [dbuserviceclass]/[deviceinstance]. For example - # "battery/245". The value is the name to show to the user in the dropdown. The full dbus- - # servicename, ie 'com.victronenergy.vebus.ttyO1' is not used, since the last part of that is not - # fixed. dbus-serviceclass name and the device instance are already fixed, so best to use those. - - services = self._get_connected_service_list('com.victronenergy.vebus') - services.update(self._get_connected_service_list('com.victronenergy.battery')) - services.update({k: v for k, v in self._get_connected_service_list( - 'com.victronenergy.multi').items() if self._dbusmonitor.get_value(k, '/Soc') is not None}) - services.update({k: v for k, v in self._get_connected_service_list( - 'com.victronenergy.inverter').items() if self._dbusmonitor.get_value(k, '/Soc') is not None}) - - ul = {self.BATSERVICE_DEFAULT: 'Automatic', self.BATSERVICE_NOBATTERY: 'No battery monitor'} - for servicename, instance in services.items(): - key = self._get_instance_service_name(servicename, instance) - ul[key] = self._get_readable_service_name(servicename) - self._dbusservice['/AvailableBatteryServices'] = json.dumps(ul) - - ul = {self.BATSERVICE_DEFAULT: 'Automatic', self.BATSERVICE_NOBATTERY: 'No battery monitor'} - # For later: for device supporting multiple Dc measurement we should add entries for /Dc/1 etc as - # well. - for servicename, instance in services.items(): - key = self._get_instance_service_name(servicename, instance).replace('.', '_').replace('/', '_') + '/Dc/0' - ul[key] = self._get_readable_service_name(servicename) - self._dbusservice['/AvailableBatteryMeasurements'] = ul - - self._determinebatteryservice() - - self._changed = True - - def _get_readable_service_name(self, servicename): - return '%s on %s' % ( - self._dbusmonitor.get_value(servicename, '/ProductName'), - self._dbusmonitor.get_value(servicename, '/Mgmt/Connection')) - - def _get_instance_service_name(self, service, instance): - return '%s/%s' % ('.'.join(service.split('.')[0:3]), instance) - - def _remove_unconnected_services(self, services): - # Workaround: because com.victronenergy.vebus is available even when there is no vebus product - # connected, remove any service that is not connected. Previously we used - # /State since mandatory path /Connected is not implemented in mk2dbus, - # but this has since been resolved. - for servicename in list(services.keys()): - if (self._dbusmonitor.get_value(servicename, '/Connected') != 1 - or self._dbusmonitor.get_value(servicename, '/ProductName') is None - or self._dbusmonitor.get_value(servicename, '/Mgmt/Connection') is None): - del services[servicename] - - def _dbus_value_changed(self, dbusServiceName, dbusPath, dict, changes, deviceInstance): - self._changed = True - - # Workaround because com.victronenergy.vebus is available even when there is no vebus product - # connected. - if (dbusPath in ['/Connected', '/ProductName', '/Mgmt/Connection'] or - (dbusPath == '/State' and dbusServiceName.split('.')[0:3] == ['com', 'victronenergy', 'vebus'])): - self._handleservicechange() - - # Track the timezone changes - if dbusPath == '/Settings/System/TimeZone': - tz = changes.get('Value') - if tz is not None: - os.environ['TZ'] = tz - time.tzset() - - def _device_added(self, service, instance, do_service_change=True): - if do_service_change: - self._handleservicechange() - - for m in self._modules: - m.device_added(service, instance, do_service_change) - - def _device_removed(self, service, instance): - self._handleservicechange() - - for m in self._modules: - m.device_removed(service, instance) - - def _gettext(self, path, value): - item = self._summeditems.get(path) - if item is not None: - try: - gettext = item['gettext'] - except KeyError: - pass - else: - if callable(gettext): - return gettext(value) - return gettext % value - return str(value) - - def _compute_number_of_phases(self, path, newvalues): - number_of_phases = None - for phase in range(1, 4): - p = newvalues.get('%s/L%s/Power' % (path, phase)) - if p is not None: - number_of_phases = phase - newvalues[path + '/NumberOfPhases'] = number_of_phases - - def _get_connected_service_list(self, classfilter=None): - services = self._dbusmonitor.get_service_list(classfilter=classfilter) - self._remove_unconnected_services(services) - return services - - # returns a servicename string - def _get_first_connected_service(self, classfilter): - services = self._get_connected_service_list(classfilter=classfilter) - if len(services) == 0: - return None - return next(iter(services.items()), (None,))[0] - - # returns a tuple (servicename, instance) - def _get_service_having_lowest_instance(self, classfilter=None): - services = self._get_connected_service_list(classfilter=classfilter) - if len(services) == 0: - return None - - # sort the dict by value; returns list of tuples: (value, key) - s = sorted((value, key) for (key, value) in services.items()) - return (s[0][1], s[0][0]) - - -class DbusSystemCalc(SystemCalc): - def _create_dbus_monitor(self, *args, **kwargs): - return DbusMonitor(*args, **kwargs) - - def _create_settings(self, *args, **kwargs): - bus = dbus.SessionBus() if 'DBUS_SESSION_BUS_ADDRESS' in os.environ else dbus.SystemBus() - return SettingsDevice(bus, *args, timeout=10, **kwargs) - - def _create_dbus_service(self): - venusversion, venusbuildtime = self._get_venus_versioninfo() - - dbusservice = VeDbusService('com.victronenergy.system') - dbusservice.add_mandatory_paths( - processname=__file__, - processversion=softwareVersion, - connection='data from other dbus processes', - deviceinstance=0, - productid=None, - productname=None, - firmwareversion=venusversion, - hardwareversion=None, - connected=1) - dbusservice.add_path('/FirmwareBuild', value=venusbuildtime) - return dbusservice - - def _get_venus_versioninfo(self): - try: - with open("/opt/victronenergy/version", "r") as fp: - version, software, buildtime = fp.read().split('\n')[:3] - major, minor, _, rev = re.compile('v([0-9]*)\.([0-9]*)(~([0-9]*))?').match(version).groups() - return (int(major, 16)<<16)+(int(minor, 16)<<8)+(0 if rev is None else int(rev, 16)), buildtime - except Exception: - pass - return 0, '0' - -if __name__ == "__main__": - # Argument parsing - parser = argparse.ArgumentParser( - description='Converts readings from AC-Sensors connected to a VE.Bus device in a pvinverter ' + - 'D-Bus service.' - ) - - parser.add_argument("-d", "--debug", help="set logging level to debug", - action="store_true") - - args = parser.parse_args() - - print("-------- dbus_systemcalc, v" + softwareVersion + " is starting up --------") - logger = setup_logging(args.debug) - - # Have a mainloop, so we can send/receive asynchronous calls to and from dbus - DBusGMainLoop(set_as_default=True) - - systemcalc = DbusSystemCalc() - - # Start and run the mainloop - logger.info("Starting mainloop, responding only on events") - mainloop = GLib.MainLoop() - mainloop.run() diff --git a/FileSets/PatchSource/obsoletePatches/main.qml-v2.94 b/FileSets/PatchSource/obsoletePatches/main.qml-v2.94 deleted file mode 100644 index 8afb44bb..00000000 --- a/FileSets/PatchSource/obsoletePatches/main.qml-v2.94 +++ /dev/null @@ -1,563 +0,0 @@ -//////// Modified to hide the OverviewTiles page -//////// Modified to substitute flow overview pages - -import QtQuick 1.1 - -import Qt.labs.components.native 1.0 -import com.victron.velib 1.0 -import "utils.js" as Utils - -PageStackWindow { - id: rootWindow - - gpsConnected: gpsFix.value === 1 - onCompletedChanged: checkAlarm() - initialPage: PageMain {} - - property VeQuickItem gpsService: VeQuickItem { uid: "dbus/com.victronenergy.system/GpsService" } - property VeQuickItem gpsFix: VeQuickItem { uid: Utils.path("dbus/", gpsService.value, "/Fix") } - property bool completed: false - property bool showAlert: NotificationCenter.alert - property bool alarm: NotificationCenter.alarm -//////// added for GuiMods flow pages - property bool overviewsLoaded: defaultOverview.valid && generatorOverview.valid && mobileOverview.valid && startWithMenu.valid && mobileOverviewEnhanced.valid && guiModsFlowOverview.valid && generatorOverviewEnhanced.valid - property string bindPrefix: "com.victronenergy.settings" - - property bool isNotificationPage: pageStack.currentPage && pageStack.currentPage.title === qsTr("Notifications") - property bool isOverviewPage: pageStack.currentPage && pageStack.currentPage.model === overviewModel; - property bool isOfflineFwUpdatePage: pageStack.currentPage && pageStack.currentPage.objectName === "offlineFwUpdatePage"; - -//////// modified for GuiMods pages - property string hubOverviewType: theSystem.systemType.valid ? - withoutGridMeter.value === 1 ? "Hub" : theSystem.systemType.value : "unknown" - property string currentHubOverview: "OverviewHub.qml" - property string currentMobileOverview: "" - property string currentGeneratorOverview: "" - - // Keep track of the current view (menu/overview) to show as default next time the - // CCGX is restarted - onIsOverviewPageChanged: startWithMenu.setValue(isOverviewPage ? 0 : 1) - - // Add the correct OverviewGridParallelEnhanced page -//////// modified for OverviewHubEnhanced page - onHubOverviewTypeChanged: selectHubOverview () - - VBusItem - { - id: guiModsFlowOverview - bind: "com.victronenergy.settings/Settings/GuiMods/FlowOverview" - onValueChanged: selectHubOverview () - } - -////// GuiMods — DarkMode - property VBusItem darkModeItem: VBusItem { bind: "com.victronenergy.settings/Settings/GuiMods/DarkMode" } - property bool darkMode: darkModeItem.valid && darkModeItem.value == 1 - -////// GuiMods — DarkMode - Rectangle { - anchors - { - fill: parent - } - color: !darkMode ? "transparent" : "#202020" - z: -1 - } - - // base a new hub selection on the hub type and the enhanced flow overview flag - function selectHubOverview () - { - var newHubOverview = currentHubOverview - // Victron stock overviews with automatic selection - if (guiModsFlowOverview.value == 0) - { - switch(hubOverviewType){ - case "Hub": - case "Hub-1": - case "Hub-2": - case "Hub-3": - case "unknown": - newHubOverview = "OverviewHub.qml" - break; - case "Hub-4": - case "ESS": - newHubOverview = "OverviewGridParallel.qml" - break; - default: - break; - } - } - // Gui Mods simple flow - else if (guiModsFlowOverview.value === 1) - { - newHubOverview = "OverviewHubEnhanced.qml" - } - // Gui Mods complex flow (AC coupled or DC coupled) - else - { - newHubOverview = "OverviewFlowComplex.qml" - } - - if (newHubOverview != currentHubOverview) - { - replaceOverview(currentHubOverview, newHubOverview); - currentHubOverview = newHubOverview - } - - // Workaround the QTBUG-17012 (only the first sentence in each case of Switch Statement can be executed) - // by adding a return statement - return - } - - VBusItem { - id: generatorOverview - bind: "com.victronenergy.settings/Settings/Relay/Function" - onValueChanged: selectGeneratorOverview () - } - - VBusItem - { - id: generatorOverviewEnhanced - bind: "com.victronenergy.settings/Settings/GuiMods/UseEnhancedGeneratorOverview" - onValueChanged: selectGeneratorOverview () - } - - VBusItem { - id: fischerPandaGenOverview - bind: "com.victronenergy.generator.startstop1/AutoStartEnabled" - onValueChanged: { - extraOverview("OverviewGeneratorFp.qml", value === 1) - // Switch to FP overview in case it is the default one - if (isOverviewPage) { - pageStack.currentPage.currentIndex = getDefaultOverviewIndex() - } - } - } - function selectGeneratorOverview () - { - var newGeneratorOverview - if (generatorOverview.value === 1) - { - if (generatorOverviewEnhanced.value === 1) - newGeneratorOverview = "OverviewGeneratorRelayEnhanced.qml" - else - newGeneratorOverview = "OverviewGeneratorRelay.qml" - if (currentGeneratorOverview === "") - extraOverview (newGeneratorOverview, true) - else - replaceOverview (currentGeneratorOverview, newGeneratorOverview) - currentGeneratorOverview = newGeneratorOverview - } - else - { - // hide existing generator overview if any - if (currentGeneratorOverview != "") - { - extraOverview (currentGeneratorOverview, false) - currentGeneratorOverview = "" - } - } - } - -//////// handle OverviewMobileEnhanced page - VBusItem - { - id: mobileOverview - bind: "com.victronenergy.settings/Settings/Gui/MobileOverview" - onValueChanged: selectMobileOverview () - } - VBusItem - { - id: mobileOverviewEnhanced - bind: "com.victronenergy.settings/Settings/GuiMods/UseEnhancedMobileOverview" - onValueChanged: selectMobileOverview () - } - - // base a new mobile overview selection on the the mobile overview and enhanced mobile overview flags - function selectMobileOverview () - { - var newMobileOverview - if (mobileOverview.value === 1) - { - if (mobileOverviewEnhanced.value === 1) - newMobileOverview = "OverviewMobileEnhanced.qml" - else - newMobileOverview = "OverviewMobile.qml" - if (currentMobileOverview === "") - extraOverview (newMobileOverview, true) - else - replaceOverview (currentMobileOverview, newMobileOverview) - currentMobileOverview = newMobileOverview - } - else - { - // hide existing mobile overview if any - if (currentMobileOverview != "") - { - extraOverview (currentMobileOverview, false) - currentMobileOverview = "" - } - } - } - -//////// show/hide the OverviewTiles page - VBusItem - { - id: showOverviewTiles - bind: "com.victronenergy.settings/Settings/GuiMods/ShowTileOverview" - onValueChanged: extraOverview ("OverviewTiles.qml", value === 1) - } - -//////// show/hide the OverviewRelays page - VBusItem { - id: showOverviewRelays - 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 - bind: "com.victronenergy.settings/Settings/GuiMods/ShowTanksTempsDigIn" - onValueChanged: extraOverview ("OverviewTanksTempsDigInputs.qml", value === 1) - } - - VBusItem { - id: tanksOverview - bind: "com.victronenergy.settings/Settings/Gui/TanksOverview" - onValueChanged:{ - extraOverview("OverviewTanks.qml", value === 1) - } - } - - VBusItem { - id: startWithMenu - bind: "com.victronenergy.settings/Settings/Gui/StartWithMenuView" - } - - VBusItem { - id: withoutGridMeter - bind: "com.victronenergy.settings/Settings/CGwacs/RunWithoutGridMeter" - } - - - VBusItem { - id: defaultOverview - bind: "com.victronenergy.settings/Settings/Gui/DefaultOverview" - } - - // 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. - Connections { - target: storageEvents - onVrmStorageError: { - if (error === 4) { - setTopPage(offlineFwUpdates) - } - } - } - - onAlarmChanged: { - if (completed) - checkAlarm() - } - - // always keep track of system information - HubData { - id: theSystem - } - - // note: used for leaving the overviews as well - function backToMainMenu() - { - pageStack.pop(initialPage); - } - - Toast { - id: toast - transform: Scale { - xScale: screen.scaleX - yScale: screen.scaleY - origin.x: toast.width / 2 - origin.y: toast.height / 2 - } - } - - SignalToaster {} - - ToolbarHandlerPages { - id: mainToolbarHandler - isDefault: true - } - - ToolBarLayout { - id: mbTools - height: parent.height - -//// GuiMods - DarkMode - Row - { - spacing: 0 - anchors.fill: parent - Item { - id: pagesItem - anchors.verticalCenter: parent.verticalCenter - height: mbTools.height - width: 170 - - MouseArea { - anchors.fill: parent - onClicked: { - if (pageStack.currentPage) - pageStack.currentPage.toolbarHandler.leftAction(true) - } - } - - Row { - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - - MbIcon { - anchors.verticalCenter: parent.verticalCenter - iconId: pageStack.currentPage ? pageStack.currentPage.leftIcon : "" - } - - Text { - anchors.verticalCenter: parent.verticalCenter - text: pageStack.currentPage ? pageStack.currentPage.leftText : "" - color: "white" - font.bold: true - font.pixelSize: 16 - } - } - } - - Item { - anchors.verticalCenter: parent.verticalCenter - height: mbTools.height - width: mbTools.width - pagesItem.width - menusItem.width - centerScrollIndicator.width - - MouseArea - { - anchors.fill: parent - onClicked: - { - if (darkModeItem.valid) - darkModeItem.setValue (! darkMode) - } - } - - Text - { - anchors.fill: parent - horizontalAlignment: Text.AlignHCenter - text: qsTr ("change to") + "\n" + (darkMode ? qsTr ("Light mode") : qsTr ("Dark mode")) - color: "white" - font.bold: true - font.pixelSize: 12 - visible: darkModeItem.valid - } - } - Item - { - id: centerScrollIndicator - anchors.verticalCenter: parent.verticalCenter - height: mbTools.height - width: 20 - MbIcon { - anchors.verticalCenter: parent.verticalCenter - iconId: pageStack.currentPage ? pageStack.currentPage.scrollIndicator : "" - } - } - - Item { - id: menusItem - anchors.verticalCenter: parent.verticalCenter - height: mbTools.height - width: pagesItem.width - - MouseArea { - anchors.fill: parent - onClicked: { - if (pageStack.currentPage) - pageStack.currentPage.toolbarHandler.rightAction(true) - } - } - - Row { - anchors.centerIn: parent - - MbIcon { - iconId: pageStack.currentPage ? pageStack.currentPage.rightIcon : "" - anchors.verticalCenter: parent.verticalCenter - } - - Text { - text: pageStack.currentPage ? pageStack.currentPage.rightText : "" - anchors.verticalCenter: parent.verticalCenter - color: "white" - font.bold: true - font.pixelSize: 16 - } - } - } - } - } - - Component.onCompleted: { - completed = true - } - - ListModel { - id: overviewModel - ListElement { - pageSource: "OverviewHub.qml" - } -//////// (commented out) -- added dynamically above -// ListElement { -// pageSource: "OverviewTiles.qml" -// } - } - - Component { - id: overviewComponent - PageFlow { - // Display default overview when loaded - defaultIndex: getDefaultOverviewIndex() - // Store the current overview page as default - onCurrentIndexChanged: if (active) defaultOverview.setValue(overviewModel.get(currentIndex).pageSource.replace(".qml", "")) - model: overviewModel - } - } - - // When all the related settings items are valid, show the overview page if was the last oppened page - // before restarting - Timer { - interval: 2000 - running: completed && overviewsLoaded && startWithMenu.valid - onTriggered: - { -//////// modified for OverviewGridParallelEnhanced page - selectHubOverview () - if (startWithMenu.value === 0) showOverview() - } - } - - function getDefaultOverviewIndex() - { - if(!defaultOverview.valid) - return 0 - for (var i = 0; i < overviewModel.count; i++){ - if (overviewModel.get(i).pageSource.replace(".qml", "") === defaultOverview.value) { - return i - } - } - return 0 - } - - Component { - id: noticationsComponent - PageNotifications {} - } - - Component { - id: offlineFwUpdates - PageSettingsFirmwareOffline { checkOnCompleted: true} - - } - - // Add or remove extra overviews. for example, generator overview - // shouldn't be shown if the start/stop functionality is not enabled. - // Index parameter is optional, usefull to keep an order. - function extraOverview(name, show, index) - { - var i = 0 - if (show) { - if (index !== undefined) { - if (overviewModel.get(index).pageSource === name) - return - // First append the page - overviewModel.append({"pageSource": name}) - // Then move all the pages behind index - overviewModel.move(index, overviewModel.count - 2, overviewModel.count - 2) - } else { - for (i = 0; i < overviewModel.count; i++) - if (overviewModel.get(i).pageSource === name) - // Don't append if already exists - return - overviewModel.append({"pageSource": name}) - } - } else { - for (i = 0; i < overviewModel.count; i++) - if (overviewModel.get(i).pageSource === name) - overviewModel.remove(i) - } - } - -//////// Modified to append page if oldPage not found - function replaceOverview(oldPage, newPage) - { - for (var i = 0; i < overviewModel.count; i++) - { - if (overviewModel.get(i).pageSource === oldPage) - { - overviewModel.get(i).pageSource = newPage - return - } - } - // here if oldPage wasn't found -- append the new page - overviewModel.append({"pageSource": newPage}) - } - - // Central mover for the ball animation on the overviews - // Instead of using a timer per line, using a central one - // reduces the CPU usage a little bit and makes the animations - // smoother. - Timer { - id: mover - property double pos: _counter / _loops - property int _counter - property int _loops: 13 - - interval: 100 - running: true - repeat: true - onTriggered: if (_counter >= (_loops - 1)) _counter = 0; else _counter++ - } - - // If an overview or notifications is active, the new page will replace it - // instead to be pushed. This way we prevent an unwanted stackpage depth - // increment everytime another page wants to be on top. - function setTopPage(page) - { - if (isNotificationPage || isOverviewPage || isOfflineFwUpdatePage) - rootWindow.pageStack.replace(page); - else - rootWindow.pageStack.push(page); - } - - function spuriousKeyPress() - { - return !pageStack.currentPage || !pageStack.currentPage.active - } - - function showOverview() - { - if (spuriousKeyPress() || isOverviewPage) - return - setTopPage(overviewComponent) - } - - function showPageNotifications() - { - if (spuriousKeyPress() || isNotificationPage) - return - setTopPage(noticationsComponent) - } - - function checkAlarm() - { - if (alarm) - showPageNotifications() - } -} diff --git a/FileSets/PatchSource/obsoletePatches/main.qml-v2.94.orig b/FileSets/PatchSource/obsoletePatches/main.qml-v2.94.orig deleted file mode 100644 index 1d744d9d..00000000 --- a/FileSets/PatchSource/obsoletePatches/main.qml-v2.94.orig +++ /dev/null @@ -1,370 +0,0 @@ -import QtQuick 1.1 - -import Qt.labs.components.native 1.0 -import com.victron.velib 1.0 -import "utils.js" as Utils - -PageStackWindow { - id: rootWindow - - gpsConnected: gpsFix.value === 1 - onCompletedChanged: checkAlarm() - initialPage: PageMain {} - - property VeQuickItem gpsService: VeQuickItem { uid: "dbus/com.victronenergy.system/GpsService" } - property VeQuickItem gpsFix: VeQuickItem { uid: Utils.path("dbus/", gpsService.value, "/Fix") } - property bool completed: false - property bool showAlert: NotificationCenter.alert - property bool alarm: NotificationCenter.alarm - property bool overviewsLoaded: defaultOverview.valid && generatorOverview.valid && mobileOverview.valid && tanksOverview.valid && startWithMenu.valid - property string bindPrefix: "com.victronenergy.settings" - - property bool isNotificationPage: pageStack.currentPage && pageStack.currentPage.title === qsTr("Notifications") - property bool isOverviewPage: pageStack.currentPage && pageStack.currentPage.model === overviewModel; - property bool isOfflineFwUpdatePage: pageStack.currentPage && pageStack.currentPage.objectName === "offlineFwUpdatePage"; - - - property string hubOverviewType: theSystem.systemType.valid ? - withoutGridMeter.value === 1 ? "Hub" : theSystem.systemType.value : "" - - // Keep track of the current view (menu/overview) to show as default next time the - // CCGX is restarted - onIsOverviewPageChanged: startWithMenu.setValue(isOverviewPage ? 0 : 1) - - // Add the correct OverviewHub page - onHubOverviewTypeChanged: { - switch(hubOverviewType){ - case "Hub": - case "Hub-1": - case "Hub-2": - case "Hub-3": - replaceOverview("OverviewGridParallel.qml", "OverviewHub.qml"); - break; - case "Hub-4": - case "ESS": - replaceOverview("OverviewHub.qml", "OverviewGridParallel.qml"); - break; - default: - break; - } - // Workaround the QTBUG-17012 (only the first sentence in each case of Switch Statement can be executed) - // by adding a return statement - return - } - - VBusItem { - id: generatorOverview - bind: "com.victronenergy.settings/Settings/Relay/Function" - onValueChanged: extraOverview("OverviewGeneratorRelay.qml", value === 1) - } - - VBusItem { - id: fischerPandaGenOverview - bind: "com.victronenergy.generator.startstop1/AutoStartEnabled" - onValueChanged: { - extraOverview("OverviewGeneratorFp.qml", value === 1) - // Switch to FP overview in case it is the default one - if (isOverviewPage) { - pageStack.currentPage.currentIndex = getDefaultOverviewIndex() - } - } - } - - VBusItem { - id: mobileOverview - bind: "com.victronenergy.settings/Settings/Gui/MobileOverview" - onValueChanged:{ - extraOverview("OverviewMobile.qml", value === 1) - } - } - VBusItem { - id: tanksOverview - bind: "com.victronenergy.settings/Settings/Gui/TanksOverview" - onValueChanged:{ - extraOverview("OverviewTanks.qml", value === 1) - } - } - - VBusItem { - id: startWithMenu - bind: "com.victronenergy.settings/Settings/Gui/StartWithMenuView" - } - - VBusItem { - id: withoutGridMeter - bind: "com.victronenergy.settings/Settings/CGwacs/RunWithoutGridMeter" - } - - - VBusItem { - id: defaultOverview - bind: "com.victronenergy.settings/Settings/Gui/DefaultOverview" - } - - // 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. - Connections { - target: storageEvents - onVrmStorageError: { - if (error === 4) { - setTopPage(offlineFwUpdates) - } - } - } - - onAlarmChanged: { - if (completed) - checkAlarm() - } - - // always keep track of system information - HubData { - id: theSystem - } - - // note: used for leaving the overviews as well - function backToMainMenu() - { - pageStack.pop(initialPage); - } - - Toast { - id: toast - transform: Scale { - xScale: screen.scaleX - yScale: screen.scaleY - origin.x: toast.width / 2 - origin.y: toast.height / 2 - } - } - - SignalToaster {} - - ToolbarHandlerPages { - id: mainToolbarHandler - isDefault: true - } - - ToolBarLayout { - id: mbTools - height: parent.height - - Item { - anchors.verticalCenter: parent.verticalCenter - anchors.left: mbTools.left - height: mbTools.height - width: 200 - - MouseArea { - anchors.fill: parent - onClicked: { - if (pageStack.currentPage) - pageStack.currentPage.toolbarHandler.leftAction(true) - } - } - - Row { - anchors.centerIn: parent - - MbIcon { - anchors.verticalCenter: parent.verticalCenter - iconId: pageStack.currentPage ? pageStack.currentPage.leftIcon : "" - } - - Text { - anchors.verticalCenter: parent.verticalCenter - text: pageStack.currentPage ? pageStack.currentPage.leftText : "" - color: "white" - font.bold: true - font.pixelSize: 16 - } - } - } - - MbIcon { - id: centerScrollIndicator - - anchors { - horizontalCenter: parent.horizontalCenter - verticalCenter: mbTools.verticalCenter - } - iconId: pageStack.currentPage ? pageStack.currentPage.scrollIndicator : "" - } - - Item { - anchors.verticalCenter: parent.verticalCenter - height: mbTools.height - anchors.right: mbTools.right - width: 200 - - MouseArea { - anchors.fill: parent - onClicked: { - if (pageStack.currentPage) - pageStack.currentPage.toolbarHandler.rightAction(true) - } - } - - Row { - anchors.centerIn: parent - - MbIcon { - iconId: pageStack.currentPage ? pageStack.currentPage.rightIcon : "" - anchors.verticalCenter: parent.verticalCenter - } - - Text { - text: pageStack.currentPage ? pageStack.currentPage.rightText : "" - anchors.verticalCenter: parent.verticalCenter - color: "white" - font.bold: true - font.pixelSize: 16 - } - } - } - } - - Component.onCompleted: { - completed = true - } - - ListModel { - id: overviewModel - ListElement { - pageSource: "OverviewHub.qml" - } - ListElement { - pageSource: "OverviewTiles.qml" - } - } - - Component { - id: overviewComponent - PageFlow { - // Display default overview when loaded - defaultIndex: getDefaultOverviewIndex() - // Store the current overview page as default - onCurrentIndexChanged: if (active) defaultOverview.setValue(overviewModel.get(currentIndex).pageSource.replace(".qml", "")) - model: overviewModel - } - } - - // When all the related settings items are valid, show the overview page if was the last oppened page - // before restarting - Timer { - interval: 2000 - running: completed && overviewsLoaded && startWithMenu.valid - onTriggered: if (startWithMenu.value === 0) showOverview() - } - - function getDefaultOverviewIndex() - { - if(!defaultOverview.valid) - return 0 - for (var i = 0; i < overviewModel.count; i++){ - if (overviewModel.get(i).pageSource.replace(".qml", "") === defaultOverview.value) { - return i - } - } - return 0 - } - - Component { - id: noticationsComponent - PageNotifications {} - } - - Component { - id: offlineFwUpdates - PageSettingsFirmwareOffline { checkOnCompleted: true} - - } - - // Add or remove extra overviews. for example, generator overview - // shouldn't be shown if the start/stop functionality is not enabled. - // Index parameter is optional, usefull to keep an order. - function extraOverview(name, show, index) - { - var i = 0 - if (show) { - if (index !== undefined) { - if (overviewModel.get(index).pageSource === name) - return - // First append the page - overviewModel.append({"pageSource": name}) - // Then move all the pages behind index - overviewModel.move(index, overviewModel.count - 2, overviewModel.count - 2) - } else { - for (i = 0; i < overviewModel.count; i++) - if (overviewModel.get(i).pageSource === name) - // Don't append if already exists - return - overviewModel.append({"pageSource": name}) - } - } else { - for (i = 0; i < overviewModel.count; i++) - if (overviewModel.get(i).pageSource === name) - overviewModel.remove(i) - } - } - - function replaceOverview(oldPage, newPage) - { - for (var i = 0; i < overviewModel.count; i++) - if (overviewModel.get(i).pageSource === oldPage) - overviewModel.get(i).pageSource = newPage - } - - // Central mover for the ball animation on the overviews - // Instead of using a timer per line, using a central one - // reduces the CPU usage a little bit and makes the animations - // smoother. - Timer { - id: mover - property double pos: _counter / _loops - property int _counter - property int _loops: 13 - - interval: 100 - running: true - repeat: true - onTriggered: if (_counter >= (_loops - 1)) _counter = 0; else _counter++ - } - - // If an overview or notifications is active, the new page will replace it - // instead to be pushed. This way we prevent an unwanted stackpage depth - // increment everytime another page wants to be on top. - function setTopPage(page) - { - if (isNotificationPage || isOverviewPage || isOfflineFwUpdatePage) - rootWindow.pageStack.replace(page); - else - rootWindow.pageStack.push(page); - } - - function spuriousKeyPress() - { - return !pageStack.currentPage || !pageStack.currentPage.active - } - - function showOverview() - { - if (spuriousKeyPress() || isOverviewPage) - return - setTopPage(overviewComponent) - } - - function showPageNotifications() - { - if (spuriousKeyPress() || isNotificationPage) - return - setTopPage(noticationsComponent) - } - - function checkAlarm() - { - if (alarm) - showPageNotifications() - } -} diff --git a/FileSets/v3.50/PageGenerator.qml.USE_ORIGINAL b/FileSets/v3.50/PageGenerator.qml.USE_ORIGINAL new file mode 100644 index 00000000..e69de29b diff --git a/changes b/changes index 8fd0bba0..2ff2e304 100644 --- a/changes +++ b/changes @@ -1,3 +1,7 @@ +v10.71: + fixed: white screen (GUI crash) + fixed: light/dark selection not shown at bottom of screen + v10.70: fixed: PackageManager crash (bad GuiMods version string) diff --git a/version b/version index bf61f98b..851e283c 100644 --- a/version +++ b/version @@ -1 +1 @@ -v10.70 +v10.71