├── .github └── workflows │ └── latest-tag.yml ├── CommonResources ├── FileSets ├── PatchSource │ ├── PageSettings.qml │ ├── PageSettings.qml.orig │ └── PageSettings.qml.patch ├── VersionIndependent │ ├── MbDisplayDefaultPackage.qml │ ├── MbDisplayPackageVersion.qml │ ├── PageSettingsAddPackageList.qml │ ├── PageSettingsPackageAdd.qml │ ├── PageSettingsPackageEdit.qml │ ├── PageSettingsPackageManager.qml │ ├── PageSettingsPackageVersions.qml │ ├── PageSettingsPmBackup.qml │ └── PageSettingsPmInitialize.qml ├── fileListPatched └── fileListVersionIndependent ├── HelperResources ├── CommonResources ├── DbusSettingsResources ├── EssentialResources ├── IncludeHelpers ├── LogHandler ├── ServiceResources ├── VersionResources └── version ├── PackageDevelopmentGuidelines.md ├── PackageManager.py ├── ReadMe.md ├── blindInstall ├── SetupHelperVersion ├── blindInstall.sh ├── post-hook.sh ├── pre-hook.sh ├── rcS.local └── rcS.localForUninstall ├── changes ├── defaultPackageList ├── forSetupScript ├── genericSetupScript ├── gitHubInfo ├── makeVelib_python ├── packageManagerEnd.sh ├── patch ├── rcS.local ├── reinstallMods ├── services └── PackageManager │ ├── log │ └── run │ └── run ├── settingsList ├── setup ├── updatePackage ├── velib_python ├── dbusmonitor.py ├── oldestVersion ├── settingsdevice.py ├── ve_utils.py ├── vedbus.py └── velib_python │ ├── latest │ ├── dbusmonitor.py │ ├── oldestVersion │ ├── settingsdevice.py │ ├── ve_utils.py │ └── vedbus.py │ ├── v3.34 │ ├── dbusmonitor.py │ ├── oldestVersion │ ├── settingsdevice.py │ ├── ve_utils.py │ └── vedbus.py │ └── v3.41 │ ├── dbusmonitor.py │ ├── oldestVersion │ ├── settingsdevice.py │ ├── ve_utils.py │ └── vedbus.py ├── venus-data-UninstallPackages.tgz ├── venus-data.tgz └── version /.github/workflows/latest-tag.yml: -------------------------------------------------------------------------------- 1 | name: Add latest tag to new release 2 | on: 3 | release: 4 | types: [published] 5 | workflow_dispatch: 6 | 7 | jobs: 8 | run: 9 | name: Run local action 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout repository 13 | uses: actions/checkout@master 14 | 15 | - name: Run latest-tag 16 | uses: EndBug/latest-tag@v1 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | -------------------------------------------------------------------------------- /CommonResources: -------------------------------------------------------------------------------- 1 | HelperResources/IncludeHelpers -------------------------------------------------------------------------------- /FileSets/PatchSource/PageSettings.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 1.1 2 | import com.victron.velib 1.0 3 | import net.connman 0.1 4 | import "utils.js" as Utils 5 | 6 | MbPage { 7 | title: qsTr("Settings") 8 | property string bindPrefix: "com.victronenergy.settings" 9 | property VBusItem relay0Item: VBusItem {bind: "com.victronenergy.system/Relay/0/State"} 10 | property bool hasRelay0: relay0Item.valid 11 | 12 | model: VisibleItemModel { 13 | MbSubMenu { 14 | id: generalItem 15 | description: qsTr("General") 16 | subpage: Component { 17 | PageSettingsGeneral { 18 | title: generalItem.description 19 | } 20 | } 21 | } 22 | 23 | MbSubMenu { 24 | description: qsTr("Firmware") 25 | subpage: Component { 26 | PageSettingsFirmware { 27 | title: qsTr("Firmware") 28 | } 29 | } 30 | } 31 | 32 | MbSubMenu { 33 | description: qsTr("Date & Time") 34 | subpage: Component { 35 | PageTzInfo { 36 | title: qsTr("Date & Time") 37 | } 38 | } 39 | } 40 | 41 | MbSubMenu { 42 | description: qsTr("Remote Console") 43 | subpage: Component { PageSettingsRemoteConsole {} } 44 | } 45 | 46 | MbSubMenu { 47 | id: systemSetupItem 48 | description: qsTr("System setup") 49 | subpage: Component { 50 | PageSettingsSystem { 51 | title: systemSetupItem.description 52 | } 53 | } 54 | } 55 | 56 | MbSubMenu { 57 | id: dvcc 58 | description: qsTr("DVCC") 59 | subpage: Component { 60 | PageSettingsDVCC { 61 | title: dvcc.description 62 | } 63 | } 64 | } 65 | 66 | MbSubMenu { 67 | id: displayItem 68 | description: qsTr("Display & language") 69 | subpage: Component { 70 | PageSettingsDisplay { 71 | title: displayItem.description 72 | } 73 | } 74 | } 75 | 76 | MbSubMenu { 77 | id: vrmLoggerItem 78 | description: qsTr("VRM online portal") 79 | subpage: Component { 80 | PageSettingsLogger { 81 | title: vrmLoggerItem.description 82 | } 83 | } 84 | } 85 | 86 | MbSubMenu { 87 | VBusItem { 88 | id: systemType 89 | bind: "com.victronenergy.system/SystemType" 90 | } 91 | description: systemType.value === "Hub-4" ? systemType.value : qsTr("ESS") 92 | subpage: Component { PageSettingsHub4 {} } 93 | } 94 | 95 | MbSubMenu { 96 | description: qsTr("Energy meters") 97 | subpage: Component { PageSettingsCGwacsOverview {} } 98 | } 99 | 100 | MbSubMenu { 101 | description: qsTr("PV inverters") 102 | subpage: Component { PageSettingsFronius {} } 103 | } 104 | 105 | MbSubMenu { 106 | show: App.withQwacs 107 | description: qsTr("Wireless AC sensors") 108 | subpage: Component { PageSettingsQwacs {} } 109 | } 110 | 111 | MbSubMenu { 112 | description: qsTr("Modbus TCP/UDP devices") 113 | subpage: Component { PageSettingsModbus {} } 114 | } 115 | 116 | MbSubMenu { 117 | id: ethernetItem 118 | description: qsTr("Ethernet") 119 | subpage: Component { PageSettingsTcpIp { showLinkLocal: true } } 120 | } 121 | 122 | MbSubMenu { 123 | description: qsTr("Wi-Fi") 124 | property VeQuickItem accessPoint: VeQuickItem { uid: "dbus/com.victronenergy.platform/Services/AccessPoint/Enabled" } 125 | subpage: accessPoint.value !== undefined ? wifiWithAP : wifiWithoutAP 126 | Component { id: wifiWithoutAP; PageSettingsWifi {} } 127 | Component { id: wifiWithAP; PageSettingsWifiWithAccessPoint {} } 128 | } 129 | 130 | MbSubMenu { 131 | description: qsTr("GSM modem") 132 | subpage: Component { PageSettingsGsm {} } 133 | } 134 | 135 | MbSubMenu { 136 | description: qsTr("Bluetooth") 137 | subpage: Component { PageSettingsBluetooth {} } 138 | show: Connman.technologyList.indexOf("bluetooth") !== -1 139 | } 140 | 141 | MbSubMenu { 142 | description: qsTr("GPS") 143 | subpage: Component { PageSettingsGpsList {} } 144 | } 145 | 146 | MbSubMenu { 147 | description: qsTr("Generator start/stop") 148 | subpage: Component { PageRelayGenerator {} } 149 | show: hasRelay0 150 | } 151 | 152 | MbSubMenu { 153 | description: qsTr("Tank pump") 154 | subpage: Component { PageSettingsTankPump {} } 155 | } 156 | 157 | MbSubMenu { 158 | description: qsTr("Relay") 159 | subpage: Component { PageSettingsRelay {} } 160 | show: hasRelay0 161 | } 162 | 163 | MbSubMenu { 164 | description: qsTr("Services") 165 | subpage: Component { PageSettingsServices {} } 166 | } 167 | 168 | MbSubMenu { 169 | description: qsTr("I/O") 170 | subpage: ioSettings 171 | show: ioSettings.haveSubMenus 172 | PageSettingsIo { id: ioSettings } 173 | } 174 | 175 | /* 176 | MbSubMenu { 177 | description: qsTr("Backup & Restore") 178 | subpage: Component { PageSettingsBackup {} } 179 | } 180 | */ 181 | 182 | MbSubMenu { 183 | description: qsTr("Venus OS Large features") 184 | subpage: Component { PageSettingsLarge {} } 185 | property VBusItem signalK: VBusItem { bind: "com.victronenergy.platform/Services/SignalK/Enabled" } 186 | property VBusItem nodeRed: VBusItem { bind: "com.victronenergy.platform/Services/NodeRed/Mode" } 187 | show: signalK.valid || nodeRed.valid 188 | } 189 | 190 | MbSubMenu { 191 | description: "Debug" 192 | subpage: Component { PageDebug {} } 193 | showAccessLevel: User.AccessService 194 | } 195 | //////// added for PackageManager 196 | MbSubMenu 197 | { 198 | description: qsTr("Package manager") 199 | subpage: Component { PageSettingsPackageManager {} } 200 | } 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /FileSets/PatchSource/PageSettings.qml.orig: -------------------------------------------------------------------------------- 1 | import QtQuick 1.1 2 | import com.victron.velib 1.0 3 | import net.connman 0.1 4 | import "utils.js" as Utils 5 | 6 | MbPage { 7 | title: qsTr("Settings") 8 | property string bindPrefix: "com.victronenergy.settings" 9 | property VBusItem relay0Item: VBusItem {bind: "com.victronenergy.system/Relay/0/State"} 10 | property bool hasRelay0: relay0Item.valid 11 | 12 | model: VisibleItemModel { 13 | MbSubMenu { 14 | id: generalItem 15 | description: qsTr("General") 16 | subpage: Component { 17 | PageSettingsGeneral { 18 | title: generalItem.description 19 | } 20 | } 21 | } 22 | 23 | MbSubMenu { 24 | description: qsTr("Firmware") 25 | subpage: Component { 26 | PageSettingsFirmware { 27 | title: qsTr("Firmware") 28 | } 29 | } 30 | } 31 | 32 | MbSubMenu { 33 | description: qsTr("Date & Time") 34 | subpage: Component { 35 | PageTzInfo { 36 | title: qsTr("Date & Time") 37 | } 38 | } 39 | } 40 | 41 | MbSubMenu { 42 | description: qsTr("Remote Console") 43 | subpage: Component { PageSettingsRemoteConsole {} } 44 | } 45 | 46 | MbSubMenu { 47 | id: systemSetupItem 48 | description: qsTr("System setup") 49 | subpage: Component { 50 | PageSettingsSystem { 51 | title: systemSetupItem.description 52 | } 53 | } 54 | } 55 | 56 | MbSubMenu { 57 | id: dvcc 58 | description: qsTr("DVCC") 59 | subpage: Component { 60 | PageSettingsDVCC { 61 | title: dvcc.description 62 | } 63 | } 64 | } 65 | 66 | MbSubMenu { 67 | id: displayItem 68 | description: qsTr("Display & language") 69 | subpage: Component { 70 | PageSettingsDisplay { 71 | title: displayItem.description 72 | } 73 | } 74 | } 75 | 76 | MbSubMenu { 77 | id: vrmLoggerItem 78 | description: qsTr("VRM online portal") 79 | subpage: Component { 80 | PageSettingsLogger { 81 | title: vrmLoggerItem.description 82 | } 83 | } 84 | } 85 | 86 | MbSubMenu { 87 | VBusItem { 88 | id: systemType 89 | bind: "com.victronenergy.system/SystemType" 90 | } 91 | description: systemType.value === "Hub-4" ? systemType.value : qsTr("ESS") 92 | subpage: Component { PageSettingsHub4 {} } 93 | } 94 | 95 | MbSubMenu { 96 | description: qsTr("Energy meters") 97 | subpage: Component { PageSettingsCGwacsOverview {} } 98 | } 99 | 100 | MbSubMenu { 101 | description: qsTr("PV inverters") 102 | subpage: Component { PageSettingsFronius {} } 103 | } 104 | 105 | MbSubMenu { 106 | show: App.withQwacs 107 | description: qsTr("Wireless AC sensors") 108 | subpage: Component { PageSettingsQwacs {} } 109 | } 110 | 111 | MbSubMenu { 112 | description: qsTr("Modbus TCP/UDP devices") 113 | subpage: Component { PageSettingsModbus {} } 114 | } 115 | 116 | MbSubMenu { 117 | id: ethernetItem 118 | description: qsTr("Ethernet") 119 | subpage: Component { PageSettingsTcpIp { showLinkLocal: true } } 120 | } 121 | 122 | MbSubMenu { 123 | description: qsTr("Wi-Fi") 124 | property VeQuickItem accessPoint: VeQuickItem { uid: "dbus/com.victronenergy.platform/Services/AccessPoint/Enabled" } 125 | subpage: accessPoint.value !== undefined ? wifiWithAP : wifiWithoutAP 126 | Component { id: wifiWithoutAP; PageSettingsWifi {} } 127 | Component { id: wifiWithAP; PageSettingsWifiWithAccessPoint {} } 128 | } 129 | 130 | MbSubMenu { 131 | description: qsTr("GSM modem") 132 | subpage: Component { PageSettingsGsm {} } 133 | } 134 | 135 | MbSubMenu { 136 | description: qsTr("Bluetooth") 137 | subpage: Component { PageSettingsBluetooth {} } 138 | show: Connman.technologyList.indexOf("bluetooth") !== -1 139 | } 140 | 141 | MbSubMenu { 142 | description: qsTr("GPS") 143 | subpage: Component { PageSettingsGpsList {} } 144 | } 145 | 146 | MbSubMenu { 147 | description: qsTr("Generator start/stop") 148 | subpage: Component { PageRelayGenerator {} } 149 | show: hasRelay0 150 | } 151 | 152 | MbSubMenu { 153 | description: qsTr("Tank pump") 154 | subpage: Component { PageSettingsTankPump {} } 155 | } 156 | 157 | MbSubMenu { 158 | description: qsTr("Relay") 159 | subpage: Component { PageSettingsRelay {} } 160 | show: hasRelay0 161 | } 162 | 163 | MbSubMenu { 164 | description: qsTr("Services") 165 | subpage: Component { PageSettingsServices {} } 166 | } 167 | 168 | MbSubMenu { 169 | description: qsTr("I/O") 170 | subpage: ioSettings 171 | show: ioSettings.haveSubMenus 172 | PageSettingsIo { id: ioSettings } 173 | } 174 | 175 | /* 176 | MbSubMenu { 177 | description: qsTr("Backup & Restore") 178 | subpage: Component { PageSettingsBackup {} } 179 | } 180 | */ 181 | 182 | MbSubMenu { 183 | description: qsTr("Venus OS Large features") 184 | subpage: Component { PageSettingsLarge {} } 185 | property VBusItem signalK: VBusItem { bind: "com.victronenergy.platform/Services/SignalK/Enabled" } 186 | property VBusItem nodeRed: VBusItem { bind: "com.victronenergy.platform/Services/NodeRed/Mode" } 187 | show: signalK.valid || nodeRed.valid 188 | } 189 | 190 | MbSubMenu { 191 | description: "Debug" 192 | subpage: Component { PageDebug {} } 193 | showAccessLevel: User.AccessService 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /FileSets/PatchSource/PageSettings.qml.patch: -------------------------------------------------------------------------------- 1 | --- /Users/Kevin/GitHub/SetupHelper.copy/FileSets/PatchSource/PageSettings.qml.orig 2024-05-15 13:06:53 2 | +++ /Users/Kevin/GitHub/SetupHelper.copy/FileSets/PatchSource/PageSettings.qml 2025-01-24 22:39:59 3 | @@ -192,5 +192,11 @@ 4 | subpage: Component { PageDebug {} } 5 | showAccessLevel: User.AccessService 6 | } 7 | +//////// added for PackageManager 8 | + MbSubMenu 9 | + { 10 | + description: qsTr("Package manager") 11 | + subpage: Component { PageSettingsPackageManager {} } 12 | + } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /FileSets/VersionIndependent/MbDisplayDefaultPackage.qml: -------------------------------------------------------------------------------- 1 | //////// new for PackageManager 2 | 3 | import QtQuick 1.1 4 | import com.victron.velib 1.0 5 | import "utils.js" as Utils 6 | 7 | MbItem { 8 | id: root 9 | 10 | property int defaultIndex 11 | property string servicePrefix 12 | 13 | property bool isCurrentItem: root.ListView.isCurrentItem 14 | property MbStyle style: MbStyle { isCurrentItem: root.ListView.isCurrentItem } 15 | 16 | VBusItem { id: packageName; bind: getServiceBind ("PackageName") } 17 | 18 | 19 | onClicked: rootWindow.pageStack.push ("/opt/victronenergy/gui/qml/PageSettingsPackageAdd.qml", {defaultIndex: defaultIndex}) 20 | 21 | 22 | function getServiceBind(param) 23 | { 24 | return Utils.path(servicePrefix, "/Default/", defaultIndex, "/", param) 25 | } 26 | 27 | 28 | MbRowSmall 29 | { 30 | description: "" 31 | 32 | anchors.verticalCenter: parent.verticalCenter 33 | Column 34 | { 35 | width: root.width - gitHubUser.width - gitHubBranch.width - 20 36 | Text // puts a bit of space above package name 37 | { 38 | text: " " 39 | font.pixelSize: 6 40 | } 41 | Text 42 | { 43 | text:packageName.valid ? packageName.value : "" 44 | color: isCurrentItem ? root.style.textColorSelected : root.style.textColor 45 | font.pixelSize: 14 46 | horizontalAlignment: Text.AlignLeft 47 | } 48 | Text 49 | { 50 | text: "" 51 | font.pixelSize: 10 52 | horizontalAlignment: Text.AlignLeft 53 | } 54 | } 55 | Column 56 | { 57 | Text // puts a bit of space above version boxes 58 | { 59 | text: " " 60 | font.pixelSize: 3 61 | } 62 | Text 63 | { 64 | text: "GitHub User" 65 | color: isCurrentItem ? root.style.textColorSelected : root.style.textColor 66 | font.pixelSize: 10 67 | } 68 | MbTextBlock 69 | { 70 | id: gitHubUser 71 | item { bind: getServiceBind("GitHubUser") } 72 | height: 20; width: 120 73 | } 74 | Text // puts a bit of space below version boxes - only needed in one column 75 | { 76 | text: " " 77 | font.pixelSize: 6 78 | } 79 | } 80 | Column 81 | { 82 | Text // puts a bit of space above version boxes 83 | { 84 | text: " " 85 | font.pixelSize: 3 86 | } 87 | Text 88 | { 89 | text: qsTr ("GitHub Tag") 90 | color: isCurrentItem ? root.style.textColorSelected : root.style.textColor 91 | font.pixelSize: 10 92 | } 93 | MbTextBlock 94 | { 95 | id: gitHubBranch 96 | item { bind: getServiceBind("GitHubBranch") } 97 | height: 20; width: 120 98 | } 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /FileSets/VersionIndependent/MbDisplayPackageVersion.qml: -------------------------------------------------------------------------------- 1 | //////// new for PackageManager 2 | 3 | import QtQuick 1.1 4 | import com.victron.velib 1.0 5 | import "utils.js" as Utils 6 | 7 | MbItem { 8 | id: root 9 | 10 | property int packageIndex 11 | property string servicePrefix 12 | property string settingsPrefix 13 | 14 | property bool isCurrentItem: root.ListView.isCurrentItem 15 | property MbStyle style: MbStyle { isCurrentItem: root.ListView.isCurrentItem } 16 | 17 | VBusItem { id: packageName; bind: getSettingsBind ("PackageName") } 18 | property VBusItem rebootNeededItem: VBusItem { bind: getServiceBind ( "RebootNeeded") } 19 | property VBusItem guiRestartNeededItem: VBusItem { bind: getServiceBind ( "GuiRestartNeeded") } 20 | property bool rebootNeeded: rebootNeededItem.valid && rebootNeededItem.value == 1 21 | property bool guiRestartNeeded: guiRestartNeededItem.valid && guiRestartNeededItem.value == 1 22 | 23 | VBusItem { id: incompatibleItem; bind: getServiceBind ( "Incompatible" ) } 24 | property string incompatibleReason: incompatibleItem.valid ? incompatibleItem.value : "" 25 | property bool compatible: incompatibleReason == "" 26 | VBusItem { id: platformItem; bind: Utils.path("com.victronenergy.packageManager", "/Platform" ) } 27 | property string platform: platformItem.valid ? platformItem.value : "???" 28 | 29 | // version info may be in platform service or in vePlatform.version 30 | VBusItem { id: osVersionItem; bind: Utils.path("com.victronenergy.platform", "/Firmware/Installed/Version" ) } 31 | property string osVersion: osVersionItem.valid ? osVersionItem.value : vePlatform.version 32 | 33 | onClicked: rootWindow.pageStack.push ("/opt/victronenergy/gui/qml/PageSettingsPackageEdit.qml", {newPackageIndex: packageIndex}) 34 | 35 | 36 | function statusText () 37 | { 38 | if (rebootNeeded) 39 | return qsTr ("REBOOT needed") 40 | if (guiRestartNeeded) 41 | return qsTr ("GUI restart needed") 42 | else if (incompatibleReason == 'PLATFORM') 43 | return qsTr ( "incompatible with " + platform ) 44 | // don't show warning incompatibilities here - they are shown in the editor menu 45 | else if (incompatibleReason != "" 46 | && incompatibleReason.toLowerCase().indexOf ("warning") == -1 ) 47 | return incompatibleReason 48 | else 49 | return "" 50 | } 51 | 52 | function getSettingsBind(param) 53 | { 54 | return Utils.path(settingsPrefix, "/", packageIndex, "/", param) 55 | } 56 | function getServiceBind(param) 57 | { 58 | return Utils.path(servicePrefix, "/Package/", packageIndex, "/", param) 59 | } 60 | 61 | MbRowSmall 62 | { 63 | description: "" 64 | 65 | anchors.verticalCenter: parent.verticalCenter 66 | Column 67 | { 68 | width: root.width - gitHubVersion.width - packageVersion.width - installedVersion.width - 20 69 | Text // puts a bit of space above package name 70 | { 71 | text: " " 72 | font.pixelSize: 6 73 | } 74 | Text 75 | { 76 | text:packageName.valid ? packageName.value : "" 77 | color: isCurrentItem ? root.style.textColorSelected : root.style.textColor 78 | font.pixelSize: 14 79 | horizontalAlignment: Text.AlignLeft 80 | } 81 | Text 82 | { 83 | text: statusText () 84 | color: isCurrentItem ? root.style.textColorSelected : root.style.textColor 85 | font.pixelSize: 10 86 | horizontalAlignment: Text.AlignLeft 87 | } 88 | } 89 | Column 90 | { 91 | Text // puts a bit of space above version boxes 92 | { 93 | text: " " 94 | font.pixelSize: 3 95 | } 96 | Text 97 | { 98 | text: "GitHub" 99 | color: isCurrentItem ? root.style.textColorSelected : root.style.textColor 100 | font.pixelSize: 10 101 | } 102 | MbTextBlock 103 | { 104 | id: gitHubVersion 105 | item { bind: getServiceBind("GitHubVersion") } 106 | height: 20; width: 99 107 | } 108 | Text // puts a bit of space below version boxes - only needed in one column 109 | { 110 | text: " " 111 | font.pixelSize: 6 112 | } 113 | } 114 | Column 115 | { 116 | Text // puts a bit of space above version boxes 117 | { 118 | text: " " 119 | font.pixelSize: 3 120 | } 121 | Text 122 | { 123 | text: qsTr ("Stored") 124 | color: isCurrentItem ? root.style.textColorSelected : root.style.textColor 125 | font.pixelSize: 10 126 | } 127 | MbTextBlock 128 | { 129 | id: packageVersion 130 | item { bind: getServiceBind("PackageVersion") } 131 | height: 20; width: 99 132 | } 133 | } 134 | Column 135 | { 136 | Text // puts a bit of space above version boxes 137 | { 138 | text: " " 139 | font.pixelSize: 3 140 | } 141 | Text 142 | { 143 | text: qsTr ("Installed") 144 | color: isCurrentItem ? root.style.textColorSelected : root.style.textColor 145 | font.pixelSize: 10 146 | } 147 | MbTextBlock 148 | { 149 | id: installedVersion 150 | item { bind: getServiceBind("InstalledVersion") } 151 | height: 20; width: 99 152 | } 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /FileSets/VersionIndependent/PageSettingsAddPackageList.qml: -------------------------------------------------------------------------------- 1 | /////// new menu for package version display 2 | 3 | import QtQuick 1.1 4 | import "utils.js" as Utils 5 | import com.victron.velib 1.0 6 | 7 | MbPage { 8 | id: root 9 | title: defaultCount.valid ? qsTr("Inactive packages (tap to activate) ") : qsTr ("Package manager not running") 10 | 11 | property string servicePrefix: "com.victronenergy.packageManager" 12 | // use DefaultCount as an indication that PackageManager is running 13 | property VBusItem defaultCount: VBusItem { bind: Utils.path(servicePrefix, "/DefaultCount") } 14 | 15 | model: defaultCount.valid ? defaultCount.value : 0 16 | delegate: Component 17 | { 18 | MbDisplayDefaultPackage 19 | { 20 | servicePrefix: root.servicePrefix 21 | defaultIndex: index 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /FileSets/VersionIndependent/PageSettingsPackageAdd.qml: -------------------------------------------------------------------------------- 1 | /////// new menu for package add edit 2 | 3 | import QtQuick 1.1 4 | import "utils.js" as Utils 5 | import com.victron.velib 1.0 6 | 7 | MbPage { 8 | id: root 9 | title: editActionItem.valid ? qsTr("Add package") : qsTr ("Package manager not running") 10 | 11 | property bool isCurrentItem: root.ListView.isCurrentItem 12 | property MbStyle style: MbStyle { isCurrentItem: root.ListView.isCurrentItem } 13 | 14 | property string settingsPrefix: "com.victronenergy.settings/Settings/PackageManager" 15 | property string servicePrefix: "com.victronenergy.packageManager" 16 | property int defaultIndex:0 17 | property VBusItem defaultCount: VBusItem { bind: Utils.path(servicePrefix, "/DefaultCount") } 18 | property VBusItem editActionItem: VBusItem { bind: Utils.path(servicePrefix, "/GuiEditAction") } 19 | property VBusItem editStatus: VBusItem { bind: Utils.path(servicePrefix, "/GuiEditStatus") } 20 | property string packageName: packageNameBox.item.valid ? packageNameBox.item.value : "" 21 | property string editAction: editActionItem.valid ? editActionItem.value : '' 22 | 23 | property VBusItem defaultPackageName: VBusItem { bind: Utils.path ( servicePrefix, "/Default/", defaultIndex, "/", "PackageName" ) } 24 | property VBusItem defaultGitHubUser: VBusItem { bind: Utils.path ( servicePrefix, "/Default/", defaultIndex, "/", "GitHubUser" ) } 25 | property VBusItem defaultGitHubBranch: VBusItem { bind: Utils.path ( servicePrefix, "/Default/", defaultIndex, "/", "GitHubBranch" ) } 26 | property VBusItem editPackageName: VBusItem { bind: Utils.path ( settingsPrefix, "/Edit/", "PackageName" ) } 27 | property VBusItem editGitHubUser: VBusItem { bind: Utils.path ( settingsPrefix, "/Edit/", "GitHubUser" ) } 28 | property VBusItem editGitHubBranch: VBusItem { bind: Utils.path ( settingsPrefix, "/Edit/", "GitHubBranch" ) } 29 | property bool addPending: false 30 | property bool entryValid: editPackageName.value != "" && editGitHubUser.value != "" && editGitHubBranch.value != "" 31 | 32 | Component.onCompleted: 33 | { 34 | updateEdit () 35 | } 36 | 37 | onEditActionChanged: 38 | { 39 | if (addPending && editAction == '') 40 | { 41 | addPending = false 42 | pageStack.pop() 43 | } 44 | } 45 | 46 | function getSettingsBind(param) 47 | { 48 | return Utils.path(settingsPrefix, "/Edit/", param) 49 | } 50 | function getServiceBind(param) 51 | { 52 | return Utils.path(servicePrefix, "/Default/", defaultIndex, "/", param) 53 | } 54 | 55 | // copy a set of default package values to Edit area when changing indexes 56 | function updateEdit () 57 | { 58 | bindPrefix = Utils.path(servicePrefix, "/Default/", defaultIndex ) 59 | var defaultName = defaultPackageName.valid ? defaultPackageName.value : "??" 60 | if (defaultName == "new") 61 | defaultName = "" 62 | editPackageName.setValue ( defaultName ) 63 | editGitHubUser.setValue ( defaultGitHubUser.valid ? defaultGitHubUser.value : "??" ) 64 | editGitHubBranch.setValue ( defaultGitHubBranch.valid ? defaultGitHubBranch.value : "??" ) 65 | editStatus.setValue ("") 66 | editActionItem.setValue ("") 67 | addPending = false 68 | } 69 | 70 | function cancelEdit () 71 | { 72 | addPending = false 73 | if (editAction == '') 74 | pageStack.pop() 75 | else 76 | { 77 | editStatus.setValue ("") 78 | editActionItem.setValue ("") 79 | } 80 | } 81 | function confirm () 82 | { 83 | if (entryValid) 84 | { 85 | addPending = true 86 | // provide local confirmation of action - takes PackageManager too long 87 | editStatus.setValue ( "adding " + packageName) 88 | editActionItem.setValue ('add:' + packageName) 89 | } 90 | } 91 | model: VisibleItemModel 92 | { 93 | MbEditBox 94 | { 95 | id: packageNameBox 96 | description: qsTr ("Package name") 97 | maximumLength: 30 98 | item.bind: getSettingsBind ("PackageName") 99 | overwriteMode: false 100 | writeAccessLevel: User.AccessInstaller 101 | } 102 | MbEditBox 103 | { 104 | id: gitHubUserBox 105 | description: qsTr ("GitHub user") 106 | maximumLength: 20 107 | item.bind: getSettingsBind ("GitHubUser") 108 | overwriteMode: false 109 | writeAccessLevel: User.AccessInstaller 110 | } 111 | MbEditBox 112 | { 113 | id: gitHubBranchBox 114 | description: qsTr ("GitHub branch or tag") 115 | maximumLength: 20 116 | item.bind: getSettingsBind ("GitHubBranch") 117 | overwriteMode: false 118 | writeAccessLevel: User.AccessInstaller 119 | } 120 | MbOK 121 | { 122 | id: cancelButton 123 | width: 90 124 | anchors { right: parent.right } 125 | description: "" 126 | value: editAction == '' ? qsTr("Cancel") : qsTr("OK") 127 | onClicked: cancelEdit () 128 | } 129 | MbOK 130 | { 131 | id: proceedButton 132 | width: 100 133 | anchors { right: cancelButton.left; bottom: cancelButton.bottom } 134 | description: "" 135 | value: qsTr ("Proceed") 136 | onClicked: confirm () 137 | show: editAction == '' && entryValid 138 | writeAccessLevel: User.AccessInstaller 139 | } 140 | Text 141 | { 142 | id: statusMessage 143 | width: 250 144 | wrapMode: Text.WordWrap 145 | anchors { left: parent.left; leftMargin: 10; bottom: cancelButton.bottom; bottomMargin: 5 } 146 | font.pixelSize: 12 147 | text: 148 | { 149 | if (editStatus.valid && editStatus.value != "") 150 | return editStatus.value 151 | else if (entryValid) 152 | return ("add " + packageName + " ?") 153 | else if (editPackageName.value == "") 154 | return ("enter a unique package name") 155 | else if (editGitHubUser.value == "") 156 | return ("enter GitHub user") 157 | else if (editGitHubBranch.value == "") 158 | return ("enter GitHub branch") 159 | else 160 | return ("") 161 | } 162 | color: isCurrentItem ? root.style.textColorSelected : root.style.textColor 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /FileSets/VersionIndependent/PageSettingsPackageEdit.qml: -------------------------------------------------------------------------------- 1 | /////// new menu for package version edit 2 | 3 | import QtQuick 1.1 4 | import "utils.js" as Utils 5 | import com.victron.velib 1.0 6 | 7 | MbPage { 8 | id: root 9 | title: platform.valid ? qsTr("Package editor") : qsTr ("Package manager not running") 10 | 11 | property bool isCurrentItem: root.ListView.isCurrentItem 12 | property MbStyle style: MbStyle { isCurrentItem: root.ListView.isCurrentItem } 13 | 14 | property string settingsPrefix: "com.victronenergy.settings/Settings/PackageManager" 15 | property string servicePrefix: "com.victronenergy.packageManager" 16 | property int packageIndex: 0 17 | property int newPackageIndex:0 18 | property VBusItem packageCount: VBusItem { bind: Utils.path(settingsPrefix, "/Count") } 19 | property VBusItem editAction: VBusItem { bind: Utils.path(servicePrefix, "/GuiEditAction") } 20 | property VBusItem editStatus: VBusItem { bind: Utils.path(servicePrefix, "/GuiEditStatus") } 21 | property VBusItem packageNameItem: VBusItem { bind: getSettingsBind ("PackageName") } 22 | property string packageName: packageNameItem.valid ? packageNameItem.value : "" 23 | property bool isSetupHelper: packageName == "SetupHelper" 24 | 25 | property VBusItem incompatibleReasonItem: VBusItem { bind: getServiceBind ( "Incompatible" ) } 26 | property string incompatibleReason: incompatibleReasonItem.valid ? incompatibleReasonItem.value : "" 27 | property VBusItem incompatibleDetailsItem: VBusItem { bind: getServiceBind ( "IncompatibleDetails") } 28 | property string incompatibleDetails: incompatibleDetailsItem.valid ? incompatibleDetailsItem.value : "" 29 | property bool incompatible: incompatibleReason != "" 30 | property VBusItem platform: VBusItem { bind: Utils.path(servicePrefix, "/Platform") } 31 | property VBusItem incompatibleResolvableItem: VBusItem { bind: getServiceBind ( "IncompatibleResolvable") } 32 | 33 | property bool gitHubValid: gitHubVersion.item.valid && gitHubVersion.item.value.substring (0,1) === "v" 34 | property bool packageValid: packageVersion.item.valid && packageVersion.item.value.substring (0,1) === "v" 35 | property bool installedValid: installedVersion.item.valid && installedVersion.item.value.substring (0,1) === "v" 36 | property bool downloadOk: gitHubValid && gitHubVersion.item.value != "" 37 | property bool installOk: ! incompatible 38 | property string requestedAction: '' 39 | property bool actionPending: requestedAction != '' 40 | property bool waitForAction: editAction.value != '' && ! editError 41 | property bool editError: editAction.value == 'ERROR' 42 | property bool navigate: ! actionPending && ! waitForAction 43 | property bool detailsExist: incompatibleDetails != "" 44 | property bool detailsResolvable: incompatibleResolvableItem.valid ? incompatibleResolvableItem.value : "" 45 | 46 | property bool showDetails: false 47 | property string localError: "" 48 | 49 | // version info may be in platform service or in vePlatform.version 50 | VBusItem { id: osVersionItem; bind: Utils.path("com.victronenergy.platform", "/Firmware/Installed/Version" ) } 51 | property string osVersion: osVersionItem.valid ? osVersionItem.value : vePlatform.version 52 | 53 | // ActionNeeded is a global parameter provided inform the GUI that a GUI restart or system reboot is needed 54 | // when dismissed, a timer started which hides the information 55 | // when the timer expires, the information is shown again 56 | // changes to ActionNeeded will stop the timer so the new value will be shown immediately 57 | property VBusItem actionNeededItem: VBusItem { bind: Utils.path(servicePrefix, "/ActionNeeded") } 58 | property string actionNeeded: actionNeededItem.valid ? actionNeededItem.value : "" 59 | property bool showActionNeeded: ! hideActionNeededTimer.running && actionNeeded != '' 60 | 61 | 62 | onActionNeededChanged: 63 | { 64 | hideActionNeededTimer.stop () 65 | } 66 | 67 | onWaitForActionChanged: 68 | { 69 | if ( ! waitForAction ) 70 | { 71 | hideActionNeededTimer.stop () 72 | requestedAction = '' 73 | } 74 | } 75 | 76 | onIncompatibleChanged: 77 | { 78 | if (! incompatible ) 79 | showDetails = false 80 | } 81 | 82 | onActiveChanged: 83 | { 84 | if (active) 85 | { 86 | hideActionNeededTimer.stop () 87 | resetPackageIndex () 88 | refreshGitHubVersions () 89 | acknowledgeError () 90 | requestedAction = '' 91 | } 92 | } 93 | 94 | onNavigateChanged: resetPackageIndex () 95 | 96 | // hide action for 10 minutes 97 | Timer 98 | { 99 | id: hideActionNeededTimer 100 | running: false 101 | repeat: false 102 | interval: 1000 * 60 * 10 103 | } 104 | 105 | // refresh the GitHub version GitHub version age is greater than 30 seconds 106 | property bool waitForIndexChange: false 107 | property bool waitForNameChange: false 108 | 109 | onPackageIndexChanged: 110 | { 111 | waitForIndexChange = false 112 | } 113 | onPackageNameChanged: 114 | { 115 | waitForNameChange = false 116 | refreshGitHubVersions () 117 | } 118 | 119 | function refreshGitHubVersions () 120 | { 121 | if ( waitForIndexChange || waitForNameChange ) 122 | return 123 | else if (! active || editAction.value != "" || actionPending) 124 | return 125 | sendCommand ( 'gitHubScan' + ':' + packageName, false ) 126 | } 127 | 128 | // acknowledge error reported from PackageManager 129 | // and erase status message 130 | function acknowledgeError () 131 | { 132 | if (editError) 133 | { 134 | editAction.setValue ("") 135 | editStatus.setValue ("") 136 | } 137 | } 138 | 139 | function resetPackageIndex () 140 | { 141 | if (waitForAction) 142 | return 143 | 144 | if (newPackageIndex < 0) 145 | newPackageIndex = 0 146 | else if (newPackageIndex >= packageCount.value) 147 | newPackageIndex = packageCount.value - 1 148 | 149 | if (newPackageIndex != packageIndex) 150 | { 151 | waitForIndexChange = true 152 | waitForNameChange = true 153 | packageIndex = newPackageIndex 154 | requestedAction = '' 155 | showDetails = false 156 | } 157 | } 158 | 159 | function getSettingsBind(param) 160 | { 161 | return Utils.path(settingsPrefix, "/", packageIndex, "/", param) 162 | } 163 | function getServiceBind(param) 164 | { 165 | return Utils.path(servicePrefix, "/Package/", packageIndex, "/", param) 166 | } 167 | 168 | function sendCommand (command, updateEditStatus ) 169 | { 170 | if (editAction.value != "") 171 | localError = "command could not be sent (" + command + ")" 172 | else 173 | { 174 | if (updateEditStatus) 175 | editStatus.setValue ("sending " + command) 176 | editAction.setValue (command) 177 | } 178 | } 179 | 180 | // don't change packages if pending operation or waiting for completion 181 | function nextIndex () 182 | { 183 | if (editError) 184 | return 185 | newPackageIndex += 1 186 | resetPackageIndex () 187 | } 188 | function previousIndex () 189 | { 190 | if (editError) 191 | return 192 | newPackageIndex -= 1 193 | resetPackageIndex () 194 | } 195 | 196 | function cancelEdit () 197 | { 198 | // cancel any pending operation 199 | requestedAction = '' 200 | showDetails = false 201 | 202 | acknowledgeError () 203 | 204 | // if was showing action needed, hide that messge for now 205 | if (showActionNeeded) 206 | hideActionNeededTimer.start () 207 | } 208 | function confirm () 209 | { 210 | if (showDetails) 211 | { 212 | if (detailsResolvable) 213 | { 214 | sendCommand ( 'resolveConflicts:' + packageName, true ) 215 | showDetails = false 216 | } 217 | // trigger setup script prechecks 218 | else 219 | { 220 | sendCommand ( 'check:' + packageName, true ) 221 | showDetails = false 222 | } 223 | } 224 | else if (actionPending) 225 | sendCommand ( requestedAction + ':' + packageName, true ) 226 | else if (showActionNeeded) 227 | { 228 | if (actionNeeded.indexOf ( "REBOOT" ) != -1 ) 229 | sendCommand ( 'reboot', true ) 230 | else if (actionNeeded.indexOf ( "restart" ) != -1 ) 231 | sendCommand ( 'restartGui', true ) 232 | hideActionNeededTimer.start () 233 | } 234 | requestedAction = '' 235 | } 236 | function install () 237 | { 238 | if (navigate && installOk && ! editError) 239 | { 240 | requestedAction = 'install' 241 | showDetails = false 242 | } 243 | } 244 | function uninstall () 245 | { 246 | if (navigate && installedValid && ! editError) 247 | { 248 | requestedAction = 'uninstall' 249 | showDetails = false 250 | } 251 | } 252 | function gitHubDownload () 253 | { 254 | if (navigate && downloadOk && ! editError) 255 | { 256 | requestedAction = 'download' 257 | showDetails = false 258 | } 259 | } 260 | function remove () 261 | { 262 | if ( ! editError) 263 | { 264 | requestedAction = 'remove' 265 | showDetails = false 266 | } 267 | } 268 | 269 | model: VisibleItemModel 270 | { 271 | MbItemText 272 | { 273 | id: packageNameBox 274 | text: packageName + " versions" 275 | } 276 | MbRowSmall 277 | { 278 | description: " " 279 | height: 25 280 | Text 281 | { 282 | text: "GitHub:" 283 | color: isCurrentItem ? root.style.textColorSelected : root.style.textColor 284 | font.pixelSize: 10 285 | } 286 | MbTextBlock 287 | { 288 | id: gitHubVersion 289 | item { bind: getServiceBind("GitHubVersion") } 290 | height: 25; width: 105 291 | } 292 | Text 293 | { 294 | text: qsTr ("stored:") 295 | color: isCurrentItem ? root.style.textColorSelected : root.style.textColor 296 | font.pixelSize: 10 297 | } 298 | MbTextBlock 299 | { 300 | id: packageVersion 301 | item { bind: getServiceBind("PackageVersion") } 302 | height: 25; width: 105 303 | } 304 | Text 305 | { 306 | text: qsTr ("installed:") 307 | color: isCurrentItem ? root.style.textColorSelected : root.style.textColor 308 | horizontalAlignment: Text.AlignRight 309 | font.pixelSize: 10 310 | } 311 | MbTextBlock 312 | { 313 | id: installedVersion 314 | item { bind: getServiceBind("InstalledVersion") } 315 | height: 25 316 | width: 105 317 | } 318 | } 319 | MbEditBox 320 | { 321 | id: gitHubUser 322 | description: qsTr ("GitHub user") 323 | maximumLength: 20 324 | item.bind: getSettingsBind ("GitHubUser") 325 | overwriteMode: false 326 | writeAccessLevel: User.AccessInstaller 327 | } 328 | MbEditBox 329 | { 330 | id: gitHubBranch 331 | description: qsTr ("GitHub branch or tag") 332 | maximumLength: 20 333 | item.bind: getSettingsBind ("GitHubBranch") 334 | overwriteMode: false 335 | writeAccessLevel: User.AccessInstaller 336 | } 337 | 338 | MbOK 339 | { 340 | id: cancelButton 341 | width: 85 342 | anchors { right: parent.right; bottom: statusMessage.bottom } 343 | description: "" 344 | value: ( actionPending || showDetails ) ? qsTr("Cancel") : (editError ? qsTr("OK") : qsTr("Later")) 345 | onClicked: cancelEdit () 346 | show: ( actionPending || showDetails || editError || showActionNeeded ) && ! waitForAction 347 | } 348 | MbOK 349 | { 350 | id: confirmButton 351 | width: 92 352 | anchors { right: cancelButton.left; bottom: statusMessage.bottom } 353 | description: "" 354 | value: ( actionPending || detailsResolvable ) ? qsTr("Proceed") : showDetails ? qsTr ("Recheck") : qsTr ("Now") 355 | onClicked: confirm () 356 | show: ( actionPending || showDetails || showActionNeeded ) && ! waitForAction 357 | writeAccessLevel: User.AccessInstaller 358 | } 359 | MbOK 360 | { 361 | id: showDetailsButton 362 | width: 150 363 | anchors { right: parent.right; bottom: statusMessage.bottom} 364 | description: "" 365 | value: qsTr("Show Details") 366 | onClicked: showDetails = true 367 | writeAccessLevel: User.AccessInstaller 368 | show: navigate && detailsExist && ! ( editError || actionPending || waitForAction || showActionNeeded || showDetails) 369 | } 370 | 371 | // bottom row of buttons 372 | MbOK 373 | { 374 | id: previousButton 375 | width: 100 376 | anchors { left: parent.left; top: statusMessage.bottom; topMargin: 5 } 377 | description: "" 378 | value: qsTr("Previous") 379 | onClicked: previousIndex () 380 | opacity: ! editError && newPackageIndex > 0 ? 1.0 : 0.2 381 | } 382 | MbOK 383 | { 384 | id: nextButton 385 | width: 70 386 | anchors { left: previousButton.right; top: statusMessage.bottom; topMargin: 5 } 387 | description: "" 388 | value: qsTr("Next") 389 | onClicked: nextIndex () 390 | opacity: ! editError && (newPackageIndex < packageCount.value - 1) ? 1.0 : 0.2 391 | } 392 | MbOK 393 | { 394 | id: downloadButton 395 | width: 110 396 | anchors { right: installButton.left; top: statusMessage.bottom; topMargin: 5 } 397 | description: "" 398 | value: qsTr ("Download") 399 | onClicked: gitHubDownload () 400 | opacity: ! editError && navigate && downloadOk > 0 ? 1.0 : 0.2 401 | writeAccessLevel: User.AccessInstaller 402 | } 403 | MbOK 404 | { 405 | id: installButton 406 | width: 80 407 | anchors { right: uninstallButton.left; top: statusMessage.bottom; topMargin: 5 } 408 | description: "" 409 | value: qsTr ("Install") 410 | onClicked: install () 411 | opacity: ! editError && navigate && installOk > 0 ? 1.0 : 0.2 412 | writeAccessLevel: User.AccessInstaller 413 | } 414 | MbOK 415 | { 416 | id: uninstallButton 417 | width: 105 418 | anchors { right: parent.right; top: statusMessage.bottom; topMargin: 5 } 419 | description: "" 420 | value: installedValid ? qsTr("Uninstall") : qsTr("Remove") 421 | onClicked: installedValid ? uninstall () : remove () 422 | opacity: ! editError && navigate ? 1.0 : 0.2 423 | writeAccessLevel: User.AccessInstaller 424 | } 425 | // at bottom so it's not in the middle of hard button cycle 426 | Text 427 | { 428 | id: statusMessage 429 | width: 430 | { 431 | var smWidth = root.width 432 | if (cancelButton.show) 433 | smWidth -= cancelButton.width 434 | if (confirmButton.show) 435 | smWidth -= confirmButton.width 436 | if (showDetailsButton.show) 437 | smWidth -= showDetailsButton.width 438 | return smWidth 439 | } 440 | height: Math.max (paintedHeight, 35) 441 | wrapMode: Text.WordWrap 442 | horizontalAlignment: Text.AlignLeft 443 | anchors { left: parent.left; leftMargin: 5; top: gitHubBranch.bottom } 444 | font.pixelSize: 12 445 | color: isSetupHelper && requestedAction == 'uninstall' ? "red" : root.style.textColor 446 | text: 447 | { 448 | if (showDetails) 449 | { 450 | if (detailsResolvable) 451 | return ( incompatibleDetails + qsTr ("\nResolve conflicts?") ) 452 | else 453 | return ( incompatibleDetails ) 454 | } 455 | else if (actionPending) 456 | { 457 | if (isSetupHelper && requestedAction == 'uninstall') 458 | return qsTr ("WARNING: SetupHelper is required for these menus - uninstall anyway ?") 459 | else 460 | return (requestedAction + " " + packageName + " ?") 461 | } 462 | else if (editStatus.valid && editStatus.value != "") 463 | return ( editStatus.value ) 464 | else if (showActionNeeded) 465 | return ( actionNeeded ) 466 | else if (incompatible) 467 | return ( incompatibleReason ) 468 | else 469 | return localError 470 | } 471 | } 472 | // dummy item to allow scrolling to show last button line when status message has many lines 473 | MbItemText 474 | { 475 | text: "" 476 | opacity: 0 477 | show: statusMessage.height > 35 478 | } 479 | } 480 | } 481 | -------------------------------------------------------------------------------- /FileSets/VersionIndependent/PageSettingsPackageManager.qml: -------------------------------------------------------------------------------- 1 | /////// new menu for package version display 2 | 3 | import QtQuick 1.1 4 | import "utils.js" as Utils 5 | import com.victron.velib 1.0 6 | 7 | MbPage { 8 | id: root 9 | title: showControls ? qsTr("Package manager") : qsTr("Package manager not running") 10 | property string settingsPrefix: "com.victronenergy.settings/Settings/PackageManager" 11 | property string servicePrefix: "com.victronenergy.packageManager" 12 | property string bindVrmloggerPrefix: "com.victronenergy.logger" 13 | VBusItem { id: pmStatusItem; bind: Utils.path(servicePrefix, "/PmStatus") } 14 | property string pmStatus: pmStatusItem.valid ? pmStatusItem.value : "" 15 | VBusItem { id: mediaStatus; bind: Utils.path(servicePrefix, "/MediaUpdateStatus") } 16 | VBusItem { id: actionNeeded; bind: Utils.path(servicePrefix, "/ActionNeeded") } 17 | VBusItem { id: editAction; bind: Utils.path(servicePrefix, "/GuiEditAction") } 18 | property bool showMediaStatus: mediaStatus.valid && mediaStatus.value != "" 19 | property bool showControls: pmStatusItem.valid 20 | 21 | // the last status message received from PackageManager is saved in lastStatus 22 | // so there is some status to display when PackageManager quits 23 | property string lastStatus: "" 24 | 25 | onPmStatusChanged: 26 | { 27 | if (pmStatusItem.valid) 28 | lastStatus = pmStatus 29 | } 30 | 31 | model: VisibleItemModel 32 | { 33 | MbItemText 34 | { 35 | id: status 36 | text: 37 | { 38 | if (mediaStatus.valid && mediaStatus.value != "") 39 | return mediaStatus.value 40 | else if (showControls) 41 | return pmStatus 42 | else 43 | return lastStatus 44 | } 45 | wrapMode: Text.WordWrap 46 | horizontalAlignment: Text.AlignHCenter 47 | } 48 | MbItemOptions 49 | { 50 | id: autoDownload 51 | description: qsTr ("GitHub check frequency") 52 | bind: Utils.path (settingsPrefix, "/GitHubAutoDownload") 53 | possibleValues: 54 | [ 55 | MbOption { description: "Once"; value: 99 }, 56 | MbOption { description: "Every 10 minutes"; value: 1 }, 57 | MbOption { description: "Hourly"; value: 2 }, 58 | MbOption { description: "Daily"; value: 3 }, 59 | MbOption { description: "Never"; value: 0 } 60 | ] 61 | writeAccessLevel: User.AccessInstaller 62 | } 63 | MbSwitch 64 | { 65 | id: autoInstall 66 | bind: Utils.path (settingsPrefix, "/AutoInstall") 67 | name: qsTr ("Auto install packages") 68 | writeAccessLevel: User.AccessInstaller 69 | } 70 | MbSubMenu 71 | { 72 | description: qsTr("Active packages") 73 | subpage: Component { PageSettingsPackageVersions {} } 74 | show: showControls 75 | } 76 | MbSubMenu 77 | { 78 | description: qsTr("Inactive packages") 79 | subpage: Component { PageSettingsAddPackageList {} } 80 | show: showControls 81 | } 82 | MbOK 83 | { 84 | id: finishButton 85 | description: 86 | { 87 | if (editAction.value == 'reboot') 88 | return qsTr ("REBOOTING ...") 89 | else if (editAction.value == 'guiRestart') 90 | return qsTr ("restarting GUI ...") 91 | else 92 | return qsTr ("action to finish install/uninstall") 93 | } 94 | value: 95 | { 96 | if (! actionNeeded.valid) 97 | return "" 98 | else if (actionNeeded.value.indexOf ( "REBOOT" ) != -1 ) 99 | return qsTr ("Reboot") 100 | else if (actionNeeded.value.indexOf ( "restart" ) != -1 ) 101 | return qsTr ("Restart GUI") 102 | else 103 | return "" 104 | } 105 | onClicked: 106 | { 107 | if (finishButton.value == 'REBOOT') 108 | { 109 | // needs immediate update because GUI will be going down ASAP 110 | finishButton.description = qsTr ("REBOOTING ...") 111 | editAction.setValue ( 'reboot' ) 112 | } 113 | else if (finishButton.value == 'guiRestart') 114 | { 115 | // needs immediate update because GUI will be going down ASAP 116 | finishButton.description = qsTr ("restarting GUI ...") 117 | editAction.setValue ( 'restartGui' ) 118 | } 119 | } 120 | show: actionNeeded.valid && actionNeeded.value != '' 121 | writeAccessLevel: User.AccessInstaller 122 | } 123 | MbSubMenu 124 | { 125 | description: qsTr("Backup & restore settings") 126 | subpage: Component { PageSettingsPmBackup {} } 127 | show: showControls 128 | } 129 | MbOK { 130 | property int notMounted: 0 131 | property int mounted: 1 132 | property int unmountRequested: 2 133 | property int unmountBusy: 3 134 | 135 | function mountStateToText(s) 136 | { 137 | switch (s) { 138 | case mounted: 139 | return qsTr("Press to eject"); 140 | case unmountRequested: 141 | case unmountBusy: 142 | return qsTr("Ejecting, please wait"); 143 | default: 144 | return qsTr("No storage found"); 145 | } 146 | } 147 | 148 | VBusItem { 149 | id: vMountState 150 | bind: Utils.path(bindVrmloggerPrefix, "/Storage/MountState") 151 | } 152 | description: qsTr("microSD / USB") 153 | value: mountStateToText(vMountState.value) 154 | writeAccessLevel: User.AccessUser 155 | onClicked: vMountState.setValue(unmountRequested); 156 | editable: vMountState.value === mounted 157 | cornerMark: false 158 | } 159 | MbSubMenu 160 | { 161 | description: qsTr("Restart or initialize ...") 162 | subpage: Component { PageSettingsPmInitialize {} } 163 | show: showControls 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /FileSets/VersionIndependent/PageSettingsPackageVersions.qml: -------------------------------------------------------------------------------- 1 | /////// new menu for package version display 2 | 3 | import QtQuick 1.1 4 | import "utils.js" as Utils 5 | import com.victron.velib 1.0 6 | 7 | MbPage { 8 | id: root 9 | title: defaultCount.valid ? qsTr("Active packages (tap to edit) ") : qsTr ("Package manager not running") 10 | property string servicePrefix: "com.victronenergy.packageManager" 11 | property string settingsPrefix: "com.victronenergy.settings/Settings/PackageManager" 12 | property VBusItem count: VBusItem { bind: Utils.path(settingsPrefix, "/Count") } 13 | // use DefaultCount as an indication that PackageManager is running 14 | property VBusItem defaultCount: VBusItem { bind: Utils.path(servicePrefix, "/DefaultCount") } 15 | property VBusItem editAction: VBusItem { bind: Utils.path(servicePrefix, "/GuiEditAction") } 16 | 17 | // notify PackageManager to refresh GitHub versions for all packages 18 | // when this menu goes active (entering from parent or returning from child) 19 | // or if first package's GitHub version age is greater than 60 seconds 20 | onActiveChanged: refreshGitHubVersions () 21 | 22 | function refreshGitHubVersions () 23 | { 24 | if (! active) 25 | return 26 | else if ( editAction.value != "" ) 27 | return 28 | 29 | editAction.setValue ('gitHubScan:ALL') 30 | } 31 | 32 | 33 | model: defaultCount.valid ? count.valid ? count.value : 0 : 0 34 | delegate: Component 35 | { 36 | MbDisplayPackageVersion 37 | { 38 | servicePrefix: root.servicePrefix 39 | settingsPrefix: root.settingsPrefix 40 | packageIndex: index 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /FileSets/VersionIndependent/PageSettingsPmBackup.qml: -------------------------------------------------------------------------------- 1 | /////// new menu for settings backup and restore 2 | 3 | import QtQuick 1.1 4 | import "utils.js" as Utils 5 | import com.victron.velib 1.0 6 | 7 | MbPage { 8 | id: root 9 | title: qsTr("Settings backup & restore") 10 | property string settingsPrefix: "com.victronenergy.settings/Settings/PackageManager" 11 | property string servicePrefix: "com.victronenergy.packageManager" 12 | VBusItem { id: mediaAvailable; bind: Utils.path(servicePrefix, "/BackupMediaAvailable") } 13 | VBusItem { id: settingsFileExists; bind: Utils.path(servicePrefix, "/BackupSettingsFileExist") } 14 | VBusItem { id: settingsLocalFileExists; bind: Utils.path(servicePrefix, "/BackupSettingsLocalFileExist") } 15 | VBusItem { id: backupProgressItem; bind: Utils.path(servicePrefix, "/BackupProgress") } 16 | property int backupProgress: backupProgressItem.valid ? backupProgressItem.value : 0 17 | 18 | model: VisibleItemModel 19 | { 20 | MbItemText 21 | { 22 | id: info 23 | text: qsTr ("Backup and restore\nSOME system settings, logs and logos\nthis is NOT the Victron mechanism\ncurrently under development") 24 | wrapMode: Text.WordWrap 25 | horizontalAlignment: Text.AlignHCenter 26 | } 27 | MbItemText 28 | { 29 | id: status 30 | text: 31 | { 32 | if (backupProgress == 21 || backupProgress == 23) 33 | return qsTr ("backing up settings to local storage ... (may take a while)") 34 | else if (backupProgress == 22 || backupProgress == 24) 35 | return qsTr ("restoring settings from local storage ... (may take a while)") 36 | else if (backupProgress == 1 || backupProgress == 3) 37 | return qsTr ("backing up settings ... (may take a while)") 38 | else if (backupProgress == 2 || backupProgress == 4) 39 | return qsTr ("restoring settings ... (may take a while)") 40 | else if ( ! mediaAvailable.valid || mediaAvailable.value == 0) 41 | return qsTr ("No USB or SD media found - insert one to continue") 42 | else if (settingsFileExists.valid && settingsFileExists.value == 1) 43 | return qsTr ("Settings backup file found") 44 | else 45 | return "" 46 | } 47 | wrapMode: Text.WordWrap 48 | horizontalAlignment: Text.AlignHCenter 49 | } 50 | MbOK 51 | { 52 | description: qsTr("Backup settings, logos, logs") 53 | value: qsTr("Press to backup") 54 | onClicked: backupProgressItem.setValue (1) 55 | show: mediaAvailable.valid && mediaAvailable.value == 1 && backupProgressItem.value == 0 56 | writeAccessLevel: User.AccessInstaller 57 | } 58 | MbOK 59 | { 60 | description: qsTr("Restore settings, logos") 61 | value: qsTr("Press to restore") 62 | onClicked: backupProgressItem.setValue (2) 63 | show: settingsFileExists.valid && settingsFileExists.value == 1 && backupProgressItem.value == 0 64 | writeAccessLevel: User.AccessInstaller 65 | } 66 | MbOK 67 | { 68 | description: qsTr("Backup settings to local storage") 69 | value: qsTr("Press to backup") 70 | onClicked: backupProgressItem.setValue (21) 71 | show: backupProgressItem.value == 0 72 | writeAccessLevel: User.AccessInstaller 73 | } 74 | MbOK 75 | { 76 | description: qsTr("Restore settings to from storage") 77 | value: qsTr("Press to restore") 78 | onClicked: backupProgressItem.setValue (22) 79 | show: settingsLocalFileExists.valid && settingsLocalFileExists.value == 1 && backupProgressItem.value == 0 80 | writeAccessLevel: User.AccessInstaller 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /FileSets/VersionIndependent/PageSettingsPmInitialize.qml: -------------------------------------------------------------------------------- 1 | /////// new menu for PackageManager initialize 2 | 3 | import QtQuick 1.1 4 | import "utils.js" as Utils 5 | import com.victron.velib 1.0 6 | 7 | MbPage { 8 | id: root 9 | title: pmRunning ? qsTr("PackageManager restart/initialize") : qsTr ("Package manager not running") 10 | property string settingsPrefix: "com.victronenergy.settings/Settings/PackageManager" 11 | VBusItem { id: pmStatus; bind: Utils.path(servicePrefix, "/PmStatus") } 12 | property bool pmRunning: pmStatus.valid 13 | 14 | property bool showInProgress: false 15 | property string initializeMessage: "" 16 | 17 | onPmRunningChanged: { showInProgress = false } 18 | 19 | function sendCommand (command, message) 20 | { 21 | initializeMessage = message 22 | showInProgress = true 23 | editAction.setValue (command) 24 | } 25 | 26 | model: VisibleItemModel 27 | { 28 | MbOK 29 | { 30 | description: qsTr("Restart") 31 | value: qsTr("Press to restart Package Manager") 32 | onClicked:sendCommand ("RESTART_PM", qsTr ("restarting Package Manager ...")) 33 | writeAccessLevel: User.AccessInstaller 34 | show: ! showInProgress 35 | } 36 | MbOK 37 | { 38 | description: qsTr("Restart GUI") 39 | value: qsTr("Press to restart GUI") 40 | onClicked:sendCommand ("restartGui", qsTr ("restarting GUI ...")) 41 | writeAccessLevel: User.AccessInstaller 42 | show: ! showInProgress 43 | } 44 | MbItemText 45 | { 46 | id: info 47 | text: qsTr ("Initializing PackageManager will\nreset persistent storage to an empty state\nGit Hub user and branch are reset to defaults\nPackages added manually must be added again") 48 | wrapMode: Text.WordWrap 49 | horizontalAlignment: Text.AlignHCenter 50 | show: ! showInProgress 51 | } 52 | MbOK 53 | { 54 | description: qsTr("Initialize") 55 | value: qsTr("Press to INITIALIZE Package Manager") 56 | onClicked: sendCommand ("INITIALIZE_PM", qsTr ("INITIALIZING Package Manager ...")) 57 | writeAccessLevel: User.AccessInstaller 58 | show: ! showInProgress 59 | } 60 | MbItemText 61 | { 62 | id: initializingMessage 63 | text: initializeMessage 64 | wrapMode: Text.WordWrap 65 | horizontalAlignment: Text.AlignHCenter 66 | show: showInProgress 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /FileSets/fileListPatched: -------------------------------------------------------------------------------- 1 | /opt/victronenergy/gui/qml/PageSettings.qml 2 | -------------------------------------------------------------------------------- /FileSets/fileListVersionIndependent: -------------------------------------------------------------------------------- 1 | /opt/victronenergy/gui/qml/PageSettingsPackageManager.qml 2 | /opt/victronenergy/gui/qml/PageSettingsPackageVersions.qml 3 | /opt/victronenergy/gui/qml/PageSettingsPackageEdit.qml 4 | /opt/victronenergy/gui/qml/PageSettingsAddPackageList.qml 5 | /opt/victronenergy/gui/qml/PageSettingsPackageAdd.qml 6 | /opt/victronenergy/gui/qml/PageSettingsPmBackup.qml 7 | /opt/victronenergy/gui/qml/PageSettingsPmInitialize.qml 8 | /opt/victronenergy/gui/qml/MbDisplayPackageVersion.qml 9 | /opt/victronenergy/gui/qml/MbDisplayDefaultPackage.qml 10 | -------------------------------------------------------------------------------- /HelperResources/DbusSettingsResources: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # DbusSettingsResources for SetupHelper 4 | # 5 | # contains a functions and variables necessary to access dbus Settings parameters 6 | # it should be sourced by scripts setting, creating and removing dbus settings 7 | # 8 | # dbus Settings is not operational during system boot when some setup scripts may 9 | # need to make settings changes 10 | # These functions check to see if the settings system is operational and defer 11 | # the set/create/remove activity so the calling script may continue 12 | 13 | # dbus Settings funcitons 14 | # These functions encapsulate an interface to dbus Settings 15 | # NOTE: dbus Settings resources are not always active when it is necessary for 16 | # scripts to make changes or create/remove settings 17 | # it is up to the caller to insure dbus Settings resources are active before callling 18 | # these functions 19 | # a dbus exeption error will be logged if settings are not active yet 20 | 21 | 22 | # updateDbusStringSetting 23 | # updateDbusIntSetting 24 | # updateDbusRealSetting 25 | # updates a dbus setting parameter with a new value 26 | # 27 | # if the setting does not exist, it is created 28 | # but max and min values are not set and the default is "", 0 or 0.0 depending on data type 29 | # if these are needed use the dbus command directly 30 | # this can also be faster if lots of settings must be created at the same time 31 | # 32 | # other data types may exist and would need their own function 33 | # 34 | # $1 is the path to the setting starting with /Settings 35 | # $2 is the new value 36 | # 37 | # if the setting does not yet exist, it is created, then updated to the new value 38 | 39 | updateDbusStringSetting () 40 | { 41 | # don't do any work if install has already failed 42 | if $installFailed; then 43 | return 44 | fi 45 | 46 | dbus-send --system --print-reply=literal --dest=com.victronenergy.settings "$1"\ 47 | com.victronenergy.BusItem.GetValue &> /dev/null 48 | if (( $? != 0 )); then 49 | logMessage "creating dbus Setting $1" 50 | dbus -y com.victronenergy.settings / AddSettings "%[ {\"path\":\"$1\", \"default\":\"\"} ]" &> /dev/null 51 | fi 52 | 53 | dbus -y com.victronenergy.settings "$1" SetValue -- "$2" &> /dev/null 54 | } 55 | 56 | 57 | updateDbusIntSetting () 58 | { 59 | # don't do any work if install has already failed 60 | if $installFailed; then 61 | return 62 | fi 63 | 64 | dbus-send --system --print-reply=literal --dest=com.victronenergy.settings "$1"\ 65 | com.victronenergy.BusItem.GetValue &> /dev/null 66 | if (( $? != 0 )); then 67 | logMessage "creating dbus Setting $1" 68 | dbus -y com.victronenergy.settings / AddSettings "%[ {\"path\":\"$1\", \"default\":0} ]" &> /dev/null 69 | fi 70 | 71 | dbus -y com.victronenergy.settings "$1" SetValue -- "$2" &> /dev/null 72 | } 73 | 74 | 75 | updateDbusRealSetting () 76 | { 77 | # don't do any work if install has already failed 78 | if $installFailed; then 79 | return 80 | fi 81 | 82 | dbus-send --system --print-reply=literal --dest=com.victronenergy.settings "$1"\ 83 | com.victronenergy.BusItem.GetValue &> /dev/null 84 | if (( $? != 0 )); then 85 | logMessage "creating dbus Setting $1" 86 | dbus -y com.victronenergy.settings / AddSettings "%[ {\"path\":\"$1\", \"default\":0.0} ]" &> /dev/null 87 | fi 88 | 89 | dbus -y com.victronenergy.settings "$1" SetValue -- "$2" &> /dev/null 90 | } 91 | 92 | 93 | 94 | # addAllDbusSettings adds settings from DbusSettingsList in the package directory 95 | # the format of each line is: 96 | # {"path":"/Settings/GuiMods/ShortenTankNames", "default":1, "min":0, "max":1} 97 | # min and max are optional 98 | 99 | addAllDbusSettings () 100 | { 101 | local settings 102 | 103 | if [ -f "$scriptDir/DbusSettingsList" ]; then 104 | logMessage "updating dbus Settings" 105 | while read -r line || [[ -n "$line" ]]; do 106 | settings+="$line, " 107 | done < "$scriptDir/DbusSettingsList" 108 | 109 | dbus -y com.victronenergy.settings / AddSettings "%[ $settings ]" &> /dev/null 110 | fi 111 | } 112 | 113 | # same as above but removes them 114 | # typically settings are retained when removing a package so 115 | # the developer must make this call specifically in the setup script's UNINSTALL section 116 | # if they wish to remove the settings 117 | 118 | removeAllDbusSettings () 119 | { 120 | local settings 121 | 122 | if [ -f "$scriptDir/DbusSettingsList" ]; then 123 | logMessage "removing dbus Settings" 124 | while read -r line || [[ -n "$line" ]]; do 125 | settings+=$( echo $line | awk -F[:,] '{print $2, ","}' ) 126 | done < "$scriptDir/DbusSettingsList" 127 | 128 | dbus -y com.victronenergy.settings / RemoveSettings "%[ $settings ]" 129 | fi 130 | } 131 | 132 | 133 | 134 | # removeDbusSettings removes the setting from dbus Settings 135 | # 136 | # all parameters are each a quoted path to the setting to be removed 137 | # e.g., removeDbusSettings "/Settings/foo" "/Settings/bar" 138 | # (including all settings in one dbus call is much faster) 139 | 140 | removeDbusSettings () 141 | { 142 | logMessage "removing dbus Settings $@" 143 | local settings=$(echo "$@" | sed -e s_^_\"_ -e s_\$_\"_ -e s_\ _'", "'_g) 144 | dbus -y com.victronenergy.settings / RemoveSettings "%[ $settings ]" &> /dev/null 145 | } 146 | 147 | 148 | # setSetting updates the dbus setting parameter 149 | # the setting must already exist or the update will fail 150 | # (the setting can not be created without knowing the data type(s)) 151 | # 152 | # $1 is the new value 153 | # $2 is the setting path 154 | 155 | setSetting () 156 | { 157 | # don't do any work if install has already failed 158 | if $installFailed; then 159 | return 160 | fi 161 | 162 | dbus -y com.victronenergy.settings $2 SetValue $1 &> /dev/null 163 | } 164 | 165 | # move a setting from setup options or from previous dbus Setting 166 | # $1 is the setup options path 167 | # $2 is the old dbus path (has priority over setup option) 168 | # $3 is the new dbus path 169 | # dbus paths start with /Settings 170 | # if specified, the setup option file must include a value 171 | # that value has priority over the old dbus parameter 172 | # 173 | # setup options can either contain a value or be a flag file 174 | # for flag files, the file will be empty and the state of the option 175 | # depends on the presence of the file (true) or absense of the file (false) 176 | # 177 | # Note: this function does NOT create or remove any old option or Setting 178 | # use other functions or commands to do so 179 | 180 | moveSetting () 181 | { 182 | # don't do any work if install has already failed 183 | if $installFailed; then 184 | return 185 | fi 186 | 187 | local setupOption="$1" 188 | local oldDbusPath=$2 189 | local newDbusPath=$3 190 | 191 | if [ ! -z "$oldDbusPath" ]; then 192 | oldSetting=$(dbus-send --system --print-reply=literal --dest=com.victronenergy.settings\ 193 | $oldDbusPath com.victronenergy.BusItem.GetValue 2> /dev/null | awk '{print $3}') 194 | elif [ ! -z $setupOption ]; then 195 | if [ -f "$setupOption" ]; then 196 | oldSetting=$(cat "$setupOption") 197 | # flag file - old setting is true (1) 198 | if [ -z $oldSetting ]; then 199 | oldSetting=1 200 | fi 201 | # file did not exist - assume a false value for a flag file 202 | else 203 | oldSetting=0 204 | fi 205 | else 206 | oldSetting="" 207 | fi 208 | if [ ! -z $oldSetting ] && [ ! -z "$newDbusPath" ]; then 209 | dbus -y com.victronenergy.settings $newDbusPath SetValue $oldSetting &> /dev/null 210 | fi 211 | } 212 | -------------------------------------------------------------------------------- /HelperResources/EssentialResources: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # EssentialResources for SetupHelper 4 | # contains a variables necessary for all setup helper scripts 5 | # 6 | # sourced from IncludeHelpers, packageManagerEnd.sh and reinstallMods 7 | 8 | # get the full, unambiguous path to this script (and therefore to script that sourced it) 9 | scriptDir="$( cd "$(dirname $0)" >/dev/null 2>&1 ; /bin/pwd -P )" 10 | packageRoot="$( dirname $scriptDir )" 11 | packageName=$( basename "$scriptDir" ) 12 | 13 | shortScriptName=$(basename "$scriptDir")/$(basename "$0") 14 | fullScriptName="$scriptDir/$(basename "$0")" 15 | 16 | if [ -f "/opt/victronenergy/version" ]; then 17 | venusVersion="$(cat /opt/victronenergy/version | head -n 1)" 18 | else 19 | venusVersion="" 20 | fi 21 | 22 | installedVersionPrefix="/etc/venus/installedVersion-" 23 | installedVersionFile="$installedVersionPrefix"$packageName 24 | 25 | installedFilesDir="/etc/venus/installedModifications" 26 | installedFilesList="$installedFilesDir/installedFiles"-$packageName 27 | installedServicesList="$installedFilesDir/installedServices"-$packageName 28 | 29 | 30 | # obsolete - use installedVersion 31 | installedFlagPrefix="/etc/venus/inInstalled-" 32 | installedFlag="$installedFlagPrefix"$packageName 33 | 34 | # set up pointers to package files 35 | # based on the actual package for compatibility with older packages 36 | pkgFileSets="$scriptDir/FileSets" 37 | fileSet="$pkgFileSets/$venusVersion" 38 | versionIndependentFileSet="$pkgFileSets/VersionIndependent" 39 | # location of patch files 40 | patchSourceDir="$pkgFileSets/PatchSource" 41 | altOrigFileDir="$pkgFileSets/AlternateOriginals" 42 | 43 | servicesDir="$scriptDir/services" 44 | 45 | 46 | # LogHandler functions and variables 47 | 48 | # enable logging to console 49 | # scripts can disable logging by setting 50 | # logToConsole to false AFTER sourcing EssentialResources 51 | logToConsole=true 52 | 53 | # write a message to log file and console 54 | 55 | logMessage () 56 | { 57 | # to console 58 | if $logToConsole ; then 59 | echo "$*" 60 | fi 61 | 62 | # to setup helper log 63 | echo "$shortScriptName: $*" | tai64n >> $logFile 64 | } 65 | 66 | # create log file and directory tree if it does not exist yet 67 | logDir="/var/log/PackageManager" 68 | logFile="$logDir/current" 69 | if ! [ -e "$logDir" ]; then 70 | mkdir -p "$logDir" 71 | touch "$logFile" 72 | logMessage "creating log file and directory" 73 | fi 74 | 75 | oldLogFile="/var/log/SetupHelper" 76 | if [ -e "$oldLogFile" ]; then 77 | if (( $( tail -5 "$oldLogFile" | grep -c "WARNING: this log file no longer used" ) == 0 )); then 78 | echo "WARNING: this log file no longer used" >> "$oldLogFile" 79 | echo " SetupHelper now logged to /var/log/PackageManager/current" >> "$oldLogFile" 80 | fi 81 | fi 82 | 83 | 84 | # rc local file that calls reinstallMods 85 | # rcS.local avoids conflicts with mods that blindly replace /data/rc.local 86 | rcLocal="/data/rcS.local" 87 | 88 | # defined exit codes - must be consistent between all setup scripts and reinstallMods 89 | # and PackageManager.py 90 | EXIT_SUCCESS=0 91 | EXIT_REBOOT=123 92 | EXIT_RESTART_GUI=124 93 | EXIT_ERROR=255 # unknown error 94 | EXIT_INCOMPATIBLE_VERSION=254 95 | EXIT_INCOMPATIBLE_PLATFORM=253 96 | EXIT_FILE_SET_ERROR=252 97 | EXIT_OPTIONS_NOT_SET=251 98 | EXIT_RUN_AGAIN=250 99 | EXIT_ROOT_FULL=249 100 | EXIT_DATA_FULL=248 101 | EXIT_NO_GUI_V1=247 102 | EXIT_PACKAGE_CONFLICT=246 103 | EXIT_PATCH_ERROR=245 104 | 105 | 106 | # directory that holds script's options 107 | # options were removed from the script directory so they are preserved when the package is reinstalled 108 | setupOptionsRoot="/data/setupOptions" 109 | setupOptionsDir="$setupOptionsRoot"/$packageName 110 | 111 | # packages managed by SetupHelper 112 | packageListFile="/data/packageList" 113 | 114 | qmlDir=/opt/victronenergy/gui/qml 115 | 116 | 117 | # setInstallFailed sets flags to prevent further install steps 118 | # and insure the package is uninstalled completely 119 | # 120 | # $1 indicates the reason for the failure and will evenutally be uused 121 | # report the failure reason when exiting the script 122 | # 123 | # any remaining paremeters are passed to logMessage 124 | # and also saved in installFailMessage for others to use 125 | # the message is also sent to stderr if not running from the command line 126 | # this allows PackageManager to report the full reason for failure 127 | # 128 | # a setup script can be run from the console, or from another script or program (unattended) 129 | # when running from the console 130 | # setInstallFailed will report errors and return to the caller 131 | # 132 | # if running unattended and if the script action is INSTALL 133 | # during the precheck period (before any system modification) 134 | # setInstallFailed will EXIT WITHOUT RETURNING TO THE CALLER !!!!! 135 | # after the precheck period, system modifications may have been made so 136 | # the scriptAction is changed to UNINSTALL so the modifictions can be reversed 137 | # otherwise, setInstallFailed just logs the error 138 | # 139 | # installFailed is set here so that additional install operations will not be performed 140 | 141 | installFailed=false 142 | installExitReason=$EXIT_ERROR 143 | uninstallExitReason=$EXIT_ERROR 144 | installFailMessage="" 145 | installPreChecks=true 146 | installFailCount=0 147 | uninstallFailed=false 148 | 149 | setInstallFailed () 150 | { 151 | local reason 152 | 153 | (( installFailCount += 1 )) 154 | if [ ! -z "$1" ]; then 155 | reason=$1 156 | # no reson specified - use the generaic error exit code 157 | else 158 | reason=EXIT_ERROR 159 | fi 160 | message="${@:2}" 161 | if [ ! -z "$message" ]; then 162 | installFailMessage="$message" 163 | logMessage "ERROR: $message" 164 | # if not running from console, output error to stderr 165 | if ! $logToConsole ; then 166 | echo "$message" >&2 167 | fi 168 | else 169 | installFailMessage="" 170 | fi 171 | 172 | if [ $scriptAction == 'UNINSTALL' ]; then 173 | uninstallExitReason=$reason 174 | uninstallFailed=true 175 | else 176 | installExitReason=$reason 177 | installFailed=true 178 | fi 179 | if ! $userInteraction && [ $scriptAction == 'INSTALL' ]; then 180 | # during "pre-checks" failures occur before any system mofifications 181 | # EXIT NOW - DO NOT RETURN TO THE CALLER !!!!! 182 | if $installPreChecks ; then 183 | exit $installExitReason 184 | # after "pre-checks" system mofifications may already have occured 185 | # so an uninstall needs to follow the install 186 | else 187 | scriptAction='UNINSTALL' 188 | fi 189 | fi 190 | } 191 | 192 | # set global machine type 193 | if [ -f /etc/venus/machine ]; then 194 | machine=$(cat /etc/venus/machine) 195 | else 196 | machine="" 197 | setInstallFailed $EXIT_INCOMPATIBLE_PLATFORM "can't determine Venus device type" 198 | fi 199 | 200 | # make sure rootfs is mounted R/W & and resized to allow space for replacement files 201 | # arbitrary minimum size of 3 MB 202 | # this needs to be called before root fs mods are made. 203 | # CommonResources calls this but if you source a subset of helper resources 204 | # that script needs to find a place to call updateRootToRW 205 | 206 | updateRootToReadWrite () 207 | { 208 | if ! $installFailed; then 209 | rootMinimumSize=3 210 | availableSpace=$(df -m / | tail -1 | awk '{print $4}') 211 | 212 | # remount read-write 213 | if (( $(mount | grep ' / ' | grep -c 'rw') == 0 )); then 214 | # only remount read-write for CCGX 215 | if [ "$machine" == "ccgx" ]; then 216 | if [ -f /opt/victronenergy/swupdate-scripts/remount-rw.sh ]; then 217 | logMessage "remounting root read-write" 218 | /opt/victronenergy/swupdate-scripts/remount-rw.sh 219 | fi 220 | # remount and resize for other platforms 221 | elif [ -f /opt/victronenergy/swupdate-scripts/resize2fs.sh ]; then 222 | /opt/victronenergy/swupdate-scripts/resize2fs.sh 223 | availableSpace=$(df -m / | tail -1 | awk '{print $4}') 224 | logMessage "remounting read-write root and resizing - $availableSpace MB now available" 225 | fi 226 | # check to see if remount was successful 227 | if (( $(mount | grep ' / ' | grep -c 'rw') == 0 )); then 228 | setInstallFailed $EXIT_ROOT_FULL "ERROR: unable to remount root read-write - can't continue" 229 | fi 230 | # root already read-write, attempt to resize if space is limited (CCGX can't resize) 231 | elif (( $availableSpace < $rootMinimumSize )); then 232 | if [ "$machine" == "ccgx" ]; then 233 | logMessage "can't resize root on CCGX" 234 | else 235 | if [ -f /opt/victronenergy/swupdate-scripts/resize2fs.sh ]; then 236 | /opt/victronenergy/swupdate-scripts/resize2fs.sh 237 | availableSpace=$(df -m / | tail -1 | awk '{print $4}') 238 | logMessage "resized root - $availableSpace MB now available" 239 | fi 240 | fi 241 | fi 242 | fi 243 | if ! $installFailed; then 244 | # make sure the root partition has space for the package 245 | if (( $availableSpace < $rootMinimumSize )); then 246 | setInstallFailed $EXIT_ROOT_FULL "no room for modified files on root ($availableSpace MB remaining) - can't continue" 247 | fi 248 | fi 249 | } 250 | 251 | 252 | # convert a version string to an integer to make comparisions easier 253 | # the Victron format for version numbers is: vX.Y~Z-large-W 254 | # the ~Z portion indicates a pre-release version so a version without it is "newer" than a version with it 255 | # the -W portion has been abandoned but was like the ~Z for large builds and is IGNORED !!!! 256 | # large builds now have the same version number as the "normal" build 257 | # 258 | # the version string passed to this function allows for quite a bit of flexibility 259 | # any alpha characters are permitted prior to the first digit 260 | # up to 3 version parts PLUS a prerelease part are permitted 261 | # each with up to 4 digits each -- MORE THAN 4 digits is indeterminate 262 | # that is: v0.0.0d0 up to v9999.9999.9999b9999 and then v9999.9999.9999 as the highest priority 263 | # any non-numeric character can be used to separate main versions 264 | # special significance is assigned to single caracter separators between the numeric strings 265 | # b or ~ indicates a beta release 266 | # a indicates an alpha release 267 | # d indicates an development release 268 | # these offset the pre-release number so that b/~ has higher numeric value than any a 269 | # and a has higher value than d separator 270 | # 271 | # a blank version or one without at least one number part is considered invalid 272 | # alpha and beta seperators require at least two number parts 273 | # 274 | # returns 0 if conversion succeeeded, 1 if not 275 | # the value integer is returned in $versionNumber 276 | # a status text string is returned in $versionStringToNumberStatus 277 | # and will include the string passed to the function 278 | # as well as the converted number if successful and the type of release detected 279 | # or an error reason if not 280 | # 281 | 282 | 283 | function versionStringToNumber () 284 | { 285 | local version="$*" 286 | local numberParts 287 | local versionParts 288 | local numberParts 289 | local otherParts 290 | local other 291 | local number=0 292 | local type='release' 293 | 294 | # split incoming string into 295 | # an array of numbers: major, minor, prerelease, etc 296 | # and an array of other substrings 297 | # the other array is searched for releasy type strings and the related offest added to the version number 298 | 299 | read -a numberParts <<< $(echo $version | tr -cs '0-9' ' ') 300 | numberPartsLength=${#numberParts[@]} 301 | if (( $numberPartsLength == 0 )); then 302 | versionNumber=0 303 | versionStringToNumberStatus="$version: invalid, missing major version" 304 | return 1 305 | fi 306 | if (( $numberPartsLength >= 2 )); then 307 | read -a otherParts <<< $(echo $version | tr -s '0-9' ' ') 308 | 309 | for other in ${otherParts[@]}; do 310 | case $other in 311 | 'b' | '~') 312 | type='beta' 313 | (( number += 60000 )) 314 | break ;; 315 | 'a') 316 | type='alpha' 317 | (( number += 30000 )) 318 | break ;; 319 | 'd') 320 | type='develop' 321 | break ;; 322 | esac 323 | done 324 | fi 325 | 326 | # if release all parts contribute to the main version number 327 | # and offset is greater than all prerelease versions 328 | if [ "$type" == "release" ] ; then 329 | (( number += 90000 )) 330 | # if pre-release, last part will be the pre release part 331 | # and others part will be part the main version number 332 | else 333 | (( numberPartsLength-- )) 334 | (( number += 10#${numberParts[$numberPartsLength]} )) 335 | fi 336 | # include core version number 337 | (( number += 10#${numberParts[0]} * 10000000000000 )) 338 | if (( numberPartsLength >= 2)); then 339 | (( number += 10#${numberParts[1]} * 1000000000 )) 340 | fi 341 | if (( numberPartsLength >= 3)); then 342 | (( number += 10#${numberParts[2]} * 100000 )) 343 | fi 344 | 345 | versionNumber=$number 346 | versionStringToNumberStatus="$version:$number $type" 347 | return 0 348 | } 349 | 350 | 351 | 352 | # compares two version strings 353 | # 354 | # missing verions are treated as 0 355 | # 356 | # returns 0 if they are equal 357 | # returns 1 if the first is newer than the second 358 | # returns -1 if the second is newer than the first 359 | 360 | function compareVersions () 361 | { 362 | local versionNumber2 363 | 364 | if [ -z $2 ]; then 365 | versionNumber2=0 366 | else 367 | versionStringToNumber $2 368 | versionNumber2=$versionNumber 369 | fi 370 | if [ -z $1 ]; then 371 | versionNumber=0 372 | else 373 | versionStringToNumber $1 374 | fi 375 | 376 | if (( versionNumber == versionNumber2 ));then 377 | return 0 378 | elif (( versionNumber > versionNumber2 ));then 379 | return 1 380 | else 381 | return -1 382 | fi 383 | } 384 | -------------------------------------------------------------------------------- /HelperResources/IncludeHelpers: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # this script sources helper Resources into the setup script 4 | # 5 | # for backward compatibility, CommonResources in the SetupHelper directory 6 | # links to this file, not CommonResources 7 | # CommonResources previously sourced the other files 8 | # now sourcing all resource files is done from here 9 | # 10 | # only the helper files located is the SetupHelper directory are used 11 | # previous versions chose between this and a helper file set in the package directory 12 | # but changes in SetupHelper to use a local copy of patch made this not possible 13 | # 14 | # this script should be sourced in the setup script before any other activities 15 | 16 | pkgDir="$( cd "$(dirname $0)" >/dev/null 2>&1 ; /bin/pwd -P )" 17 | pkgRoot="$( dirname "$pkgDir")" 18 | pkgName=$( basename $pkgDir ) 19 | helperResourcesDir="$pkgRoot/SetupHelper/HelperResources" 20 | logDir="/var/log/PackageManager" 21 | logFile="$logDir/current" 22 | 23 | if ! [ -e "$helperResourcesDir" ]; then 24 | echo "$pkgName: helper files not found - can't continue" | tee -a "/data/log/SetupHelper" 25 | exit 1 26 | fi 27 | 28 | # if we get here, helper files were located - source the files 29 | helperFileList=( EssentialResources ServiceResources DbusSettingsResources ) 30 | for file in ${helperFileList[@]}; do 31 | if [ -f "$helperResourcesDir/$file" ]; then 32 | source "$helperResourcesDir/$file" 33 | else 34 | echo "$pkgName: helper file $file not found - can't continue" | tee -a "$logFile" 35 | exit 1 36 | fi 37 | done 38 | 39 | # now transfer control to CommonResoures - it may not return ! 40 | if [ -f "$helperResourcesDir/CommonResources" ]; then 41 | source "$helperResourcesDir/CommonResources" 42 | else 43 | echo "$pkgName: helper file CommonResources not found - can't continue" | tee -a "$logFile" 44 | exit 1 45 | fi 46 | 47 | 48 | -------------------------------------------------------------------------------- /HelperResources/LogHandler: -------------------------------------------------------------------------------- 1 | # dummy file to prevent failure with older package setup scripts 2 | -------------------------------------------------------------------------------- /HelperResources/ServiceResources: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #!/bin/bash 4 | # ServiceManager for SetupHelper 5 | # contains a functions to install and remove a package's service 6 | # 7 | # If an active copy of the service already exists, the run and log/run files are updated 8 | # ONLY if there are changes, then the service and/or the logger will be restarted. 9 | # This leaves other files managed by supervise untouched. 10 | # 11 | # in Venus OS between v2.80~10 and v2.90~2, services are stored in /opt/victronenergy/service 12 | # which is overlayed onto /service 13 | # the FS overlay also maintains a "working directory": $overlayWorkDir in this script 14 | # all services need to be added there rather than /service 15 | # Note: service calls (eg svc) are still made on /service/... 16 | # there is an unknown interaction between /service and the overlay source 17 | # so code here operates on both directories 18 | # 19 | # in Venus OS starting with v2.90~3, /service is mounted as a tmpfs (RAM disk) 20 | # /opt/victronenergy/service is copied to /service ONLY at boot time 21 | # so new services need to be copied to /opt/victronenergy/service for boot processing 22 | # AND to /service so they run immediately 23 | # 24 | # svc -u /service/ starts a service that is not already running 25 | # svc -d /service/ stops a service and will not restart 26 | # these are "temporary" and don't survive a system boot 27 | # svc -t /service/ sends the service a TERM command 28 | # if the service was up at the time, it restarts 29 | # if the service was down at the time, it is NOT started 30 | # 31 | # the /service//down flag file controls the state of a service at boot time: 32 | # if the file exists, the service won't start automatically at boot or when created 33 | # if the file does not exist, the service will start at boot or immediately when it is created 34 | # 35 | # if the services manager (svscan) is not up, or the real service directory does not yet exist 36 | # some steps will be skipped to avoid errors in calling daemontools functions 37 | # 38 | # more info here: 39 | # https://cr.yp.to/daemontools/svc.html 40 | # https://cr.yp.to/daemontools/supervise.html 41 | # https://cr.yp.to/daemontools/svstat.html 42 | # https://cr.yp.to/daemontools/svok.html 43 | 44 | victronServicesDir="/opt/victronenergy/service" 45 | overlayWorkDir="/run/overlays/service" 46 | serviceDir="$victronServicesDir" 47 | serviceUsesFsOverlay=false 48 | serviceMountedTmpfs=false 49 | 50 | versionStringToNumber "v2.90~3" 51 | tmpfsStartVersion=$versionNumber 52 | versionStringToNumber "v2.80~10" 53 | overlayStartVersion=$versionNumber 54 | versionStringToNumber $venusVersion 55 | 56 | # service is mounted tmpfs 57 | if (( $versionNumber >= $tmpfsStartVersion )) ; then 58 | serviceMountedTmpfs=true 59 | # service uses a file system overlay 60 | elif (( $versionNumber >= $overlayStartVersion )) ; then 61 | serviceUsesFsOverlay=true 62 | # service is writable in place 63 | else 64 | serviceDir="/service" 65 | fi 66 | 67 | # check to see if services manager is running 68 | svscanIsUp () 69 | { 70 | pgrep -lx svscan &> /dev/null 71 | if (( $? == 0 )) ; then 72 | return 0 73 | else 74 | return 1 75 | fi 76 | } 77 | 78 | # check to see if named service is up 79 | serviceIsUp () 80 | { 81 | if ! svscanIsUp ; then 82 | return 1 83 | elif [ ! -e "/service/$1" ]; then 84 | return 1 85 | elif [ $(svstat "/service/$1" | awk '{print $2}') == "up" ]; then 86 | return 0 87 | else 88 | return 1 89 | fi 90 | } 91 | 92 | 93 | # 94 | # removeService cleanly removes the service 95 | # 96 | 97 | removeService () 98 | { 99 | # no service specified 100 | if (( $# < 1 )); then 101 | return 102 | fi 103 | local serviceName="$1" 104 | 105 | if [ -e "$serviceDir/$serviceName" ]; then 106 | logMessage "removing $serviceName service" 107 | # stop the service if it is currently running 108 | if serviceIsUp $serviceName ; then 109 | svc -d "/service/$1" 110 | fi 111 | if serviceIsUp "$serviceName/log" ; then 112 | svc -d "/service/$1/log" 113 | fi 114 | 115 | # supervise processes may hang around after removing the service so save info and kill them after removal 116 | pids="" 117 | while read -u 9 line; do 118 | read s uid pid ppid vsz rss tty stime time cmd blah <<< "$line" 119 | if [ $cmd == 'supervise' ]; then 120 | pids+="$pid " 121 | elif [ $cmd == 'multilog' ]; then 122 | pids+="$ppid " 123 | fi 124 | done 9<<< $(ps -lw | grep $serviceName) 125 | 126 | # remove the service directory 127 | rm -rf "$serviceDir/$serviceName" 128 | # when /service is mounted as tmpfs, the service needs to be removed from /service also 129 | if $serviceMountedTmpfs && [ -d "/service/$serviceName" ]; then 130 | rm -rf "/service/$serviceName" 131 | # removing the service in the overlayed service directory doesn't remove it from /service 132 | # it needs to be removed from the overlay work directory also 133 | elif $serviceUsesFsOverlay && [ -d "$overlayWorkDir/$serviceName" ] ; then 134 | rm -rf "$overlayWorkDir/$serviceName" 135 | fi 136 | 137 | # now kill the supervise processes 138 | if ! [ -z $pids ]; then 139 | kill $pids 140 | fi 141 | fi 142 | 143 | # remove service from installed services list 144 | if [ -f "$installedServicesList" ]; then 145 | grep -v "$serviceName" "$installedServicesList" | tee "$installedServicesList" > /dev/null 146 | fi 147 | } 148 | 149 | 150 | # installService adds the service to the /service directory or updates an existing one 151 | # 152 | # If the service does not yet exist, it is created 153 | # If the service already exists, installService will 154 | # update the service files then restart the service and the logger 155 | 156 | 157 | # The service usually starts automatically within a few seconds of creation. 158 | # installService waits 10 seconds to see if the service starts on its own 159 | # if not, it will be started 160 | # 161 | # The service may contain a "down" flag file. If present, the service won't be started. 162 | # This allows the service to be started manually later. 163 | # If the down flag is present the service will not start at boot. 164 | # 165 | # 166 | # $1 is the service name -- that is the name of the service in /service 167 | # the package name will be used as the service name if not specified on the command line 168 | # 169 | # $2 is the directory in the script directory to be copied to the service in /service 170 | # (this includes the run and control (down) files) 171 | # the default is 'service' in the package directory 172 | # 173 | # for most packages with one service, the defaults are fine 174 | # however if a package needs to install more than one service 175 | # then the service name and directory must be specified 176 | # installService "PackageManager" "servicePM" 177 | # installService "SetupHelper" "serviceSH" 178 | # servicePM/run would include a call to /data/SetupHelper/PackageManager.py 179 | # serviceSH/run would include a call to /data/SetupHelper/SetupHelper.sh 180 | 181 | installService () 182 | { 183 | # don't do any work if install has already failed 184 | if $installFailed; then 185 | return 186 | fi 187 | 188 | local serviceName="" 189 | if (( $# >= 1 )); then 190 | serviceName=$1 191 | else 192 | serviceName=$packageName 193 | fi 194 | 195 | local servicePath="" 196 | if (( $# >= 2 )); then 197 | servicePath="$scriptDir/$2" 198 | elif [ -e "$servicesDir/$serviceName" ]; then 199 | servicePath="$servicesDir/$serviceName" 200 | elif [ -e "$scriptDir/service" ]; then 201 | servicePath="$scriptDir/service" 202 | fi 203 | 204 | # no service to install 205 | if [ ! -e "$servicePath" ]; then 206 | setInstallFailed $EXIT_ERROR "service $service not found - can't continue" 207 | return 208 | fi 209 | 210 | if [ -L "$serviceDir/$serviceName" ]; then 211 | logMessage "removing old $serviceName service (was symbolic link)" 212 | removeService $serviceName 213 | fi 214 | 215 | # add service to the installed services list (used for uninstallAll) 216 | # do this before actually modifying things just in case there's an error 217 | # that way the uninstall is assured 218 | echo "$serviceName" >> "$installedServicesList" 219 | 220 | # service not yet installed, COPY service's directory (run files) to the service directory(s) 221 | if [ ! -e "/service/$serviceName" ]; then 222 | logMessage "installing $serviceName service" 223 | 224 | cp -R "$servicePath" "$serviceDir/$serviceName" 225 | if $serviceMountedTmpfs && [ -d "/service" ] ; then 226 | cp -R "$servicePath" "/service/$serviceName" 227 | fi 228 | # if down flag is NOT set, check every second for service to start automatically 229 | # then start it here if it is not running after 10 seconds 230 | if [ -f "$serviceDir/$serviceName/down" ]; then 231 | logMessage "$serviceName not (re)started - must be started manually (down flag set)" 232 | elif ! svscanIsUp ; then 233 | logMessage "services manager (svscan) not yet up - $serviceName should start automatically later" 234 | else 235 | local delayCount=10 236 | local serviceRunning=false 237 | while (( $delayCount > 0 )); do 238 | if serviceIsUp $serviceName ; then 239 | serviceRunning=true 240 | break 241 | fi 242 | # only report wait once 243 | if (( delayCount == 10 )); then 244 | echo "waiting for $serviceName service to start" 245 | fi 246 | sleep 1 247 | (( delayCount-- )) 248 | done 249 | if $serviceRunning; then 250 | logMessage "service $serviceName running" 251 | else 252 | logMessage "starting $serviceName service" 253 | svc -u "/service/$serviceName" 254 | if [ -e "/service/$serviceName/log" ] && ! serviceIsUp "$serviceName/log" ; then 255 | svc -u "/service/$serviceName/log" 256 | fi 257 | fi 258 | fi 259 | 260 | # service already installed - only copy changed files, then restart service if it is running 261 | else 262 | if [ -f "$servicePath/run" ]; then 263 | cmp -s "$servicePath/run" "$serviceDir/$serviceName/run" > /dev/null 264 | if (( $? != 0 )); then 265 | logMessage "updating $serviceName run file" 266 | cp "$servicePath/run" "$serviceDir/$serviceName/run" 267 | if $serviceMountedTmpfs ; then 268 | cp "$servicePath/run" "/service/$serviceName/run" 269 | fi 270 | fi 271 | if serviceIsUp $serviceName ; then 272 | svc -t "/service/$serviceName" 273 | fi 274 | fi 275 | # log needs to be handled separtely including a restart 276 | if [ -f "$servicePath/log/run" ]; then 277 | cmp -s "$servicePath/log/run" "$serviceDir/$serviceName/log/run" > /dev/null 278 | if (( $? != 0 )); then 279 | logMessage "updating $serviceName log/run file" 280 | cp "$servicePath/log/run" "$serviceDir/$serviceName/log/run" 281 | if $serviceMountedTmpfs ; then 282 | cp "$servicePath/log/run" "/service/$serviceName/log/run" 283 | fi 284 | fi 285 | if serviceIsUp "$serviceName/log" ; then 286 | logMessage "restarting $serviceName logger" 287 | svc -t "/service/$serviceName/log" 288 | fi 289 | fi 290 | fi 291 | } 292 | -------------------------------------------------------------------------------- /HelperResources/VersionResources: -------------------------------------------------------------------------------- 1 | # dummy file to prevent failure with older package setup scripts 2 | -------------------------------------------------------------------------------- /HelperResources/version: -------------------------------------------------------------------------------- 1 | ../version -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | The SetupHelper package provides: 4 | - a mechanism to automatically reinstall packages following a Venus OS update 5 | - an automatic update mechanism to keep packages up to date from GitHub archives or USB stick 6 | - control of the automatic download and install from the GUI 7 | - add and remove packages from the system 8 | - manual download, install, uninstall from the GUI 9 | - checks for package conflicts and prevents one package installing over another when the same files are modified 10 | - provides a "conflict resolution" option when such conflicts exist 11 | 12 | - a "blind" install of SetupHelper from SD/USB media 13 | 14 | - a blind uninstall mechanism which optionally includes reinstalling Venus OS 15 | 16 | - backup and restore SOME settings from `com.victronenergy.settings` 17 | 18 | This includes custom logos and copying logs to removable media 19 | - SetupHelper 20 | - PackageManager 21 | - gui 22 | 23 | - Restart or initialize PackageManager 24 | - Restart the GUI 25 | 26 | > [!NOTE] 27 | > Support for firmware prior to v3.10 has been dropped starting with SetupHelper v8.10 28 | > if you are running older versions, change the branch/tag to preV3.10support 29 | > for any packages you wish to run on that firmware 30 | > 31 | > While this branch will remain active, there will be no features added to it 32 | > and only serious bug fixes will be applied. 33 | 34 | # Changes 35 | 36 | **SetupHelper v8** 37 | - adds the ability for multiple packages to modify the same file 38 | 39 | - Packages must be written to "patch" a file rather than "replace" it 40 | 41 | **SetupHelper v7.0** 42 | - adds a conflict resolution mechanism. 43 | 44 | - Packages can identify known conflicts with other packages with a "packageDependencies" list 45 | - One package can specify that other packages must be uninstalled or installed 46 | before allowing the package to be installed 47 | - PackageManager also checks all files that will be modified to see if another package has already modified the same file. 48 | 49 | > [!NOTE] 50 | > All packages should be uninstalled, then reinstalled to create the necessary 51 | > information for these file-based conflicts to be identified. 52 | 53 | If a conflict exists it is reported on in the PackageManager menus and install is blocked. These conflicts can be resolved from within the Package editor menu. 54 | 55 | **SetupHelper v6.0** 56 | 57 | > [!NOTE] 58 | > SetupHelper v6.0 changes significantly from prevous versions 59 | 60 | - providing more automatic installation and installation of files, services and dBus Settings 61 | - v6.0 will install older packages but package setup scripts that utilize the new 62 | automated install and uninstall functions **will not work with SetupHelper v5.x** 63 | 64 | For this reason, packages that rely on the v6.0 setup helper functionality 65 | should also include a copy of the **HelperResources** found in SetupHelper v6.0 and newer 66 | 67 | Sourcing these helpers has also changed in v6.0. But there is also a backward 68 | compatible hook for older packages. 69 | 70 | The new sourcing mechanism can be found in the file `SetupHelper/HelperResources/forSetupScript`. 71 | 72 | # Helper resources 73 | 74 | Other packages use "helper resources" provided by SetupHelper 75 | 76 | Helper Resources simplify the package's setup script and include hooks that PackageManager uses to control installs and uninstalls. 77 | 78 | More information about Setup Helper and how to create packages that use it can be found in the file PackageDevelopmentGuidelines.md in the package directory. 79 | 80 | # Blind Install: 81 | 82 | By far, the easiest way to install SetupHelper is the "blind install" which requires no command-line interaction. 83 | 84 | 1. Download `venus-data.tgz` from the SetupHelper GitHub [repo](https://github.com/kwindrem/SetupHelper/raw/main/venus-data.tgz). 85 | > [!NOTE] 86 | > Mac OS and Safari are set by default to unzip packages. 87 | > The Open "safe" files after downloading (bottom of Safari Preferences General) 88 | > must be disabled in order to retain the zip file. 89 | 90 | 2. copy it to the root of a freshly formatted SD card or USB memory stick 91 | 3. place the media in the GX device (Cerbo, CCGX, etc) 92 | 4. reboot the GX device and allow the system to display the GUI 93 | 94 | - if you are running Venus OS v2.90 and beyond: 95 | - you should find the Package Manager menu at the bottom of the Settings menu 96 | - you should remove the media at this point 97 | 98 | Mechanisms are in place to prevent reinstallation, but removal is still a good idea! 99 | 100 | *If you are running Venus OS **prior to v2.90**, perform these additional steps:* 101 | 102 | 5. reboot the GX device a second time 103 | 6. WHILE the GX device is booting, **REMOVE THE MEDIA** from the GX device *to prevent the next reboot from starting the process all over again.* Failure to do so could disable reinstalls following a Venus OS firmware update !!! 104 | 105 | You should find the Package Manager menu at the bottom of the Settings menu 106 | 107 | > [!CAUTION] 108 | > Prior to v2.90, this mechanism overwrites /data/rcS.local !!!! 109 | > If you are using rcS.local to perform boot-time activities, 110 | > /data/rcS.local must be recreated following this "blind" install 111 | > 112 | > Note that SetupHelper also uses /data/rcS.local for 113 | > reinstallation following a firmware update so use caution in 114 | > recreating rcS.local. 115 | 116 | 117 | Another way to install SetupHelper is to use the following from the command line of the GX device: 118 | 119 | ```bash 120 | wget -qO - https://github.com/kwindrem/SetupHelper/archive/latest.tar.gz | tar -xzf - -C /data 121 | mv -f /data/SetupHelper-latest /data/SetupHelper 122 | /data/SetupHelper/setup 123 | ``` 124 | 125 | Once SetupHelper is installed, updates to it and other packages can be performed through the GUI using the PackageManager menus. 126 | 127 | > [!CAUTION] 128 | > Package Manager allows uninstalling SetupHelper. 129 | > 130 | > This can not be undone since the menus to control Package Manager will go away. 131 | You would need to use the Blind Install or run /data/SetupHelper/setup again to reinstall SetupHelper 132 | > 133 | > Note that removal does not actually remove the package so other setup scripts 134 | > will continue to function. 135 | 136 | > [!NOTE] 137 | > You can install other packages using wget as described above. 138 | > Or you can download the .tgz file and put that on a USB stick and plug that into the GX device. 139 | > 140 | > PackageManager will detect the file and install the package. 141 | 142 | 143 | # ssh access: 144 | 145 | Setting up ssh access with ssh keys is highly recommended for any system, 146 | but especially when installing third party extensions to Venus OS. 147 | Attaching a serial terminal for direct console access is another option, 148 | especially if you don't have a network setup. 149 | 150 | [This document](https://www.victronenergy.com/live/ccgx:root_access) describes ssh access and also serial terminal connections on Cerbo GX. 151 | 152 | Remote ssh access is now available via tailscale using the **TailscaleGX** package 153 | 154 | # System Recovery: 155 | 156 | It is unlikely, but some users have reported a package install leaving their system unresponsive or with a nonfunctional GUI (white screen). In this case, your options depend on the current state of the system. 157 | 158 | 1. (as always) reboot. This may clear the problem. 159 | 160 | 2. if you have a functioning GUI (either locally or via remote console, see if you can access the PackageManager menu. 161 | - If so, you can remove pacakges one at a time from there. 162 | - If you find an offeding package, post an issue to the GitHub repo for that package and include: 163 | - Platform (Cerbo, CCGX, Raspberry PI, etc) 164 | - Venus OS firmware version 165 | - Run a Settings backup and post the logs.zip file on the removable media. 166 | - Remove SetupHelper last since once you do, you loose the PackageManager menus! 167 | 168 | 3. if you have terminal or ssh access, try running the package setup scripts to uninstall packages one at a time. 169 | 170 | 4. try booting to the previous Venus OS version (in Stored backup firmware) 171 | Then perform a fresh Online firmware update to the latest version or use the .swu update via removable media. 172 | 173 | Use the Settings / Firmware / Stored backup firmware menu if you have GUI access. 174 | 175 | If you don't have GUI access, you can also switch to the backup version from the command line: 176 | 177 | ```bash 178 | /opt/victronenergy/swupdate-scripts/set-version.sh 2 179 | ``` 180 | 181 | You can also force a firmware update from the command line if you have ssh or terminal access: 182 | - For on-line updates: 183 | ```bash 184 | /opt/victronenergy/swupdate-scripts/check-swupdate.sh -force -update 185 | ``` 186 | - For updates from removable media: 187 | ```bash 188 | /opt/victronenergy/swupdate-scripts/check-swupdate.sh -force -update -offline 189 | ``` 190 | 191 | 5. If PackageManager is still running, it will detect a file named AUTO_UNINSTALL_PACKAGES on removable media. 192 | - Create a file of that name (no extension, content unimportant) on a USB memory stick or SD card and insert this into the GX device. 193 | 194 | - The system should eventually reboot. In most cases, this should occur within 1-2 minutes. 195 | - After reboot, the system should come up in the stock configuration with no packages installed. 196 | 197 | - If the system does not reboot, it is likely PackageManager is no longer running, so try other options. 198 | 199 | - Remember to remove the media containing the `AUTO_UNINSTALL_PACKAGES` file to this will be repeated the next time PackageManager runs. 200 | 201 | 6. perform the Blind uninstall procedure below. 202 | 203 | **Finally:** 204 | - If you are running on a Raspberry PI, you can reimage the system SD card. 205 | 206 | - If you have a Cerbo, you can reimage it using this procedure: 207 | https://community.victronenergy.com/questions/204255/cerbo-gx-bricked-how-to-recover.html 208 | 209 | > [!NOTE] 210 | > This will wipe out all settings and you'll need to reconfigure the GX device from scratch. 211 | 212 | - The Victron "restore factory default" procedure can be used to will wipe out all settings. 213 | - You'll need to reconfigure the GX device from scratch. 214 | - However, it will NOT replace the operating system and Victron application, nor will it uninstall any packages. 215 | - You will most likely be locked out of ssh access since log-in information and ssh keys 216 | are stored in the /data partition which is completey erased by this procedure. 217 | - For this reason, I do not recommend using this as part of your attempt to recover a system with no GUI. 218 | 219 | 220 | # Blind UNINSTALL: 221 | 222 | A blind uninstall mechanism is provided to recover a system with an unresponsive GUI (white screen) or no ssh/terminal access. 223 | This will run all package setup scripts to uninstall that package from system files. 224 | 225 | In addition to uninstalling all packages, the blind uninstall can optionally reinstall VenusOS. To do so, include a `.swu` file for the platform and desired firmware version on the removable media containing the blind uninstall `venus-data.tar.gz` file. 226 | 227 | The archive for this is named `venus-data.UninstallPackages.tar.gz`. 228 | 229 | 1. Copy `venus-data.UninstallPackages.tar.gz` to a USB memory stick or SD card 230 | 2. Rename the copy to `venus-data.tar.gz` 231 | 3. Plug the removable media into the GX device 232 | 4. Reboot, wait 2 minutes and reboot a second time 233 | 5. When the system automatically reboots after the second manual one, remove the media. 234 | You should eventually see the GUI on the local display if there is one 235 | or be able to connect via remote console. 236 | 237 | > [!CAUTION] 238 | > Removing media or power cycling the GX device during the uninstall, 239 | > especially if reinstalling firmware could render the system unresponsive! 240 | > Wait to see the GUI before removing media or power cycling. 241 | 242 | Note that a firmware update can take several minutes to complete but will eventually reboot. 243 | 244 | When the blind uninstall finishes, `venus-data-tar.gz` file on the removable media 245 | is renamed to `venus-data.UninstallPackages.tar.gz` so that the blind install will run only once. 246 | This renaming is necessary to prevent a loop where the system uninstalls and reboots. 247 | 248 | # System automatic configuration and package installation: 249 | 250 | It is possible to use SetupHelper to set up a new system based on a template saved from a working system. 251 | - Setup the working system the way you want the new system to behave including custom icons, 252 | - then perform a Settings backup. 253 | - Remove the flash drive from the GX device and plug into a computer that has internet access. 254 | - Copy `venus-data.tgz` from the SetupHelper GitHub repo to the same flash drive. 255 | - If you wish packages to also be installed, copy the package -latest.tgz file from those repos as well. 256 | - Create `SETTINGS_AUTO_RESTORE` on the flash drive (contents don't matter - file may be empty). 257 | - Create `AUTO_INSTALL_PACKAGES` on the flash drive as well. 258 | - Place the flash drive into the GX device to be configured and reboot (once for v2.90 or twice for prior versions). 259 | - **REMOVE THE FLASH DRIVE** after you have verified that all packages have been installed (check Active packages in PackageManager). 260 | -------------------------------------------------------------------------------- /blindInstall/SetupHelperVersion: -------------------------------------------------------------------------------- 1 | v8.33 2 | -------------------------------------------------------------------------------- /blindInstall/blindInstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # this script is part of a "blind install" archive which installs SetupHelper 4 | # without user interaction. Simply inserting media into the GX device 5 | # and rebooting once or twice (see below) will install SetupHelper 6 | # 7 | # the process makes use of the Venus OS update-data.sh script run during system boot 8 | # archives named "venus-data.tgz" are unpacked during boot 9 | # overriting matching content in /data 10 | # 11 | # this archive unpacks to: 12 | # /data/SetupHelper-blind to avoid overwriting an existing copy of SetupHelper 13 | # /data/rc for the pre/post scripts (not used prior to v2.90) 14 | # /data/rcS.local (used prior to v2.90) 15 | # (overwrites any current file - restored as of v2.90 but not before!) 16 | # if versions of /data/SetupHelper-blind and the installed version of SetupHelper 17 | # DIFFER, OR if SetupHelper is NOT INSTALLED, 18 | # SetupHelper-blind replaces SetupHelper and the setup script is run 19 | # 20 | # prior to v2.90: 21 | # the first reboot, unpacks the archive replacing the itmes listed above 22 | # Venus must be rebooted a second time 23 | # The second reboot: 24 | # runs /data/rcS.local included in the archive 25 | # rcS.local compares versions then runs blindInstall.sh if appropriate 26 | # 27 | # starting with v2.90: 28 | # pre-hook.sh and post-hook.sh scripts are run before and after the archive is unpacked 29 | # /data/rcS.local is saved in pre-hook.sh and restored in post-hook.sh. 30 | # The /data/rcS.local file included in the archive is never executed 31 | # In stead, post-hook.sh performs the version checks and calls blindInstall.sh 32 | # if appropriate. This eliminates the second reboot ! 33 | # In order to check versions prior to unpacking the archive, 34 | # the SetupHelper version is duplicated in the rc folder which unpacks to /data 35 | # BEFORE the SetupHelper-blind is unpacked. 36 | # 37 | # a call to /data/SetupHelper/reinstallMods is appended to rcS.local by all setup scripts 38 | # using SetupHelper CommonResources. 39 | # That call is included in the blind install rcS.local so that if the media is left inserted 40 | # subsequent reboots will still check for reinstalls (applies only to firmwire before v2.90) 41 | # 42 | # the rcS.local from the blindInstall is removed/restored at the end of blindInstall.sh 43 | # SetupHelper/setup creates a new one or appends to the original rcS.local 44 | # 45 | # blindInstall.sh is run in the background so it can wait for dbus Settings resources 46 | # to become available before running the package install script. 47 | # 48 | 49 | source "/data/SetupHelper-blind/HelperResources/EssentialResources" 50 | source "/data/SetupHelper-blind/HelperResources/LogHandler" 51 | logToConsole=false 52 | 53 | logMessage "starting" 54 | 55 | # wait until dbus settings are active 56 | while [ $(dbus -y | grep -c "com.victronenergy.settings") == 0 ]; do 57 | logMessage "waiting for dBus settings" 58 | sleep 1 59 | done 60 | 61 | sleep 2 62 | 63 | setupHelperBlind='/data/SetupHelper-blind' 64 | setupHelperStored='/data/SetupHelper' 65 | 66 | # move the extracted archive into position and run the setup script 67 | if [ -e "$setupHelperBlind" ]; then 68 | if [ -e "$setupHelperStored" ]; then 69 | logMessage "removing previous SetupHelper" 70 | rm -rf "$setupHelperStored" 71 | fi 72 | logMessage "moving SetupHelper archive into position" 73 | mv "$setupHelperBlind" "$setupHelperStored" 74 | else 75 | logMessage "SetupHelper archive not found - no changes to package" 76 | fi 77 | 78 | # restore /data/rcS.local from pre-hook backup 79 | if [ -f /data/rcS.local.orig ] ; then 80 | logMessage "restoring rcS.local" 81 | mv /data/rcS.local.orig /data/rcS.local 82 | # if rcS.local was from blind install - remove it 83 | elif [ -f "/data/rcS.local" ]; then 84 | if [ $(grep -c "blind install" /data/rcS.local) > 0 ]; then 85 | logMessage "removing rcS.local from blind install" 86 | rm "/data/rcS.local" 87 | fi 88 | fi 89 | 90 | # run the setup script 91 | if [ -f "$setupHelperStored/setup" ]; then 92 | logMessage "installing SetupHelper" 93 | "$setupHelperStored/setup" install auto 94 | else 95 | logMessage "error - can't install SetupHelper" 96 | fi 97 | 98 | # remove the blind install SetupHelper from the archive if still present 99 | rm -rf "$setupHelperBlind" 100 | 101 | logMessage "completed" 102 | 103 | -------------------------------------------------------------------------------- /blindInstall/post-hook.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # this script is part of a "blind install" archive which installs SetupHelper 4 | # without user interaction. Simply inserting media into the GX device 5 | # and rebooting once or twice (see below) will install SetupHelper 6 | # 7 | # the process makes use of the Venus OS update-data.sh script run during system boot 8 | # archives named "venus-data.tgz" are unpacked during boot 9 | # overriting matching content in /data 10 | # 11 | # this archive unpacks to: 12 | # /data/SetupHelper-blind to avoid overwriting an existing copy of SetupHelper 13 | # /data/rc for the pre/post scripts (not used prior to v2.90) 14 | # /data/rcS.local (used prior to v2.90) 15 | # (overwrites any current file - restored as of v2.90 but not before!) 16 | # if versions of /data/SetupHelper-blind and the installed version of SetupHelper 17 | # DIFFER, OR if SetupHelper is NOT INSTALLED, 18 | # SetupHelper-blind replaces SetupHelper and the setup script is run 19 | # 20 | # prior to v2.90: 21 | # the first reboot, unpacks the archive replacing the itmes listed above 22 | # Venus must be rebooted a second time 23 | # The second reboot: 24 | # runs /data/rcS.local included in the archive 25 | # rcS.local compares versions then runs blindInstall.sh if appropriate 26 | # 27 | # starting with v2.90: 28 | # pre-hook.sh and post-hook.sh scripts are run before and after the archive is unpacked 29 | # /data/rcS.local is saved in pre-hook.sh and restored in post-hook.sh. 30 | # The /data/rcS.local file included in the archive is never executed 31 | # In stead, post-hook.sh performs the version checks and calls blindInstall.sh 32 | # if appropriate. This eliminates the second reboot ! 33 | # In order to check versions prior to unpacking the archive, 34 | # the SetupHelper version is duplicated in the rc folder which unpacks to /data 35 | # BEFORE the SetupHelper-blind is unpacked. 36 | # 37 | # a call to /data/SetupHelper/reinstallMods is appended to rcS.local by all setup scripts 38 | # using SetupHelper CommonResources. 39 | # That call is included in the blind install rcS.local so that if the media is left inserted 40 | # subsequent reboots will still check for reinstalls (applies only to firmwire before v2.90) 41 | # 42 | # the rcS.local from the blindInstall is removed/restored at the end of blindInstall.sh 43 | # SetupHelper/setup creates a new one or appends to the original rcS.local 44 | # 45 | # blindInstall.sh is run in the background so it can wait for dbus Settings resources 46 | # to become available before running the package install script. 47 | # 48 | 49 | logDir="/var/log/PackageManager" 50 | logFile="$logDir/current" 51 | if ! [ "$logDir" ]; then 52 | mkdir -P "$logDir" 53 | fi 54 | logMessage () 55 | { 56 | echo "$*" 57 | echo "blind install post-hook.sh: $*" | tai64n >> "$logFile" 58 | } 59 | 60 | 61 | logMessage "starting" 62 | 63 | # run the blind install script from the SetupHelper-blind 64 | script="/data/SetupHelper-blind/blindInstall/blindInstall.sh" 65 | if [ -f "$script" ]; then 66 | logMessage "running blindInstall.sh" 67 | nohup "$script" > /dev/null & 68 | fi 69 | 70 | logMessage "completed" 71 | -------------------------------------------------------------------------------- /blindInstall/pre-hook.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # this script is part of a "blind install" archive which installs SetupHelper 4 | # without user interaction. Simply inserting media into the GX device 5 | # and rebooting once or twice (see below) will install SetupHelper 6 | # 7 | # the process makes use of the Venus OS update-data.sh script run during system boot 8 | # archives named "venus-data.tgz" are unpacked during boot 9 | # overriting matching content in /data 10 | # 11 | # this archive unpacks to: 12 | # /data/SetupHelper-blind to avoid overwriting an existing copy of SetupHelper 13 | # /data/rc for the pre/post scripts (not used prior to v2.90) 14 | # /data/rcS.local (used prior to v2.90) 15 | # (overwrites any current file - restored as of v2.90 but not before!) 16 | # if versions of /data/SetupHelper-blind and the installed version of SetupHelper 17 | # DIFFER, OR if SetupHelper is NOT INSTALLED, 18 | # SetupHelper-blind replaces SetupHelper and the setup script is run 19 | # 20 | # prior to v2.90: 21 | # the first reboot, unpacks the archive replacing the itmes listed above 22 | # Venus must be rebooted a second time 23 | # The second reboot: 24 | # runs /data/rcS.local included in the archive 25 | # rcS.local compares versions then runs blindInstall.sh if appropriate 26 | # 27 | # starting with v2.90: 28 | # pre-hook.sh and post-hook.sh scripts are run before and after the archive is unpacked 29 | # /data/rcS.local is saved in pre-hook.sh and restored in post-hook.sh. 30 | # The /data/rcS.local file included in the archive is never executed 31 | # In stead, post-hook.sh performs the version checks and calls blindInstall.sh 32 | # if appropriate. This eliminates the second reboot ! 33 | # In order to check versions prior to unpacking the archive, 34 | # the SetupHelper version is duplicated in the rc folder which unpacks to /data 35 | # BEFORE the SetupHelper-blind is unpacked. 36 | # 37 | # a call to /data/SetupHelper/reinstallMods is appended to rcS.local by all setup scripts 38 | # using SetupHelper CommonResources. 39 | # That call is included in the blind install rcS.local so that if the media is left inserted 40 | # subsequent reboots will still check for reinstalls (applies only to firmwire before v2.90) 41 | # 42 | # the rcS.local from the blindInstall is removed/restored at the end of blindInstall.sh 43 | # SetupHelper/setup creates a new one or appends to the original rcS.local 44 | # 45 | # blindInstall.sh is run in the background so it can wait for dbus Settings resources 46 | # to become available before running the package install script. 47 | # 48 | 49 | logDir="/var/log/PackageManager" 50 | logFile="$logDir/current" 51 | if ! [ "$logDir" ]; then 52 | mkdir -P "$logDir" 53 | fi 54 | logMessage () 55 | { 56 | echo "$*" 57 | echo "blind install pre-hook.sh: $*" | tai64n >> "$logFile" 58 | } 59 | 60 | 61 | logMessage "starting" 62 | 63 | scriptDir="$( cd "$(dirname $0)" >/dev/null 2>&1 ; /bin/pwd -P )" 64 | blindVersionFile="$scriptDir/SetupHelperVersion" 65 | installedVersionFile='/etc/venus/installedVersion-SetupHelper' 66 | setupHelperStored='/data/SetupHelper' 67 | 68 | # remove GitHub project data just in case it ends up on the target 69 | # (it's large (about 20 MB) and could get in the way of package replacement 70 | rm -rf $setupHelperStored/.git 71 | 72 | doInstall=false 73 | # SetupHelper is currently stored in /data 74 | # check to see if it needs to be updated 75 | if [ -d "$setupHelperStored" ]; then 76 | if [ -f "$blindVersionFile" ]; then 77 | blindVersion=$(cat "$blindVersionFile") 78 | else 79 | logMessage "Error: no blind version" 80 | blindVersion="" 81 | fi 82 | if [ -f "$installedVersionFile" ]; then 83 | installedVersion=$(cat "$installedVersionFile") 84 | else 85 | installedVersion="" 86 | fi 87 | 88 | if [ "$installedVersion" != "$blindVersion" ]; then 89 | doInstall=true 90 | fi 91 | # no SetupHelper found, skip version checks and install 92 | else 93 | doInstall=true 94 | fi 95 | # returning with 0 will trigger unpacking and run post-hook.sh 96 | if $doInstall ; then 97 | # back up /data/rcS.local for restoration in post.sh 98 | rm -rf /data/rcS.local.orig 99 | if [ -r /data/rcS.local ] ; then 100 | logMessage "backing up rcS.local" 101 | cp /data/rcS.local /data/rcS.local.orig 102 | fi 103 | logMessage "completed - will do install" 104 | exit 0 105 | # returning non-zero will prevent unpacking 106 | # there won't be an archive to unpack andpost-hook.sh will NOT run 107 | else 108 | logMessage "completed - skipping unpack and install" 109 | exit -1 110 | fi 111 | -------------------------------------------------------------------------------- /blindInstall/rcS.local: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # this script is part of a "blind install" archive which installs SetupHelper 4 | # without user interaction. Simply inserting media into the GX device 5 | # and rebooting once or twice (see below) will install SetupHelper 6 | # 7 | # the process makes use of the Venus OS update-data.sh script run during system boot 8 | # archives named "venus-data.tgz" are unpacked during boot 9 | # overriting matching content in /data 10 | # 11 | # this archive unpacks to: 12 | # /data/SetupHelper-blind to avoid overwriting an existing copy of SetupHelper 13 | # /data/rc for the pre/post scripts (not used prior to v2.90) 14 | # /data/rcS.local (used prior to v2.90) 15 | # (overwrites any current file - restored as of v2.90 but not before!) 16 | # if versions of /data/SetupHelper-blind and the installed version of SetupHelper 17 | # DIFFER, OR if SetupHelper is NOT INSTALLED, 18 | # SetupHelper-blind replaces SetupHelper and the setup script is run 19 | # 20 | # prior to v2.90: 21 | # the first reboot, unpacks the archive replacing the itmes listed above 22 | # Venus must be rebooted a second time 23 | # The second reboot: 24 | # runs /data/rcS.local included in the archive 25 | # rcS.local compares versions then runs blindInstall.sh if appropriate 26 | # 27 | # starting with v2.90: 28 | # pre-hook.sh and post-hook.sh scripts are run before and after the archive is unpacked 29 | # /data/rcS.local is saved in pre-hook.sh and restored in post-hook.sh. 30 | # The /data/rcS.local file included in the archive is never executed 31 | # In stead, post-hook.sh performs the version checks and calls blindInstall.sh 32 | # if appropriate. This eliminates the second reboot ! 33 | # In order to check versions prior to unpacking the archive, 34 | # the SetupHelper version is duplicated in the rc folder which unpacks to /data 35 | # BEFORE the SetupHelper-blind is unpacked. 36 | # 37 | # a call to /data/SetupHelper/reinstallMods is appended to rcS.local by all setup scripts 38 | # using SetupHelper CommonResources. 39 | # That call is included in the blind install rcS.local so that if the media is left inserted 40 | # subsequent reboots will still check for reinstalls (applies only to firmwire before v2.90) 41 | # 42 | # the rcS.local from the blindInstall is removed/restored at the end of blindInstall.sh 43 | # SetupHelper/setup creates a new one or appends to the original rcS.local 44 | # 45 | # blindInstall.sh is run in the background so it can wait for dbus Settings resources 46 | # to become available before running the package install script. 47 | # 48 | 49 | logDir="/var/log/PackageManager" 50 | logFile="$logDir/current" 51 | if ! [ "$logDir" ]; then 52 | mkdir -P "$logDir" 53 | fi 54 | logMessage () 55 | { 56 | echo "blind install rcS.local: $*" | tai64n >> "$logFile" 57 | } 58 | 59 | logMessage "start" 60 | 61 | # remove the pre/post hook directory 62 | # prior to v2.90, this script handles the install and /data/rc is unused 63 | rm -rf "/data/rc" 64 | sync 65 | 66 | setupHelperBlind='/data/SetupHelper-blind' 67 | blindVersionFile="$setupHelperBlind/version" 68 | setupHelperStored='/data/SetupHelper' 69 | storedVersionFile="$setupHelperStored/version" 70 | 71 | # if SetupHelper-blind exists and is different version than what's stored in the package directory 72 | # install the blind package 73 | doInstall=false 74 | if [ -f "$blindVersionFile" ]; then 75 | blindVersion=$(cat "$blindVersionFile") 76 | else 77 | blindVersion="" 78 | fi 79 | if [ -f "$storedVersionFile" ]; then 80 | storedVersion=$(cat "$storedVersionFile") 81 | else 82 | storedVersion="" 83 | fi 84 | if [ "$storedVersion" != "$blindVersion" ]; then 85 | doInstall=true 86 | fi 87 | 88 | if $doInstall ; then 89 | script="$setupHelperBlind/blindInstall/blindInstall.sh" 90 | if [ -f "$script" ]; then 91 | logMessage "running blindInstall.sh" 92 | nohup "$script" > /dev/null & 93 | fi 94 | elif [ -d "$setupHelperBlind" ]; then 95 | logMessage "no update needed - removing SetupHelper-blind" 96 | rm -rf "$setupHelperBlind" 97 | fi 98 | 99 | # SetupHelper reinstall all Venus mods 100 | # this is here in case /data/rcS.local installed by SetupHelper is replaced 101 | # by this one during the first part of the blind install process 102 | script="/data/SetupHelper/reinstallMods" 103 | if [ -f "$script" ]; then 104 | nohup "$script" > /dev/null & 105 | # surpress error message since script probably won't exist after the blind install 106 | elif ! $doInstall ; then 107 | logMessage "Error: no reinstallMods script" 108 | fi 109 | 110 | logMessage "completed" 111 | -------------------------------------------------------------------------------- /blindInstall/rcS.localForUninstall: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # this script is part of a "blind UNINSTALL" archive which UNINSTALLS all packages 4 | # Packages are not removed but marked so PackageManager does not auto intall later 5 | # 6 | # Venus OS will also be reinstalled if a suitable .swu file is found on removable media 7 | 8 | # log activity 9 | logDir="/var/log/PackageManager" 10 | logFile="$logDir/current" 11 | if ! [ "$logDir" ]; then 12 | mkdir -P "$logDir" 13 | fi 14 | logMessage () 15 | { 16 | echo "blindUninstall: $*" 17 | echo "blindUninstall: $*" | tai64n >> "$logDir" 18 | } 19 | 20 | logMessage "--- starting blind uninstall - all packages will be uninstalled" 21 | 22 | # check to see if Venus OS will be reinstalled - actuall reinstall will be done later 23 | swCheckOutput=$(/opt/victronenergy/swupdate-scripts/check-updates.sh -offline -force -check) 24 | if (( $? == 0 )); then 25 | reinstallVenusOs=true 26 | swUpdateVersion=$(echo $swCheckOutput | awk '{print $NF}') 27 | else 28 | reinstallVenusOs=false 29 | swUpdateVersion="none" 30 | fi 31 | 32 | packages=$(ls -d /data/*) 33 | for package in $packages; do 34 | if [ ! -f "$package/version" ]; then 35 | continue 36 | fi 37 | packageName=$(basename $package) 38 | # if not reinstalling Venus OS, uninstall package 39 | if ! $reinstallVenusOs ; then 40 | if [ -f /etc/venus/installedVersion-$packageName ]; then 41 | logMessage "uninstalling $packageName" 42 | "$package/setup" uninstall deferReboot deferGuiRestart auto 43 | fi 44 | fi 45 | # make sure PackageManager does not auto install package 46 | touch "$package/DO_NOT_AUTO_INSTALL" 47 | done 48 | 49 | # remove all installed info in case some were missed in loop above 50 | rm -f /etc/venus/installedVersion* 51 | rm -rf "/etc/venus/installedModifications" 52 | 53 | # rename archive on removable media to prevent blindInstall from running again 54 | drives=$(ls /run/media/) 55 | for drive in $drives ; do 56 | archive="/run/media/$drive/venus-data.tar.gz" 57 | if [ -f "$archive" ]; then 58 | logMessage "renaming venus-data.tar.gz so blindUninstall won't run again" 59 | mv $archive "/run/media/$drive/venus-data.UninstallPackages.tar.gz" 60 | fi 61 | done 62 | 63 | # reinstall Venus OS - done in background so this script can clean up and exit without disrupting the software update 64 | if $reinstallVenusOs ; then 65 | logMessage "reinstalling Venus OS $swUpdateVersion" 66 | nohup sh -c 'sleep 1; /opt/victronenergy/swupdate-scripts/check-updates.sh -offline -force -update' > /dev/null & 67 | # reboot if not reinstalling Venus OS 68 | else 69 | logMessage "rebooting ..." 70 | nohup sh -c 'sleep 1; reboot' > /dev/null & 71 | fi 72 | 73 | # don't run this script again ! 74 | rm -f /data/rcS.local 75 | 76 | logMessage "--- ending blind uninstall" 77 | -------------------------------------------------------------------------------- /defaultPackageList: -------------------------------------------------------------------------------- 1 | # the DEFAULT list of packages managed by SetupHelper 2 | # actual list is based on what is stored on the system 3 | # this list assists in adding new packages 4 | # lines beginning with # are ignored and can be used 5 | # to remove a package from auto and manual updates 6 | # or as comments 7 | # blank lines are ignored 8 | # incomplete lines are ignored 9 | 10 | # Package GitHubUser Tag/branch/version 11 | SetupHelper kwindrem latest 12 | GuiMods kwindrem latest 13 | ShutdownMonitor kwindrem latest 14 | VeCanSetup kwindrem latest 15 | RpiDisplaySetup kwindrem latest 16 | RpiGpioSetup kwindrem latest 17 | TailscaleGX kwindrem latest 18 | 19 | 123SmartBMS-Venus 123electric latest 20 | RpiTemperature TimD1981 latest 21 | BatteryAggregator pulquero latest 22 | dbus-i2c pulquero latest 23 | DCSystemAggregator pulquero latest 24 | gatt-exec-server pulquero latest 25 | dbus-pi pulquero latest 26 | FroniusSmartmeter SirUli main 27 | RemoteGPIO Lucifer06 main 28 | -------------------------------------------------------------------------------- /forSetupScript: -------------------------------------------------------------------------------- 1 | #### add the following lines to the package's setup script 2 | 3 | #### following line incorporates helper resources into this script 4 | source "/data/SetupHelper/HelperResources/IncludeHelpers" 5 | #### end of lines to include helper resources 6 | -------------------------------------------------------------------------------- /genericSetupScript: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # this script will install any package that can use 4 | # the automated install and uninstall mechanisms provided by SetupHelper 5 | # that is, no custom prompting for command line exection 6 | # and no custom installation such as editing replacement files 7 | # 8 | # link the package's setup script to this one: 9 | # ln -s /data/SetupHelper/genericSetupScript /data//setup 10 | 11 | # tell CommonResources to: 12 | # prompt for install/uninstall 13 | # auto install or auto uninstall 14 | # then exit 15 | # CommonResources will NOT return here ! 16 | 17 | standardPromptAndActions='yes' 18 | 19 | #### following line incorporates helper resources into this script 20 | source "/data/SetupHelper/HelperResources/IncludeHelpers" 21 | #### end of lines to include helper resources 22 | 23 | # never returns from CommonResources ! 24 | -------------------------------------------------------------------------------- /gitHubInfo: -------------------------------------------------------------------------------- 1 | kwindrem:latest 2 | -------------------------------------------------------------------------------- /makeVelib_python: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | 5 | # convert a version string to an integer to make comparisions easier 6 | # 7 | # Note: copied from VersionResources 8 | # but also includes code to report duplcates not in the VersionResources version 9 | 10 | function versionStringToNumber () 11 | { 12 | local version="$*" 13 | local numberParts 14 | local versionParts 15 | local numberParts 16 | local otherParts 17 | local other 18 | local number=0 19 | local type='release' 20 | 21 | # split incoming string into 22 | # an array of numbers: major, minor, prerelease, etc 23 | # and an array of other substrings 24 | # the other array is searched for releasy type strings and the related offest added to the version number 25 | 26 | read -a numberParts <<< $(echo $version | tr -cs '0-9' ' ') 27 | numberPartsLength=${#numberParts[@]} 28 | if (( $numberPartsLength == 0 )); then 29 | versionNumber=0 30 | versionStringToNumberStatus="$version: invalid, missing major version" 31 | return 1 32 | fi 33 | if (( $numberPartsLength >= 2 )); then 34 | read -a otherParts <<< $(echo $version | tr -s '0-9' ' ') 35 | for other in ${otherParts[@]}; do 36 | case $other in 37 | 'b' | '~') 38 | type='beta' 39 | (( number += 60000 )) 40 | break ;; 41 | 'a') 42 | type='alpha' 43 | (( number += 30000 )) 44 | break ;; 45 | 'd') 46 | type='develop' 47 | break ;; 48 | esac 49 | done 50 | fi 51 | 52 | # if release all parts contribute to the main version number 53 | # and offset is greater than all prerelease versions 54 | if [ "$type" == "release" ] ; then 55 | (( number += 90000 )) 56 | # if pre-release, last part will be the pre release part 57 | # and others part will be part the main version number 58 | else 59 | (( numberPartsLength-- )) 60 | (( number += 10#${numberParts[$numberPartsLength]} )) 61 | fi 62 | # include core version number 63 | (( number += 10#${numberParts[0]} * 10000000000000 )) 64 | if (( numberPartsLength >= 2)); then 65 | (( number += 10#${numberParts[1]} * 1000000000 )) 66 | fi 67 | if (( numberPartsLength >= 3)); then 68 | (( number += 10#${numberParts[2]} * 100000 )) 69 | fi 70 | 71 | versionNumber=$number 72 | versionStringToNumberStatus="$version:$number $type" 73 | return 0 74 | } 75 | 76 | 77 | totalErrors=0 78 | totalWarnings=0 79 | packageErrors=0 80 | packageWarnings=0 81 | 82 | outputtingProgress=false 83 | 84 | 85 | function logMessage () 86 | { 87 | if $outputtingProgress ; then 88 | clearProgress 89 | fi 90 | echo "$*" 91 | if [[ "$*" == "ERROR"* ]]; then 92 | ((totalErrors++)) 93 | ((packageErrors++)) 94 | elif [[ "$*" == "WARNING"* ]]; then 95 | ((totalWarnings++)) 96 | ((packageWarnings++)) 97 | fi 98 | } 99 | 100 | function outputProgressTick () 101 | { 102 | if ! $outputtingProgress ; then 103 | echo -en "$beginProgressString" 104 | fi 105 | echo -en "$1" 106 | outputtingProgress=true 107 | } 108 | 109 | function clearProgress () 110 | { 111 | # start a new line if outputting ticks 112 | if $outputtingProgress; then 113 | echo 114 | # echo -ne "\r\033[2K" #### erase line 115 | fi 116 | outputtingProgress=false 117 | } 118 | 119 | beginProgressString="" 120 | 121 | function beginProgress () 122 | { 123 | # erase the line but stay on it 124 | if $outputtingProgress ; then 125 | clearProgress 126 | fi 127 | if [ ! -z "$1" ]; then 128 | beginProgressString="$1 " 129 | echo -en "$beginProgressString" 130 | 131 | outputtingProgress=true 132 | fi 133 | } 134 | 135 | 136 | 137 | #### script code begins here 138 | 139 | # attempt to locate SharedUtilities based on the location of this script 140 | # (it is assumed to be in the SetupHelper directory) 141 | # also sets the package root directory based on this also 142 | # and also the stock files base directory 143 | # 144 | # if these are not correct, edit the lines below to set the appropriate values 145 | 146 | scriptDir="$( cd $(dirname "$0") >/dev/null 2>&1 ; /bin/pwd -P )" 147 | packageRoot="$( dirname $scriptDir )" 148 | stockFiles="$packageRoot/StockVenusOsFiles" 149 | pythonLibDir="opt/victronenergy/dbus-systemcalc-py/ext/velib_python" 150 | veLibFiles=( vedbus.py dbusmonitor.py settingsdevice.py ve_utils.py ) 151 | 152 | #### set these as appropriate to your system if the values set above are not correct 153 | #### packageRoot=FILL_THIS_IN_AND_UNCOMMENT_LINE 154 | #### stockFiles=FILL_THIS_IN_AND_UNCOMMENT_LINE 155 | 156 | if [ ! -e "$packageRoot" ]; then 157 | echo "unable to locate package root - can't continue" 158 | exit 159 | elif [ ! -e "$stockFiles" ]; then 160 | echo "unable to locate stock files - can't continue" 161 | exit 162 | fi 163 | 164 | 165 | # make the version list from the directories in stock files 166 | # version lists are sorted so the most recent version is first 167 | tempList=() 168 | stockVersionList=($(ls -d "$stockFiles"/v[0-9]* 2> /dev/null)) 169 | for entry in ${stockVersionList[@]} ; do 170 | version=$(basename $entry) 171 | versionFile="$stockFiles/$version/opt/victronenergy/version" 172 | if [ -f "$versionFile" ]; then 173 | realVersion=$(cat "$versionFile" | head -n 1) 174 | else 175 | logMessage "ERROR version file missing from stock files $version - can't continue" 176 | exit 177 | fi 178 | 179 | if [ $version != $realVersion ]; then 180 | logMessage "ERROR $version name does not mactch Venus $realVersion - can't continue" 181 | exit 182 | fi 183 | if versionStringToNumber $version ; then 184 | tempList+=("$version:$versionNumber") 185 | else 186 | logMessage "ERROR invalid version $versionStringToNumberStatus - not added to list" 187 | fi 188 | done 189 | stockVersionList=( $(echo ${tempList[@]} | tr ' ' '\n' | sort -t ':' -r -n -k 2 | uniq ) ) 190 | stockVersionListLength=${#stockVersionList[@]} 191 | 192 | if (( stockVersionListLength < 2 )); then 193 | logMessage "fewer than 2 versions - nothing to compare" 194 | exit 195 | fi 196 | 197 | if [ -e "$scriptDir/velib_python" ]; then 198 | rm -rf "$scriptDir/velib_python" 199 | fi 200 | mkdir -p "$scriptDir/velib_python" 201 | 202 | for (( i1 = 0; i1 < $stockVersionListLength; i1++ )); do 203 | newVersion=false 204 | IFS=':' read version versionNumber <<< "${stockVersionList[$i1]}" 205 | 206 | if (( i1 == 0 )); then 207 | newVersion=true 208 | else 209 | for file in ${veLibFiles[@]} ; do 210 | file1="$stockFiles/$version/$pythonLibDir/$file" 211 | file2="$stockFiles/$previousVersion/$pythonLibDir/$file" 212 | if ! cmp -s "$file1" "$file2" > /dev/null ; then 213 | logMessage " $file $previousVersion $version differ" 214 | newVersion=true 215 | fi 216 | done 217 | fi 218 | 219 | if $newVersion ; then 220 | if (( i1 == 0 ));then 221 | velibDir="$scriptDir/velib_python/latest" 222 | prevVelibDir="$scriptDir/velib_python/latest" 223 | else 224 | velibDir="$scriptDir/velib_python/$version" 225 | fi 226 | mkdir "$velibDir" 227 | logMessage "new velib_python version $version" 228 | for file in ${veLibFiles[@]} ; do 229 | file1="$stockFiles/$version/$pythonLibDir/$file" 230 | file2="$velibDir/$file" 231 | cp -f "$file1" "$file2" 232 | done 233 | newVersion=false 234 | previousVersion=$version 235 | prevVelibDir="$velibDir" 236 | fi 237 | echo $version > "$prevVelibDir/oldestVersion" 238 | done 239 | -------------------------------------------------------------------------------- /packageManagerEnd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # this script handles the actions AFTER PackageManager.py exits 5 | # that can not be easily performed while it is running: 6 | # 7 | # uninstalling SetupHelper and therefore the PackageManager service 8 | # 9 | # system reboots are also handled here since that needs to happen 10 | # AFTER SetupHelper is uninstalled 11 | # 12 | # GUI restart if needed 13 | # 14 | # this script is called from PackageManager.py just prior to it exiting 15 | # 16 | # PackageManager will pass the following options as appropriate: 17 | # 18 | # 'shUninstall' to uninstall SetupHelper 19 | # 'reboot' to trigger a system reboot 20 | # 'guiRestart' to trigger a GUI restart 21 | # 22 | # all are optional but at least one should be passed or 23 | # this script will exit without doing anything 24 | # 25 | # reboot overrides a guiRestart since the reboot will restart the GUI 26 | 27 | source "/data/SetupHelper/HelperResources/EssentialResources" 28 | logToConsole=false 29 | 30 | logMessage "starting packageManagerEnd.sh" 31 | 32 | shUninstall=false 33 | guiRestart=false 34 | reboot=false 35 | 36 | while [ $# -gt 0 ]; do 37 | case $1 in 38 | "shUninstall") 39 | shUninstall=true 40 | ;; 41 | "guiRestart") 42 | guiRestart=true 43 | ;; 44 | "reboot") 45 | reboot=true 46 | ;; 47 | *) 48 | esac 49 | shift 50 | done 51 | 52 | if $shUninstall || $reboot ; then 53 | waitCount=0 54 | # wait for up to 20 seconds for PM to exit 55 | while true; do 56 | if [ -z "$( pgrep -f PackageManager.py )" ]; then 57 | break 58 | elif (( waitCount == 0 )); then 59 | logMessage "waiting for PackageManager.py to exit" 60 | elif (( waitCount > 20 )); then 61 | logMessage "PackageManager.py DID NOT EXIT - continuing anyway" 62 | break 63 | fi 64 | (( waitCount += 1 )) 65 | sleep 1 66 | done 67 | fi 68 | 69 | if $shUninstall ; then 70 | logMessage ">>>> uninstalling SetupHelper" 71 | setupFile="/data/SetupHelper/setup" 72 | if [ -f "$setupFile" ]; then 73 | $setupFile uninstall runFromPm 74 | returnCode=$( echo $? ) 75 | else 76 | returnCode=$EXIT_SUCCESS 77 | fi 78 | if (( returnCode == $EXIT_REBOOT )); then 79 | reboot=true 80 | elif (( returnCode == $EXIT_RESTART_GUI )); then 81 | guiRestart=true 82 | fi 83 | fi 84 | 85 | if $reboot ; then 86 | logMessage ">>>> REBOOTING ..." 87 | # TODO: add -k for debugging - outputs message but doesn't reboot 88 | shutdown -r now "PackageManager is REBOOTING SYSTEM ..." 89 | elif $guiRestart ; then 90 | if $shUninstall ; then 91 | logMessage ">>>> restarting GUI" 92 | else 93 | logMessage ">>>> restarting GUI and Package Manager" 94 | fi 95 | if [ -e "/service/start-gui" ]; then 96 | svc -t "/service/start-gui" 97 | elif [ -e "/service/gui" ]; then 98 | svc -t "/service/gui" 99 | fi 100 | fi 101 | 102 | logMessage "end packageManagerEnd.sh" 103 | -------------------------------------------------------------------------------- /patch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kwindrem/SetupHelper/3d0779cf9bd24a860003b0ccd3c55d359ac69b7c/patch -------------------------------------------------------------------------------- /rcS.local: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # SetupHelper reinstall 4 | if [ -f /data/SetupHelper/reinstallMods ]; then 5 | nohup /data/SetupHelper/reinstallMods > /dev/null & 6 | fi 7 | -------------------------------------------------------------------------------- /reinstallMods: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # this script is called from /data/rcS.local during system boot 4 | # it checks to see the PackageManager service is installed and if not, 5 | # will install ONLY the PackageManager service 6 | # 7 | # the REINSTALL_PACKAGES flag file is then set so that 8 | # when PackageManger runs, it will do boot-time reinstall checks for all packages 9 | 10 | scriptDir="$( cd "$(dirname $0)" >/dev/null 2>&1 ; /bin/pwd -P )" 11 | helperResourcesDir="$scriptDir/HelperResources" 12 | source "$helperResourcesDir/EssentialResources" 13 | source "$helperResourcesDir/ServiceResources" 14 | 15 | # disable outputting log messages to console 16 | logToConsole=false 17 | 18 | logMessage "reinstallMods starting" 19 | 20 | if [ -f "$setupOptionsDir/DO_NOT_AUTO_INSTALL" ]; then 21 | logMessage "CRITICAL: SetupHelper was manually uninstalled therefore it was not reinstalled" 22 | logMessage " other packages will NOT BE REINSTALLED either !" 23 | 24 | # install PackageManager service 25 | else 26 | # installing the PackageManager service requires remounting root R/W 27 | updateRootToReadWrite 28 | 29 | if ! $installFailed ; then 30 | # install PackageManager service if not yet installed 31 | if ! [ -e "$serviceDir/PackageManager" ]; then 32 | logMessage "installing PackageManager service - PackageManager will reinstall all packages" 33 | installService PackageManager 34 | fi 35 | fi 36 | if ! $installFailed ; then 37 | # notify PackageManager that it needs to check all packages for possible reinstall 38 | # flag file is cleared in PackageManager when all install checks have been made 39 | touch "/etc/venus/REINSTALL_PACKAGES" 40 | logMessage "reinstallMods finished" 41 | else 42 | logMessage "reinstallMods not completed - packages will not be reinstalled" 43 | fi 44 | fi 45 | 46 | 47 | -------------------------------------------------------------------------------- /services/PackageManager/log/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | exec multilog t s25000 n4 /var/log/PackageManager 3 | 4 | -------------------------------------------------------------------------------- /services/PackageManager/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | exec 2>&1 3 | exec /data/SetupHelper/PackageManager.py 4 | 5 | -------------------------------------------------------------------------------- /settingsList: -------------------------------------------------------------------------------- 1 | /Settings/Alarm/Audible 2 | /Settings/Alarm/System/GridLost 3 | /Settings/Alarm/Vebus/HighDcCurrent 4 | /Settings/Alarm/Vebus/HighDcRipple 5 | /Settings/Alarm/Vebus/HighDcVoltage 6 | /Settings/Alarm/Vebus/HighTemperature 7 | /Settings/Alarm/Vebus/InverterOverload 8 | /Settings/Alarm/Vebus/LowBattery 9 | /Settings/Alarm/Vebus/TemperatureSenseError 10 | /Settings/Alarm/Vebus/VeBusError 11 | /Settings/Alarm/Vebus/VoltageSenseError 12 | /Settings/Ble/Service/Pincode 13 | /Settings/CGwacs/AcPowerSetPoint 14 | /Settings/CGwacs/BatteryLife/DischargedTime 15 | /Settings/CGwacs/BatteryLife/Flags 16 | /Settings/CGwacs/BatteryLife/MinimumSocLimit 17 | /Settings/CGwacs/BatteryLife/Schedule/Charge/0/Day 18 | /Settings/CGwacs/BatteryLife/Schedule/Charge/0/Duration 19 | /Settings/CGwacs/BatteryLife/Schedule/Charge/0/Soc 20 | /Settings/CGwacs/BatteryLife/Schedule/Charge/0/Start 21 | /Settings/CGwacs/BatteryLife/Schedule/Charge/1/Day 22 | /Settings/CGwacs/BatteryLife/Schedule/Charge/1/Duration 23 | /Settings/CGwacs/BatteryLife/Schedule/Charge/1/Soc 24 | /Settings/CGwacs/BatteryLife/Schedule/Charge/1/Start 25 | /Settings/CGwacs/BatteryLife/Schedule/Charge/2/Day 26 | /Settings/CGwacs/BatteryLife/Schedule/Charge/2/Duration 27 | /Settings/CGwacs/BatteryLife/Schedule/Charge/2/Soc 28 | /Settings/CGwacs/BatteryLife/Schedule/Charge/2/Start 29 | /Settings/CGwacs/BatteryLife/Schedule/Charge/3/Day 30 | /Settings/CGwacs/BatteryLife/Schedule/Charge/3/Duration 31 | /Settings/CGwacs/BatteryLife/Schedule/Charge/3/Soc 32 | /Settings/CGwacs/BatteryLife/Schedule/Charge/3/Start 33 | /Settings/CGwacs/BatteryLife/Schedule/Charge/4/Day 34 | /Settings/CGwacs/BatteryLife/Schedule/Charge/4/Duration 35 | /Settings/CGwacs/BatteryLife/Schedule/Charge/4/Soc 36 | /Settings/CGwacs/BatteryLife/Schedule/Charge/4/Start 37 | /Settings/CGwacs/BatteryLife/SocLimit 38 | /Settings/CGwacs/BatteryLife/State 39 | /Settings/CGwacs/MaxChargePercentage 40 | /Settings/CGwacs/MaxChargePower 41 | /Settings/CGwacs/MaxDischargePercentage 42 | /Settings/CGwacs/MaxDischargePower 43 | /Settings/CGwacs/MaxFeedInPower 44 | /Settings/CGwacs/OvervoltageFeedIn 45 | /Settings/CGwacs/PreventFeedback 46 | /Settings/CGwacs/RunWithoutGridMeter 47 | /Settings/CanBms/SocketcanCan0/ProductId 48 | /Settings/Canbus/can0/Profile 49 | /Settings/Canbus/can1/Profile 50 | /Settings/Devices/vebus_ttyUSB0/CustomName 51 | /Settings/Devices/vebus_ttyUSB1/CustomName 52 | /Settings/DigitalInput/1/AlarmSetting 53 | /Settings/DigitalInput/1/Count 54 | /Settings/DigitalInput/1/CustomName 55 | /Settings/DigitalInput/1/InvertAlarm 56 | /Settings/DigitalInput/1/InvertTranslation 57 | /Settings/DigitalInput/1/Multiplier 58 | /Settings/DigitalInput/1/Type 59 | /Settings/DigitalInput/2/AlarmSetting 60 | /Settings/DigitalInput/2/Count 61 | /Settings/DigitalInput/2/CustomName 62 | /Settings/DigitalInput/2/InvertAlarm 63 | /Settings/DigitalInput/2/InvertTranslation 64 | /Settings/DigitalInput/2/Multiplier 65 | /Settings/DigitalInput/2/Type 66 | /Settings/DigitalInput/3/AlarmSetting 67 | /Settings/DigitalInput/3/Count 68 | /Settings/DigitalInput/3/CustomName 69 | /Settings/DigitalInput/3/InvertAlarm 70 | /Settings/DigitalInput/3/InvertTranslation 71 | /Settings/DigitalInput/3/Multiplier 72 | /Settings/DigitalInput/3/Type 73 | /Settings/DigitalInput/4/AlarmSetting 74 | /Settings/DigitalInput/4/Count 75 | /Settings/DigitalInput/4/CustomName 76 | /Settings/DigitalInput/4/InvertAlarm 77 | /Settings/DigitalInput/4/InvertTranslation 78 | /Settings/DigitalInput/4/Multiplier 79 | /Settings/DigitalInput/4/Type 80 | /Settings/DigitalInput/5/AlarmSetting 81 | /Settings/DigitalInput/5/Count 82 | /Settings/DigitalInput/5/CustomName 83 | /Settings/DigitalInput/5/InvertAlarm 84 | /Settings/DigitalInput/5/InvertTranslation 85 | /Settings/DigitalInput/5/Multiplier 86 | /Settings/DigitalInput/5/Type 87 | /Settings/DigitalInput/6/AlarmSetting 88 | /Settings/DigitalInput/6/Count 89 | /Settings/DigitalInput/6/CustomName 90 | /Settings/DigitalInput/6/InvertAlarm 91 | /Settings/DigitalInput/6/InvertTranslation 92 | /Settings/DigitalInput/6/Multiplier 93 | /Settings/DigitalInput/6/Type 94 | /Settings/FischerPanda0/AcLoad/Enabled 95 | /Settings/FischerPanda0/AcLoad/Measurement 96 | /Settings/FischerPanda0/AcLoad/QuietHoursStartValue 97 | /Settings/FischerPanda0/AcLoad/QuietHoursStopValue 98 | /Settings/FischerPanda0/AcLoad/StartTimer 99 | /Settings/FischerPanda0/AcLoad/StartValue 100 | /Settings/FischerPanda0/AcLoad/StopTimer 101 | /Settings/FischerPanda0/AcLoad/StopValue 102 | /Settings/FischerPanda0/Alarms/NoGeneratorAtAcIn 103 | /Settings/FischerPanda0/AutoStartEnabled 104 | /Settings/FischerPanda0/BatteryCurrent/Enabled 105 | /Settings/FischerPanda0/BatteryCurrent/QuietHoursStartValue 106 | /Settings/FischerPanda0/BatteryCurrent/QuietHoursStopValue 107 | /Settings/FischerPanda0/BatteryCurrent/StartTimer 108 | /Settings/FischerPanda0/BatteryCurrent/StartValue 109 | /Settings/FischerPanda0/BatteryCurrent/StopTimer 110 | /Settings/FischerPanda0/BatteryCurrent/StopValue 111 | /Settings/FischerPanda0/BatteryService 112 | /Settings/FischerPanda0/BatteryVoltage/Enabled 113 | /Settings/FischerPanda0/BatteryVoltage/QuietHoursStartValue 114 | /Settings/FischerPanda0/BatteryVoltage/QuietHoursStopValue 115 | /Settings/FischerPanda0/BatteryVoltage/StartTimer 116 | /Settings/FischerPanda0/BatteryVoltage/StartValue 117 | /Settings/FischerPanda0/BatteryVoltage/StopTimer 118 | /Settings/FischerPanda0/BatteryVoltage/StopValue 119 | /Settings/FischerPanda0/InverterHighTemp/Enabled 120 | /Settings/FischerPanda0/InverterHighTemp/StartTimer 121 | /Settings/FischerPanda0/InverterHighTemp/StopTimer 122 | /Settings/FischerPanda0/InverterOverload/Enabled 123 | /Settings/FischerPanda0/InverterOverload/StartTimer 124 | /Settings/FischerPanda0/InverterOverload/StopTimer 125 | /Settings/FischerPanda0/MinimumRuntime 126 | /Settings/FischerPanda0/OnLossCommunication 127 | /Settings/FischerPanda0/QuietHours/EndTime 128 | /Settings/FischerPanda0/QuietHours/StartTime 129 | /Settings/FischerPanda0/Soc/Enabled 130 | /Settings/FischerPanda0/Soc/QuietHoursStartValue 131 | /Settings/FischerPanda0/Soc/QuietHoursStopValue 132 | /Settings/FischerPanda0/Soc/StartValue 133 | /Settings/FischerPanda0/Soc/StopValue 134 | /Settings/FischerPanda0/StopWhenAc1Available 135 | /Settings/FischerPanda0/TestRun/Duration 136 | /Settings/FischerPanda0/TestRun/Enabled 137 | /Settings/FischerPanda0/TestRun/Interval 138 | /Settings/FischerPanda0/TestRun/RunTillBatteryFull 139 | /Settings/FischerPanda0/TestRun/SkipRuntime 140 | /Settings/FischerPanda0/TestRun/StartDate 141 | /Settings/FischerPanda0/TestRun/StartTime 142 | /Settings/Generator0/AcLoad/Enabled 143 | /Settings/Generator0/AcLoad/Measurement 144 | /Settings/Generator0/AcLoad/QuietHoursStartValue 145 | /Settings/Generator0/AcLoad/QuietHoursStopValue 146 | /Settings/Generator0/AcLoad/StartTimer 147 | /Settings/Generator0/AcLoad/StartValue 148 | /Settings/Generator0/AcLoad/StopTimer 149 | /Settings/Generator0/AcLoad/StopValue 150 | /Settings/Generator0/Alarms/NoGeneratorAtAcIn 151 | /Settings/Generator0/AutoStartEnabled 152 | /Settings/Generator0/BatteryCurrent/Enabled 153 | /Settings/Generator0/BatteryCurrent/QuietHoursStartValue 154 | /Settings/Generator0/BatteryCurrent/QuietHoursStopValue 155 | /Settings/Generator0/BatteryCurrent/StartTimer 156 | /Settings/Generator0/BatteryCurrent/StartValue 157 | /Settings/Generator0/BatteryCurrent/StopTimer 158 | /Settings/Generator0/BatteryCurrent/StopValue 159 | /Settings/Generator0/BatteryService 160 | /Settings/Generator0/BatteryVoltage/Enabled 161 | /Settings/Generator0/BatteryVoltage/QuietHoursStartValue 162 | /Settings/Generator0/BatteryVoltage/QuietHoursStopValue 163 | /Settings/Generator0/BatteryVoltage/StartTimer 164 | /Settings/Generator0/BatteryVoltage/StartValue 165 | /Settings/Generator0/BatteryVoltage/StopTimer 166 | /Settings/Generator0/BatteryVoltage/StopValue 167 | /Settings/Generator0/InverterHighTemp/Enabled 168 | /Settings/Generator0/InverterHighTemp/StartTimer 169 | /Settings/Generator0/InverterHighTemp/StopTimer 170 | /Settings/Generator0/InverterOverload/Enabled 171 | /Settings/Generator0/InverterOverload/StartTimer 172 | /Settings/Generator0/InverterOverload/StopTimer 173 | /Settings/Generator0/MinimumRuntime 174 | /Settings/Generator0/OnLossCommunication 175 | /Settings/Generator0/QuietHours/Enabled 176 | /Settings/Generator0/QuietHours/EndTime 177 | /Settings/Generator0/QuietHours/StartTime 178 | /Settings/Generator0/Soc/Enabled 179 | /Settings/Generator0/Soc/QuietHoursStartValue 180 | /Settings/Generator0/Soc/QuietHoursStopValue 181 | /Settings/Generator0/Soc/StartValue 182 | /Settings/Generator0/Soc/StopValue 183 | /Settings/Generator0/StopWhenAc1Available 184 | /Settings/Generator0/TestRun/Duration 185 | /Settings/Generator0/TestRun/Enabled 186 | /Settings/Generator0/TestRun/Interval 187 | /Settings/Generator0/TestRun/RunTillBatteryFull 188 | /Settings/Generator0/TestRun/SkipRuntime 189 | /Settings/Generator0/TestRun/StartDate 190 | /Settings/Generator0/TestRun/StartTime 191 | /Settings/Gps/Format 192 | /Settings/Gps/SpeedUnit 193 | /Settings/Gui/AutoBrightness 194 | /Settings/Gui/Brightness 195 | /Settings/Gui/DisplayOff 196 | /Settings/Gui/Language 197 | /Settings/Gui/MobileOverview 198 | /Settings/Gui/StartWithMenuView 199 | /Settings/Gui/TanksOverview 200 | /Settings/GuiMods/AcCurrentLimit/Preset1 201 | /Settings/GuiMods/AcCurrentLimit/Preset2 202 | /Settings/GuiMods/AcCurrentLimit/Preset3 203 | /Settings/GuiMods/AcCurrentLimit/Preset4 204 | /Settings/GuiMods/CustomDcSystemName 205 | /Settings/GuiMods/EnhancedFlowCombineLoads 206 | /Settings/GuiMods/FlowOverview 207 | /Settings/GuiMods/GaugeLimits/AcOutputMaxPower 208 | /Settings/GuiMods/GaugeLimits/AcOutputNonCriticalMaxPower 209 | /Settings/GuiMods/GaugeLimits/AlternatorMaxPower 210 | /Settings/GuiMods/GaugeLimits/BatteryMaxChargeCurrent 211 | /Settings/GuiMods/GaugeLimits/BatteryMaxDischargeCurrent 212 | /Settings/GuiMods/GaugeLimits/CautionPower 213 | /Settings/GuiMods/GaugeLimits/ContiuousPower 214 | /Settings/GuiMods/GaugeLimits/DcSystemMaxCharge 215 | /Settings/GuiMods/GaugeLimits/DcSystemMaxLoad 216 | /Settings/GuiMods/GaugeLimits/MaxAcChargerPower 217 | /Settings/GuiMods/GaugeLimits/MaxAlternatorPower 218 | /Settings/GuiMods/GaugeLimits/MaxChargerPower 219 | /Settings/GuiMods/GaugeLimits/MaxFeedInPower 220 | /Settings/GuiMods/GaugeLimits/MaxFuelCellPower 221 | /Settings/GuiMods/GaugeLimits/MaxWindGenPower 222 | /Settings/GuiMods/GaugeLimits/PeakPower 223 | /Settings/GuiMods/GaugeLimits/PvChargerMaxPower 224 | /Settings/GuiMods/GaugeLimits/PvOnGridMaxPower 225 | /Settings/GuiMods/GaugeLimits/PvOnOutputMaxPower 226 | /Settings/GuiMods/MoveSettings 227 | /Settings/GuiMods/ShortenTankNames 228 | /Settings/GuiMods/ShowBatteryTempOnFlows 229 | /Settings/GuiMods/ShowEnhancedFlowLoadsOnInput 230 | /Settings/GuiMods/ShowEnhancedFlowOverviewTanks 231 | /Settings/GuiMods/ShowEnhancedFlowOverviewTemps 232 | /Settings/GuiMods/ShowGauges 233 | /Settings/GuiMods/ShowInactiveFlowTiles 234 | /Settings/GuiMods/ShowRelayOverview 235 | /Settings/GuiMods/ShowTanksTempsDigIn 236 | /Settings/GuiMods/ShowTileOverview 237 | /Settings/GuiMods/TemperatureScale 238 | /Settings/GuiMods/TimeFormat 239 | /Settings/GuiMods/UseEnhancedFlowOverview 240 | /Settings/GuiMods/UseEnhancedGridParallelFlowOverview 241 | /Settings/GuiMods/UseEnhancedMobileOverview 242 | /Settings/GuiMods/UsedEnhancedGeneratorOverview 243 | /Settings/PackageManager/AutoInstall 244 | /Settings/PackageManager/Count 245 | /Settings/PackageManager/GitHubAutoDownload 246 | /Settings/Pump0/AutoStartEnabled 247 | /Settings/Pump0/Mode 248 | /Settings/Pump0/StartValue 249 | /Settings/Pump0/StopValue 250 | /Settings/Pump0/TankService 251 | /Settings/Relay/0/CustomName 252 | /Settings/Relay/0/InitialState 253 | /Settings/Relay/0/Show 254 | /Settings/Relay/1/CustomName 255 | /Settings/Relay/1/Function 256 | /Settings/Relay/1/InitialState 257 | /Settings/Relay/1/Polarity 258 | /Settings/Relay/1/Show 259 | /Settings/Relay/2/CustomName 260 | /Settings/Relay/2/InitialState 261 | /Settings/Relay/2/Show 262 | /Settings/Relay/3/CustomName 263 | /Settings/Relay/3/InitialState 264 | /Settings/Relay/3/Show 265 | /Settings/Relay/4/CustomName 266 | /Settings/Relay/4/InitialState 267 | /Settings/Relay/4/Show 268 | /Settings/Relay/5/CustomName 269 | /Settings/Relay/5/InitialState 270 | /Settings/Relay/5/Show 271 | /Settings/Relay/6/InitialState 272 | /Settings/Relay/Function 273 | /Settings/Relay/Polarity 274 | /Settings/Services/AccessPoint 275 | /Settings/Services/BleSensors 276 | /Settings/Services/Bluetooth 277 | /Settings/Services/Bol 278 | /Settings/Services/Console 279 | /Settings/Services/FischerPandaAutoStartStop 280 | /Settings/Services/Modbus 281 | /Settings/Services/MqttLocal 282 | /Settings/Services/MqttLocalInsecure 283 | /Settings/Services/MqttN2k 284 | /Settings/Services/MqttVrm 285 | /Settings/Services/NodeRed 286 | /Settings/Services/SignalK 287 | /Settings/ShutdownMonitor/ExternalSwitch 288 | /Settings/System/AccessLevel 289 | /Settings/System/AutoUpdate 290 | /Settings/System/ImageType 291 | /Settings/System/LogLevel 292 | /Settings/System/ReleaseType 293 | /Settings/System/RemoteSupport 294 | /Settings/System/SSHLocal 295 | /Settings/System/TimeZone 296 | /Settings/System/Units/Temperature 297 | /Settings/System/VncInternet 298 | /Settings/System/VncLocal 299 | /Settings/System/VolumeUnit 300 | /Settings/SystemSetup/AcInput1 301 | /Settings/SystemSetup/AcInput2 302 | /Settings/SystemSetup/BatteryService 303 | /Settings/SystemSetup/HasAcOutSystem 304 | /Settings/SystemSetup/HasDcSystem 305 | /Settings/SystemSetup/MaxChargeCurrent 306 | /Settings/SystemSetup/MaxChargeVoltage 307 | /Settings/SystemSetup/SharedTemperatureSense 308 | /Settings/SystemSetup/SharedVoltageSense 309 | /Settings/SystemSetup/SystemName 310 | /Settings/SystemSetup/TemperatureService 311 | /Settings/Vrmlogger/HttpsEnabled 312 | /Settings/Vrmlogger/LogInterval 313 | /Settings/Vrmlogger/Logmode 314 | /Settings/Vrmlogger/RamDiskMode 315 | /Settings/Vrmlogger/Url 316 | /Settings/Watchdog/VrmTimeout 317 | /Settings/Devices/adc_builtin0_1/Standard2 318 | /Settings/Devices/adc_builtin0_1/Alarms/High/Active 319 | /Settings/Devices/adc_builtin0_1/Alarms/High/Delay 320 | /Settings/Devices/adc_builtin0_1/Alarms/High/Enable 321 | /Settings/Devices/adc_builtin0_1/Alarms/High/Restore 322 | /Settings/Devices/adc_builtin0_1/Alarms/Low/Active 323 | /Settings/Devices/adc_builtin0_1/Alarms/Low/Delay 324 | /Settings/Devices/adc_builtin0_1/Alarms/Low/Enable 325 | /Settings/Devices/adc_builtin0_1/Alarms/Low/Restore 326 | /Settings/Devices/adc_builtin0_1/Capacity 327 | /Settings/Devices/adc_builtin0_1/CustomName 328 | /Settings/Devices/adc_builtin0_1/FilterLength 329 | /Settings/Devices/adc_builtin0_1/FluidType2 330 | /Settings/Devices/adc_builtin0_1/Function 331 | /Settings/Devices/adc_builtin0_1/RawValueEmpty 332 | /Settings/Devices/adc_builtin0_1/RawValueFull 333 | /Settings/Devices/adc_builtin0_1/Shape 334 | /Settings/Devices/adc_builtin0_2/Standard2 335 | /Settings/Devices/adc_builtin0_2/Alarms/High/Active 336 | /Settings/Devices/adc_builtin0_2/Alarms/High/Delay 337 | /Settings/Devices/adc_builtin0_2/Alarms/High/Enable 338 | /Settings/Devices/adc_builtin0_2/Alarms/High/Restore 339 | /Settings/Devices/adc_builtin0_2/Alarms/Low/Active 340 | /Settings/Devices/adc_builtin0_2/Alarms/Low/Delay 341 | /Settings/Devices/adc_builtin0_2/Alarms/Low/Enable 342 | /Settings/Devices/adc_builtin0_2/Alarms/Low/Restore 343 | /Settings/Devices/adc_builtin0_2/Capacity 344 | /Settings/Devices/adc_builtin0_2/CustomName 345 | /Settings/Devices/adc_builtin0_2/FilterLength 346 | /Settings/Devices/adc_builtin0_2/FluidType2 347 | /Settings/Devices/adc_builtin0_2/Function 348 | /Settings/Devices/adc_builtin0_2/RawValueEmpty 349 | /Settings/Devices/adc_builtin0_2/RawValueFull 350 | /Settings/Devices/adc_builtin0_2/Shape 351 | /Settings/Devices/adc_builtin0_3/Standard2 352 | /Settings/Devices/adc_builtin0_3/Alarms/High/Active 353 | /Settings/Devices/adc_builtin0_3/Alarms/High/Delay 354 | /Settings/Devices/adc_builtin0_3/Alarms/High/Enable 355 | /Settings/Devices/adc_builtin0_3/Alarms/High/Restore 356 | /Settings/Devices/adc_builtin0_3/Alarms/Low/Active 357 | /Settings/Devices/adc_builtin0_3/Alarms/Low/Delay 358 | /Settings/Devices/adc_builtin0_3/Alarms/Low/Enable 359 | /Settings/Devices/adc_builtin0_3/Alarms/Low/Restore 360 | /Settings/Devices/adc_builtin0_3/Capacity 361 | /Settings/Devices/adc_builtin0_3/CustomName 362 | /Settings/Devices/adc_builtin0_3/FilterLength 363 | /Settings/Devices/adc_builtin0_3/FluidType2 364 | /Settings/Devices/adc_builtin0_3/Function 365 | /Settings/Devices/adc_builtin0_3/RawValueEmpty 366 | /Settings/Devices/adc_builtin0_3/RawValueFull 367 | /Settings/Devices/adc_builtin0_3/Shape 368 | /Settings/Devices/adc_builtin0_4/Standard2 369 | /Settings/Devices/adc_builtin0_4/Alarms/High/Active 370 | /Settings/Devices/adc_builtin0_4/Alarms/High/Delay 371 | /Settings/Devices/adc_builtin0_4/Alarms/High/Enable 372 | /Settings/Devices/adc_builtin0_4/Alarms/High/Restore 373 | /Settings/Devices/adc_builtin0_4/Alarms/Low/Active 374 | /Settings/Devices/adc_builtin0_4/Alarms/Low/Delay 375 | /Settings/Devices/adc_builtin0_4/Alarms/Low/Enable 376 | /Settings/Devices/adc_builtin0_4/Alarms/Low/Restore 377 | /Settings/Devices/adc_builtin0_4/Capacity 378 | /Settings/Devices/adc_builtin0_4/CustomName 379 | /Settings/Devices/adc_builtin0_4/FilterLength 380 | /Settings/Devices/adc_builtin0_4/FluidType2 381 | /Settings/Devices/adc_builtin0_4/Function 382 | /Settings/Devices/adc_builtin0_4/Shape 383 | /Settings/Devices/adc_builtin0_4/RawValueEmpty 384 | /Settings/Devices/adc_builtin0_4/RawValueFull 385 | /Settings/Devices/adc_builtin0_5/CustomName 386 | /Settings/Devices/adc_builtin0_5/FilterLength 387 | /Settings/Devices/adc_builtin0_5/Function 388 | /Settings/Devices/adc_builtin0_5/Offset 389 | /Settings/Devices/adc_builtin0_5/Scale 390 | /Settings/Devices/adc_builtin0_5/TemperatureType2 391 | /Settings/Devices/adc_builtin0_6/CustomName 392 | /Settings/Devices/adc_builtin0_6/FilterLength 393 | /Settings/Devices/adc_builtin0_6/Function 394 | /Settings/Devices/adc_builtin0_6/Offset 395 | /Settings/Devices/adc_builtin0_6/Scale 396 | /Settings/Devices/adc_builtin0_6/TemperatureType2 397 | /Settings/Devices/adc_builtin0_7/CustomName 398 | /Settings/Devices/adc_builtin0_7/FilterLength 399 | /Settings/Devices/adc_builtin0_7/Function 400 | /Settings/Devices/adc_builtin0_7/Offset 401 | /Settings/Devices/adc_builtin0_7/Scale 402 | /Settings/Devices/adc_builtin0_7/TemperatureType2 403 | /Settings/Devices/adc_builtin0_8/CustomName 404 | /Settings/Devices/adc_builtin0_8/FilterLength 405 | /Settings/Devices/adc_builtin0_8/Function 406 | /Settings/Devices/adc_builtin0_8/Offset 407 | /Settings/Devices/adc_builtin0_8/Scale 408 | /Settings/Devices/adc_builtin0_8/TemperatureType2 409 | /Settings/TempSensorRelay/adc_builtin0_5/0/ClearValue 410 | /Settings/TempSensorRelay/adc_builtin0_5/0/Relay 411 | /Settings/TempSensorRelay/adc_builtin0_5/0/SetValue 412 | /Settings/TempSensorRelay/adc_builtin0_5/1/ClearValue 413 | /Settings/TempSensorRelay/adc_builtin0_5/1/Relay 414 | /Settings/TempSensorRelay/adc_builtin0_5/1/SetValue 415 | /Settings/TempSensorRelay/adc_builtin0_5/Enabled 416 | /Settings/TempSensorRelay/adc_builtin0_6/0/ClearValue 417 | /Settings/TempSensorRelay/adc_builtin0_6/0/Relay 418 | /Settings/TempSensorRelay/adc_builtin0_6/0/SetValue 419 | /Settings/TempSensorRelay/adc_builtin0_6/1/ClearValue 420 | /Settings/TempSensorRelay/adc_builtin0_6/1/Relay 421 | /Settings/TempSensorRelay/adc_builtin0_6/1/SetValue 422 | /Settings/TempSensorRelay/adc_builtin0_6/Enabled 423 | /Settings/TempSensorRelay/adc_builtin0_7/0/ClearValue 424 | /Settings/TempSensorRelay/adc_builtin0_7/0/Relay 425 | /Settings/TempSensorRelay/adc_builtin0_7/0/SetValue 426 | /Settings/TempSensorRelay/adc_builtin0_7/1/ClearValue 427 | /Settings/TempSensorRelay/adc_builtin0_7/1/Relay 428 | /Settings/TempSensorRelay/adc_builtin0_7/1/SetValue 429 | /Settings/TempSensorRelay/adc_builtin0_7/Enabled 430 | /Settings/TempSensorRelay/adc_builtin0_8/0/ClearValue 431 | /Settings/TempSensorRelay/adc_builtin0_8/0/Relay 432 | /Settings/TempSensorRelay/adc_builtin0_8/0/SetValue 433 | /Settings/TempSensorRelay/adc_builtin0_8/1/ClearValue 434 | /Settings/TempSensorRelay/adc_builtin0_8/1/Relay 435 | /Settings/TempSensorRelay/adc_builtin0_8/1/SetValue 436 | /Settings/TempSensorRelay/adc_builtin0_8/Enabled 437 | -------------------------------------------------------------------------------- /setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # SetupHelper provides a set of utilities used by other packages to streamline installing and removing packages 4 | # and facilitates reinstallation following a Venus OS update 5 | # package setup scripts can be run from the command line for manual installation and uninstallation, 6 | # and in some cases inputing configuration options 7 | # 8 | # SetupHelper includes PackageManager which manages package updates from GitHub 9 | # as well as package installation an uninstallation from the main GUI 10 | # 11 | # this setup script does NOT use version-dependent file sets 12 | # rather it makes modifications as part of this script 13 | # so that updates are not required when Venus OS versions are added 14 | 15 | # tell CommonResources to: 16 | # prompt for install/uninstall 17 | # auto install or auto uninstall 18 | # then exit 19 | # CommonResources will NOT return here ! 20 | 21 | standardPromptAndActions='yes' 22 | 23 | #### following line incorporates helper resources into this script 24 | source "/data/SetupHelper/HelperResources/IncludeHelpers" 25 | #### end of lines to include helper resources 26 | -------------------------------------------------------------------------------- /velib_python/oldestVersion: -------------------------------------------------------------------------------- 1 | v3.40~39 2 | -------------------------------------------------------------------------------- /velib_python/settingsdevice.py: -------------------------------------------------------------------------------- 1 | import dbus 2 | import logging 3 | import time 4 | from functools import partial 5 | 6 | # Local imports 7 | from vedbus import VeDbusItemImport 8 | 9 | ## Indexes for the setting dictonary. 10 | PATH = 0 11 | VALUE = 1 12 | MINIMUM = 2 13 | MAXIMUM = 3 14 | SILENT = 4 15 | 16 | ## The Settings Device class. 17 | # Used by python programs, such as the vrm-logger, to read and write settings they 18 | # need to store on disk. And since these settings might be changed from a different 19 | # source, such as the GUI, the program can pass an eventCallback that will be called 20 | # as soon as some setting is changed. 21 | # 22 | # The settings are stored in flash via the com.victronenergy.settings service on dbus. 23 | # See https://github.com/victronenergy/localsettings for more info. 24 | # 25 | # If there are settings in de supportSettings list which are not yet on the dbus, 26 | # and therefore not yet in the xml file, they will be added through the dbus-addSetting 27 | # interface of com.victronenergy.settings. 28 | class SettingsDevice(object): 29 | ## The constructor processes the tree of dbus-items. 30 | # @param bus the system-dbus object 31 | # @param name the dbus-service-name of the settings dbus service, 'com.victronenergy.settings' 32 | # @param supportedSettings dictionary with all setting-names, and their defaultvalue, min, max and whether 33 | # the setting is silent. The 'silent' entry is optional. If set to true, no changes in the setting will 34 | # be logged by localsettings. 35 | # @param eventCallback function that will be called on changes on any of these settings 36 | # @param timeout Maximum interval to wait for localsettings. An exception is thrown at the end of the 37 | # interval if the localsettings D-Bus service has not appeared yet. 38 | def __init__(self, bus, supportedSettings, eventCallback, name='com.victronenergy.settings', timeout=0): 39 | logging.debug("===== Settings device init starting... =====") 40 | self._bus = bus 41 | self._dbus_name = name 42 | self._eventCallback = eventCallback 43 | self._values = {} # stored the values, used to pass the old value along on a setting change 44 | self._settings = {} 45 | 46 | count = 0 47 | while True: 48 | if 'com.victronenergy.settings' in self._bus.list_names(): 49 | break 50 | if count == timeout: 51 | raise Exception("The settings service com.victronenergy.settings does not exist!") 52 | count += 1 53 | logging.info('waiting for settings') 54 | time.sleep(1) 55 | 56 | # Add the items. 57 | self.addSettings(supportedSettings) 58 | 59 | logging.debug("===== Settings device init finished =====") 60 | 61 | def addSettings(self, settings): 62 | for setting, options in settings.items(): 63 | silent = len(options) > SILENT and options[SILENT] 64 | busitem = self.addSetting(options[PATH], options[VALUE], 65 | options[MINIMUM], options[MAXIMUM], silent, callback=partial(self.handleChangedSetting, setting)) 66 | self._settings[setting] = busitem 67 | self._values[setting] = busitem.get_value() 68 | 69 | def addSetting(self, path, value, _min, _max, silent=False, callback=None): 70 | busitem = VeDbusItemImport(self._bus, self._dbus_name, path, callback) 71 | if busitem.exists and (value, _min, _max, silent) == busitem._proxy.GetAttributes(): 72 | logging.debug("Setting %s found" % path) 73 | else: 74 | logging.info("Setting %s does not exist yet or must be adjusted" % path) 75 | 76 | # Prepare to add the setting. Most dbus types extend the python 77 | # type so it is only necessary to additionally test for Int64. 78 | if isinstance(value, (int, dbus.Int64)): 79 | itemType = 'i' 80 | elif isinstance(value, float): 81 | itemType = 'f' 82 | else: 83 | itemType = 's' 84 | 85 | # Add the setting 86 | # TODO, make an object that inherits VeDbusItemImport, and complete the D-Bus settingsitem interface 87 | settings_item = VeDbusItemImport(self._bus, self._dbus_name, '/Settings', createsignal=False) 88 | setting_path = path.replace('/Settings/', '', 1) 89 | if silent: 90 | settings_item._proxy.AddSilentSetting('', setting_path, value, itemType, _min, _max) 91 | else: 92 | settings_item._proxy.AddSetting('', setting_path, value, itemType, _min, _max) 93 | 94 | busitem = VeDbusItemImport(self._bus, self._dbus_name, path, callback) 95 | 96 | return busitem 97 | 98 | def handleChangedSetting(self, setting, servicename, path, changes): 99 | oldvalue = self._values[setting] if setting in self._values else None 100 | self._values[setting] = changes['Value'] 101 | 102 | if self._eventCallback is None: 103 | return 104 | 105 | self._eventCallback(setting, oldvalue, changes['Value']) 106 | 107 | def setDefault(self, path): 108 | item = VeDbusItemImport(self._bus, self._dbus_name, path, createsignal=False) 109 | item.set_default() 110 | 111 | def __getitem__(self, setting): 112 | return self._settings[setting].get_value() 113 | 114 | def __setitem__(self, setting, newvalue): 115 | result = self._settings[setting].set_value(newvalue) 116 | if result != 0: 117 | # Trying to make some false change to our own settings? How dumb! 118 | assert False 119 | -------------------------------------------------------------------------------- /velib_python/ve_utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | import sys 4 | from traceback import print_exc 5 | from os import _exit as os_exit 6 | from os import statvfs 7 | from subprocess import check_output, CalledProcessError 8 | import logging 9 | import dbus 10 | logger = logging.getLogger(__name__) 11 | 12 | VEDBUS_INVALID = dbus.Array([], signature=dbus.Signature('i'), variant_level=1) 13 | 14 | class NoVrmPortalIdError(Exception): 15 | pass 16 | 17 | # Use this function to make sure the code quits on an unexpected exception. Make sure to use it 18 | # when using GLib.idle_add and also GLib.timeout_add. 19 | # Without this, the code will just keep running, since GLib does not stop the mainloop on an 20 | # exception. 21 | # Example: GLib.idle_add(exit_on_error, myfunc, arg1, arg2) 22 | def exit_on_error(func, *args, **kwargs): 23 | try: 24 | return func(*args, **kwargs) 25 | except: 26 | try: 27 | print ('exit_on_error: there was an exception. Printing stacktrace will be tried and then exit') 28 | print_exc() 29 | except: 30 | pass 31 | 32 | # sys.exit() is not used, since that throws an exception, which does not lead to a program 33 | # halt when used in a dbus callback, see connection.py in the Python/Dbus libraries, line 230. 34 | os_exit(1) 35 | 36 | 37 | __vrm_portal_id = None 38 | def get_vrm_portal_id(): 39 | # The original definition of the VRM Portal ID is that it is the mac 40 | # address of the onboard- ethernet port (eth0), stripped from its colons 41 | # (:) and lower case. This may however differ between platforms. On Venus 42 | # the task is therefore deferred to /sbin/get-unique-id so that a 43 | # platform specific method can be easily defined. 44 | # 45 | # If /sbin/get-unique-id does not exist, then use the ethernet address 46 | # of eth0. This also handles the case where velib_python is used as a 47 | # package install on a Raspberry Pi. 48 | # 49 | # On a Linux host where the network interface may not be eth0, you can set 50 | # the VRM_IFACE environment variable to the correct name. 51 | 52 | global __vrm_portal_id 53 | 54 | if __vrm_portal_id: 55 | return __vrm_portal_id 56 | 57 | portal_id = None 58 | 59 | # First try the method that works if we don't have a data partition. This 60 | # will fail when the current user is not root. 61 | try: 62 | portal_id = check_output("/sbin/get-unique-id").decode("utf-8", "ignore").strip() 63 | if not portal_id: 64 | raise NoVrmPortalIdError("get-unique-id returned blank") 65 | __vrm_portal_id = portal_id 66 | return portal_id 67 | except CalledProcessError: 68 | # get-unique-id returned non-zero 69 | raise NoVrmPortalIdError("get-unique-id returned non-zero") 70 | except OSError: 71 | # File doesn't exist, use fallback 72 | pass 73 | 74 | # Fall back to getting our id using a syscall. Assume we are on linux. 75 | # Allow the user to override what interface is used using an environment 76 | # variable. 77 | import fcntl, socket, struct, os 78 | 79 | iface = os.environ.get('VRM_IFACE', 'eth0').encode('ascii') 80 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 81 | try: 82 | info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', iface[:15])) 83 | except IOError: 84 | raise NoVrmPortalIdError("ioctl failed for eth0") 85 | 86 | __vrm_portal_id = info[18:24].hex() 87 | return __vrm_portal_id 88 | 89 | 90 | # See VE.Can registers - public.docx for definition of this conversion 91 | def convert_vreg_version_to_readable(version): 92 | def str_to_arr(x, length): 93 | a = [] 94 | for i in range(0, len(x), length): 95 | a.append(x[i:i+length]) 96 | return a 97 | 98 | x = "%x" % version 99 | x = x.upper() 100 | 101 | if len(x) == 5 or len(x) == 3 or len(x) == 1: 102 | x = '0' + x 103 | 104 | a = str_to_arr(x, 2); 105 | 106 | # remove the first 00 if there are three bytes and it is 00 107 | if len(a) == 3 and a[0] == '00': 108 | a.remove(0); 109 | 110 | # if we have two or three bytes now, and the first character is a 0, remove it 111 | if len(a) >= 2 and a[0][0:1] == '0': 112 | a[0] = a[0][1]; 113 | 114 | result = '' 115 | for item in a: 116 | result += ('.' if result != '' else '') + item 117 | 118 | 119 | result = 'v' + result 120 | 121 | return result 122 | 123 | 124 | def get_free_space(path): 125 | result = -1 126 | 127 | try: 128 | s = statvfs(path) 129 | result = s.f_frsize * s.f_bavail # Number of free bytes that ordinary users 130 | except Exception as ex: 131 | logger.info("Error while retrieving free space for path %s: %s" % (path, ex)) 132 | 133 | return result 134 | 135 | 136 | def _get_sysfs_machine_name(): 137 | try: 138 | with open('/sys/firmware/devicetree/base/model', 'r') as f: 139 | return f.read().rstrip('\x00') 140 | except IOError: 141 | pass 142 | 143 | return None 144 | 145 | # Returns None if it cannot find a machine name. Otherwise returns the string 146 | # containing the name 147 | def get_machine_name(): 148 | # First try calling the venus utility script 149 | try: 150 | return check_output("/usr/bin/product-name").strip().decode('UTF-8') 151 | except (CalledProcessError, OSError): 152 | pass 153 | 154 | # Fall back to sysfs 155 | name = _get_sysfs_machine_name() 156 | if name is not None: 157 | return name 158 | 159 | # Fall back to venus build machine name 160 | try: 161 | with open('/etc/venus/machine', 'r', encoding='UTF-8') as f: 162 | return f.read().strip() 163 | except IOError: 164 | pass 165 | 166 | return None 167 | 168 | 169 | def get_product_id(): 170 | """ Find the machine ID and return it. """ 171 | 172 | # First try calling the venus utility script 173 | try: 174 | return check_output("/usr/bin/product-id").strip().decode('UTF-8') 175 | except (CalledProcessError, OSError): 176 | pass 177 | 178 | # Fall back machine name mechanism 179 | name = _get_sysfs_machine_name() 180 | return { 181 | 'Color Control GX': 'C001', 182 | 'Venus GX': 'C002', 183 | 'Octo GX': 'C006', 184 | 'EasySolar-II': 'C007', 185 | 'MultiPlus-II': 'C008', 186 | 'Maxi GX': 'C009', 187 | 'Cerbo GX': 'C00A' 188 | }.get(name, 'C003') # C003 is Generic 189 | 190 | 191 | # Returns False if it cannot open the file. Otherwise returns its rstripped contents 192 | def read_file(path): 193 | content = False 194 | 195 | try: 196 | with open(path, 'r') as f: 197 | content = f.read().rstrip() 198 | except Exception as ex: 199 | logger.debug("Error while reading %s: %s" % (path, ex)) 200 | 201 | return content 202 | 203 | 204 | def wrap_dbus_value(value): 205 | if value is None: 206 | return VEDBUS_INVALID 207 | if isinstance(value, float): 208 | return dbus.Double(value, variant_level=1) 209 | if isinstance(value, bool): 210 | return dbus.Boolean(value, variant_level=1) 211 | if isinstance(value, int): 212 | try: 213 | return dbus.Int32(value, variant_level=1) 214 | except OverflowError: 215 | return dbus.Int64(value, variant_level=1) 216 | if isinstance(value, str): 217 | return dbus.String(value, variant_level=1) 218 | if isinstance(value, list): 219 | if len(value) == 0: 220 | # If the list is empty we cannot infer the type of the contents. So assume unsigned integer. 221 | # A (signed) integer is dangerous, because an empty list of signed integers is used to encode 222 | # an invalid value. 223 | return dbus.Array([], signature=dbus.Signature('u'), variant_level=1) 224 | return dbus.Array([wrap_dbus_value(x) for x in value], variant_level=1) 225 | if isinstance(value, dict): 226 | # Wrapping the keys of the dictionary causes D-Bus errors like: 227 | # 'arguments to dbus_message_iter_open_container() were incorrect, 228 | # assertion "(type == DBUS_TYPE_ARRAY && contained_signature && 229 | # *contained_signature == DBUS_DICT_ENTRY_BEGIN_CHAR) || (contained_signature == NULL || 230 | # _dbus_check_is_valid_signature (contained_signature))" failed in file ...' 231 | return dbus.Dictionary({(k, wrap_dbus_value(v)) for k, v in value.items()}, variant_level=1) 232 | return value 233 | 234 | 235 | dbus_int_types = (dbus.Int32, dbus.UInt32, dbus.Byte, dbus.Int16, dbus.UInt16, dbus.UInt32, dbus.Int64, dbus.UInt64) 236 | 237 | 238 | def unwrap_dbus_value(val): 239 | """Converts D-Bus values back to the original type. For example if val is of type DBus.Double, 240 | a float will be returned.""" 241 | if isinstance(val, dbus_int_types): 242 | return int(val) 243 | if isinstance(val, dbus.Double): 244 | return float(val) 245 | if isinstance(val, dbus.Array): 246 | v = [unwrap_dbus_value(x) for x in val] 247 | return None if len(v) == 0 else v 248 | if isinstance(val, (dbus.Signature, dbus.String)): 249 | return str(val) 250 | # Python has no byte type, so we convert to an integer. 251 | if isinstance(val, dbus.Byte): 252 | return int(val) 253 | if isinstance(val, dbus.ByteArray): 254 | return "".join([bytes(x) for x in val]) 255 | if isinstance(val, (list, tuple)): 256 | return [unwrap_dbus_value(x) for x in val] 257 | if isinstance(val, (dbus.Dictionary, dict)): 258 | # Do not unwrap the keys, see comment in wrap_dbus_value 259 | return dict([(x, unwrap_dbus_value(y)) for x, y in val.items()]) 260 | if isinstance(val, dbus.Boolean): 261 | return bool(val) 262 | return val 263 | 264 | # When supported, only name owner changes for the the given namespace are reported. This 265 | # prevents spending cpu time at irrelevant changes, like scripts accessing the bus temporarily. 266 | def add_name_owner_changed_receiver(dbus, name_owner_changed, namespace="com.victronenergy"): 267 | # support for arg0namespace is submitted upstream, but not included at the time of 268 | # writing, Venus OS does support it, so try if it works. 269 | if namespace is None: 270 | dbus.add_signal_receiver(name_owner_changed, signal_name='NameOwnerChanged') 271 | else: 272 | try: 273 | dbus.add_signal_receiver(name_owner_changed, 274 | signal_name='NameOwnerChanged', arg0namespace=namespace) 275 | except TypeError: 276 | dbus.add_signal_receiver(name_owner_changed, signal_name='NameOwnerChanged') 277 | -------------------------------------------------------------------------------- /velib_python/velib_python/latest/oldestVersion: -------------------------------------------------------------------------------- 1 | v3.50 2 | -------------------------------------------------------------------------------- /velib_python/velib_python/latest/settingsdevice.py: -------------------------------------------------------------------------------- 1 | import dbus 2 | import logging 3 | import time 4 | from functools import partial 5 | 6 | # Local imports 7 | from vedbus import VeDbusItemImport 8 | 9 | ## Indexes for the setting dictonary. 10 | PATH = 0 11 | VALUE = 1 12 | MINIMUM = 2 13 | MAXIMUM = 3 14 | SILENT = 4 15 | 16 | ## The Settings Device class. 17 | # Used by python programs, such as the vrm-logger, to read and write settings they 18 | # need to store on disk. And since these settings might be changed from a different 19 | # source, such as the GUI, the program can pass an eventCallback that will be called 20 | # as soon as some setting is changed. 21 | # 22 | # The settings are stored in flash via the com.victronenergy.settings service on dbus. 23 | # See https://github.com/victronenergy/localsettings for more info. 24 | # 25 | # If there are settings in de supportSettings list which are not yet on the dbus, 26 | # and therefore not yet in the xml file, they will be added through the dbus-addSetting 27 | # interface of com.victronenergy.settings. 28 | class SettingsDevice(object): 29 | ## The constructor processes the tree of dbus-items. 30 | # @param bus the system-dbus object 31 | # @param name the dbus-service-name of the settings dbus service, 'com.victronenergy.settings' 32 | # @param supportedSettings dictionary with all setting-names, and their defaultvalue, min, max and whether 33 | # the setting is silent. The 'silent' entry is optional. If set to true, no changes in the setting will 34 | # be logged by localsettings. 35 | # @param eventCallback function that will be called on changes on any of these settings 36 | # @param timeout Maximum interval to wait for localsettings. An exception is thrown at the end of the 37 | # interval if the localsettings D-Bus service has not appeared yet. 38 | def __init__(self, bus, supportedSettings, eventCallback, name='com.victronenergy.settings', timeout=0): 39 | logging.debug("===== Settings device init starting... =====") 40 | self._bus = bus 41 | self._dbus_name = name 42 | self._eventCallback = eventCallback 43 | self._values = {} # stored the values, used to pass the old value along on a setting change 44 | self._settings = {} 45 | 46 | count = 0 47 | while True: 48 | if 'com.victronenergy.settings' in self._bus.list_names(): 49 | break 50 | if count == timeout: 51 | raise Exception("The settings service com.victronenergy.settings does not exist!") 52 | count += 1 53 | logging.info('waiting for settings') 54 | time.sleep(1) 55 | 56 | # Add the items. 57 | self.addSettings(supportedSettings) 58 | 59 | logging.debug("===== Settings device init finished =====") 60 | 61 | def addSettings(self, settings): 62 | for setting, options in settings.items(): 63 | silent = len(options) > SILENT and options[SILENT] 64 | busitem = self.addSetting(options[PATH], options[VALUE], 65 | options[MINIMUM], options[MAXIMUM], silent, callback=partial(self.handleChangedSetting, setting)) 66 | self._settings[setting] = busitem 67 | self._values[setting] = busitem.get_value() 68 | 69 | def addSetting(self, path, value, _min, _max, silent=False, callback=None): 70 | busitem = VeDbusItemImport(self._bus, self._dbus_name, path, callback) 71 | if busitem.exists and (value, _min, _max, silent) == busitem._proxy.GetAttributes(): 72 | logging.debug("Setting %s found" % path) 73 | else: 74 | logging.info("Setting %s does not exist yet or must be adjusted" % path) 75 | 76 | # Prepare to add the setting. Most dbus types extend the python 77 | # type so it is only necessary to additionally test for Int64. 78 | if isinstance(value, (int, dbus.Int64)): 79 | itemType = 'i' 80 | elif isinstance(value, float): 81 | itemType = 'f' 82 | else: 83 | itemType = 's' 84 | 85 | # Add the setting 86 | # TODO, make an object that inherits VeDbusItemImport, and complete the D-Bus settingsitem interface 87 | settings_item = VeDbusItemImport(self._bus, self._dbus_name, '/Settings', createsignal=False) 88 | setting_path = path.replace('/Settings/', '', 1) 89 | if silent: 90 | settings_item._proxy.AddSilentSetting('', setting_path, value, itemType, _min, _max) 91 | else: 92 | settings_item._proxy.AddSetting('', setting_path, value, itemType, _min, _max) 93 | 94 | busitem = VeDbusItemImport(self._bus, self._dbus_name, path, callback) 95 | 96 | return busitem 97 | 98 | def handleChangedSetting(self, setting, servicename, path, changes): 99 | oldvalue = self._values[setting] if setting in self._values else None 100 | self._values[setting] = changes['Value'] 101 | 102 | if self._eventCallback is None: 103 | return 104 | 105 | self._eventCallback(setting, oldvalue, changes['Value']) 106 | 107 | def setDefault(self, path): 108 | item = VeDbusItemImport(self._bus, self._dbus_name, path, createsignal=False) 109 | item.set_default() 110 | 111 | def __getitem__(self, setting): 112 | return self._settings[setting].get_value() 113 | 114 | def __setitem__(self, setting, newvalue): 115 | result = self._settings[setting].set_value(newvalue) 116 | if result != 0: 117 | # Trying to make some false change to our own settings? How dumb! 118 | assert False 119 | -------------------------------------------------------------------------------- /velib_python/velib_python/latest/ve_utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | import sys 4 | from traceback import print_exc 5 | from os import _exit as os_exit 6 | from os import statvfs 7 | from subprocess import check_output, CalledProcessError 8 | import logging 9 | import dbus 10 | logger = logging.getLogger(__name__) 11 | 12 | VEDBUS_INVALID = dbus.Array([], signature=dbus.Signature('i'), variant_level=1) 13 | 14 | class NoVrmPortalIdError(Exception): 15 | pass 16 | 17 | # Use this function to make sure the code quits on an unexpected exception. Make sure to use it 18 | # when using GLib.idle_add and also GLib.timeout_add. 19 | # Without this, the code will just keep running, since GLib does not stop the mainloop on an 20 | # exception. 21 | # Example: GLib.idle_add(exit_on_error, myfunc, arg1, arg2) 22 | def exit_on_error(func, *args, **kwargs): 23 | try: 24 | return func(*args, **kwargs) 25 | except: 26 | try: 27 | print ('exit_on_error: there was an exception. Printing stacktrace will be tried and then exit') 28 | print_exc() 29 | except: 30 | pass 31 | 32 | # sys.exit() is not used, since that throws an exception, which does not lead to a program 33 | # halt when used in a dbus callback, see connection.py in the Python/Dbus libraries, line 230. 34 | os_exit(1) 35 | 36 | 37 | __vrm_portal_id = None 38 | def get_vrm_portal_id(): 39 | # The original definition of the VRM Portal ID is that it is the mac 40 | # address of the onboard- ethernet port (eth0), stripped from its colons 41 | # (:) and lower case. This may however differ between platforms. On Venus 42 | # the task is therefore deferred to /sbin/get-unique-id so that a 43 | # platform specific method can be easily defined. 44 | # 45 | # If /sbin/get-unique-id does not exist, then use the ethernet address 46 | # of eth0. This also handles the case where velib_python is used as a 47 | # package install on a Raspberry Pi. 48 | # 49 | # On a Linux host where the network interface may not be eth0, you can set 50 | # the VRM_IFACE environment variable to the correct name. 51 | 52 | global __vrm_portal_id 53 | 54 | if __vrm_portal_id: 55 | return __vrm_portal_id 56 | 57 | portal_id = None 58 | 59 | # First try the method that works if we don't have a data partition. This 60 | # will fail when the current user is not root. 61 | try: 62 | portal_id = check_output("/sbin/get-unique-id").decode("utf-8", "ignore").strip() 63 | if not portal_id: 64 | raise NoVrmPortalIdError("get-unique-id returned blank") 65 | __vrm_portal_id = portal_id 66 | return portal_id 67 | except CalledProcessError: 68 | # get-unique-id returned non-zero 69 | raise NoVrmPortalIdError("get-unique-id returned non-zero") 70 | except OSError: 71 | # File doesn't exist, use fallback 72 | pass 73 | 74 | # Fall back to getting our id using a syscall. Assume we are on linux. 75 | # Allow the user to override what interface is used using an environment 76 | # variable. 77 | import fcntl, socket, struct, os 78 | 79 | iface = os.environ.get('VRM_IFACE', 'eth0').encode('ascii') 80 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 81 | try: 82 | info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', iface[:15])) 83 | except IOError: 84 | raise NoVrmPortalIdError("ioctl failed for eth0") 85 | 86 | __vrm_portal_id = info[18:24].hex() 87 | return __vrm_portal_id 88 | 89 | 90 | # See VE.Can registers - public.docx for definition of this conversion 91 | def convert_vreg_version_to_readable(version): 92 | def str_to_arr(x, length): 93 | a = [] 94 | for i in range(0, len(x), length): 95 | a.append(x[i:i+length]) 96 | return a 97 | 98 | x = "%x" % version 99 | x = x.upper() 100 | 101 | if len(x) == 5 or len(x) == 3 or len(x) == 1: 102 | x = '0' + x 103 | 104 | a = str_to_arr(x, 2); 105 | 106 | # remove the first 00 if there are three bytes and it is 00 107 | if len(a) == 3 and a[0] == '00': 108 | a.remove(0); 109 | 110 | # if we have two or three bytes now, and the first character is a 0, remove it 111 | if len(a) >= 2 and a[0][0:1] == '0': 112 | a[0] = a[0][1]; 113 | 114 | result = '' 115 | for item in a: 116 | result += ('.' if result != '' else '') + item 117 | 118 | 119 | result = 'v' + result 120 | 121 | return result 122 | 123 | 124 | def get_free_space(path): 125 | result = -1 126 | 127 | try: 128 | s = statvfs(path) 129 | result = s.f_frsize * s.f_bavail # Number of free bytes that ordinary users 130 | except Exception as ex: 131 | logger.info("Error while retrieving free space for path %s: %s" % (path, ex)) 132 | 133 | return result 134 | 135 | 136 | def _get_sysfs_machine_name(): 137 | try: 138 | with open('/sys/firmware/devicetree/base/model', 'r') as f: 139 | return f.read().rstrip('\x00') 140 | except IOError: 141 | pass 142 | 143 | return None 144 | 145 | # Returns None if it cannot find a machine name. Otherwise returns the string 146 | # containing the name 147 | def get_machine_name(): 148 | # First try calling the venus utility script 149 | try: 150 | return check_output("/usr/bin/product-name").strip().decode('UTF-8') 151 | except (CalledProcessError, OSError): 152 | pass 153 | 154 | # Fall back to sysfs 155 | name = _get_sysfs_machine_name() 156 | if name is not None: 157 | return name 158 | 159 | # Fall back to venus build machine name 160 | try: 161 | with open('/etc/venus/machine', 'r', encoding='UTF-8') as f: 162 | return f.read().strip() 163 | except IOError: 164 | pass 165 | 166 | return None 167 | 168 | 169 | def get_product_id(): 170 | """ Find the machine ID and return it. """ 171 | 172 | # First try calling the venus utility script 173 | try: 174 | return check_output("/usr/bin/product-id").strip().decode('UTF-8') 175 | except (CalledProcessError, OSError): 176 | pass 177 | 178 | # Fall back machine name mechanism 179 | name = _get_sysfs_machine_name() 180 | return { 181 | 'Color Control GX': 'C001', 182 | 'Venus GX': 'C002', 183 | 'Octo GX': 'C006', 184 | 'EasySolar-II': 'C007', 185 | 'MultiPlus-II': 'C008', 186 | 'Maxi GX': 'C009', 187 | 'Cerbo GX': 'C00A' 188 | }.get(name, 'C003') # C003 is Generic 189 | 190 | 191 | # Returns False if it cannot open the file. Otherwise returns its rstripped contents 192 | def read_file(path): 193 | content = False 194 | 195 | try: 196 | with open(path, 'r') as f: 197 | content = f.read().rstrip() 198 | except Exception as ex: 199 | logger.debug("Error while reading %s: %s" % (path, ex)) 200 | 201 | return content 202 | 203 | 204 | def wrap_dbus_value(value): 205 | if value is None: 206 | return VEDBUS_INVALID 207 | if isinstance(value, float): 208 | return dbus.Double(value, variant_level=1) 209 | if isinstance(value, bool): 210 | return dbus.Boolean(value, variant_level=1) 211 | if isinstance(value, int): 212 | try: 213 | return dbus.Int32(value, variant_level=1) 214 | except OverflowError: 215 | return dbus.Int64(value, variant_level=1) 216 | if isinstance(value, str): 217 | return dbus.String(value, variant_level=1) 218 | if isinstance(value, list): 219 | if len(value) == 0: 220 | # If the list is empty we cannot infer the type of the contents. So assume unsigned integer. 221 | # A (signed) integer is dangerous, because an empty list of signed integers is used to encode 222 | # an invalid value. 223 | return dbus.Array([], signature=dbus.Signature('u'), variant_level=1) 224 | return dbus.Array([wrap_dbus_value(x) for x in value], variant_level=1) 225 | if isinstance(value, dict): 226 | # Wrapping the keys of the dictionary causes D-Bus errors like: 227 | # 'arguments to dbus_message_iter_open_container() were incorrect, 228 | # assertion "(type == DBUS_TYPE_ARRAY && contained_signature && 229 | # *contained_signature == DBUS_DICT_ENTRY_BEGIN_CHAR) || (contained_signature == NULL || 230 | # _dbus_check_is_valid_signature (contained_signature))" failed in file ...' 231 | return dbus.Dictionary({(k, wrap_dbus_value(v)) for k, v in value.items()}, variant_level=1) 232 | return value 233 | 234 | 235 | dbus_int_types = (dbus.Int32, dbus.UInt32, dbus.Byte, dbus.Int16, dbus.UInt16, dbus.UInt32, dbus.Int64, dbus.UInt64) 236 | 237 | 238 | def unwrap_dbus_value(val): 239 | """Converts D-Bus values back to the original type. For example if val is of type DBus.Double, 240 | a float will be returned.""" 241 | if isinstance(val, dbus_int_types): 242 | return int(val) 243 | if isinstance(val, dbus.Double): 244 | return float(val) 245 | if isinstance(val, dbus.Array): 246 | v = [unwrap_dbus_value(x) for x in val] 247 | return None if len(v) == 0 else v 248 | if isinstance(val, (dbus.Signature, dbus.String)): 249 | return str(val) 250 | # Python has no byte type, so we convert to an integer. 251 | if isinstance(val, dbus.Byte): 252 | return int(val) 253 | if isinstance(val, dbus.ByteArray): 254 | return "".join([bytes(x) for x in val]) 255 | if isinstance(val, (list, tuple)): 256 | return [unwrap_dbus_value(x) for x in val] 257 | if isinstance(val, (dbus.Dictionary, dict)): 258 | # Do not unwrap the keys, see comment in wrap_dbus_value 259 | return dict([(x, unwrap_dbus_value(y)) for x, y in val.items()]) 260 | if isinstance(val, dbus.Boolean): 261 | return bool(val) 262 | return val 263 | 264 | # When supported, only name owner changes for the the given namespace are reported. This 265 | # prevents spending cpu time at irrelevant changes, like scripts accessing the bus temporarily. 266 | def add_name_owner_changed_receiver(dbus, name_owner_changed, namespace="com.victronenergy"): 267 | # support for arg0namespace is submitted upstream, but not included at the time of 268 | # writing, Venus OS does support it, so try if it works. 269 | if namespace is None: 270 | dbus.add_signal_receiver(name_owner_changed, signal_name='NameOwnerChanged') 271 | else: 272 | try: 273 | dbus.add_signal_receiver(name_owner_changed, 274 | signal_name='NameOwnerChanged', arg0namespace=namespace) 275 | except TypeError: 276 | dbus.add_signal_receiver(name_owner_changed, signal_name='NameOwnerChanged') 277 | -------------------------------------------------------------------------------- /velib_python/velib_python/v3.34/oldestVersion: -------------------------------------------------------------------------------- 1 | v3.10 2 | -------------------------------------------------------------------------------- /velib_python/velib_python/v3.34/settingsdevice.py: -------------------------------------------------------------------------------- 1 | import dbus 2 | import logging 3 | import time 4 | from functools import partial 5 | 6 | # Local imports 7 | from vedbus import VeDbusItemImport 8 | 9 | ## Indexes for the setting dictonary. 10 | PATH = 0 11 | VALUE = 1 12 | MINIMUM = 2 13 | MAXIMUM = 3 14 | SILENT = 4 15 | 16 | ## The Settings Device class. 17 | # Used by python programs, such as the vrm-logger, to read and write settings they 18 | # need to store on disk. And since these settings might be changed from a different 19 | # source, such as the GUI, the program can pass an eventCallback that will be called 20 | # as soon as some setting is changed. 21 | # 22 | # The settings are stored in flash via the com.victronenergy.settings service on dbus. 23 | # See https://github.com/victronenergy/localsettings for more info. 24 | # 25 | # If there are settings in de supportSettings list which are not yet on the dbus, 26 | # and therefore not yet in the xml file, they will be added through the dbus-addSetting 27 | # interface of com.victronenergy.settings. 28 | class SettingsDevice(object): 29 | ## The constructor processes the tree of dbus-items. 30 | # @param bus the system-dbus object 31 | # @param name the dbus-service-name of the settings dbus service, 'com.victronenergy.settings' 32 | # @param supportedSettings dictionary with all setting-names, and their defaultvalue, min, max and whether 33 | # the setting is silent. The 'silent' entry is optional. If set to true, no changes in the setting will 34 | # be logged by localsettings. 35 | # @param eventCallback function that will be called on changes on any of these settings 36 | # @param timeout Maximum interval to wait for localsettings. An exception is thrown at the end of the 37 | # interval if the localsettings D-Bus service has not appeared yet. 38 | def __init__(self, bus, supportedSettings, eventCallback, name='com.victronenergy.settings', timeout=0): 39 | logging.debug("===== Settings device init starting... =====") 40 | self._bus = bus 41 | self._dbus_name = name 42 | self._eventCallback = eventCallback 43 | self._values = {} # stored the values, used to pass the old value along on a setting change 44 | self._settings = {} 45 | 46 | count = 0 47 | while True: 48 | if 'com.victronenergy.settings' in self._bus.list_names(): 49 | break 50 | if count == timeout: 51 | raise Exception("The settings service com.victronenergy.settings does not exist!") 52 | count += 1 53 | logging.info('waiting for settings') 54 | time.sleep(1) 55 | 56 | # Add the items. 57 | self.addSettings(supportedSettings) 58 | 59 | logging.debug("===== Settings device init finished =====") 60 | 61 | def addSettings(self, settings): 62 | for setting, options in settings.items(): 63 | silent = len(options) > SILENT and options[SILENT] 64 | busitem = self.addSetting(options[PATH], options[VALUE], 65 | options[MINIMUM], options[MAXIMUM], silent, callback=partial(self.handleChangedSetting, setting)) 66 | self._settings[setting] = busitem 67 | self._values[setting] = busitem.get_value() 68 | 69 | def addSetting(self, path, value, _min, _max, silent=False, callback=None): 70 | busitem = VeDbusItemImport(self._bus, self._dbus_name, path, callback) 71 | if busitem.exists and (value, _min, _max, silent) == busitem._proxy.GetAttributes(): 72 | logging.debug("Setting %s found" % path) 73 | else: 74 | logging.info("Setting %s does not exist yet or must be adjusted" % path) 75 | 76 | # Prepare to add the setting. Most dbus types extend the python 77 | # type so it is only necessary to additionally test for Int64. 78 | if isinstance(value, (int, dbus.Int64)): 79 | itemType = 'i' 80 | elif isinstance(value, float): 81 | itemType = 'f' 82 | else: 83 | itemType = 's' 84 | 85 | # Add the setting 86 | # TODO, make an object that inherits VeDbusItemImport, and complete the D-Bus settingsitem interface 87 | settings_item = VeDbusItemImport(self._bus, self._dbus_name, '/Settings', createsignal=False) 88 | setting_path = path.replace('/Settings/', '', 1) 89 | if silent: 90 | settings_item._proxy.AddSilentSetting('', setting_path, value, itemType, _min, _max) 91 | else: 92 | settings_item._proxy.AddSetting('', setting_path, value, itemType, _min, _max) 93 | 94 | busitem = VeDbusItemImport(self._bus, self._dbus_name, path, callback) 95 | 96 | return busitem 97 | 98 | def handleChangedSetting(self, setting, servicename, path, changes): 99 | oldvalue = self._values[setting] if setting in self._values else None 100 | self._values[setting] = changes['Value'] 101 | 102 | if self._eventCallback is None: 103 | return 104 | 105 | self._eventCallback(setting, oldvalue, changes['Value']) 106 | 107 | def setDefault(self, path): 108 | item = VeDbusItemImport(self._bus, self._dbus_name, path, createsignal=False) 109 | item.set_default() 110 | 111 | def __getitem__(self, setting): 112 | return self._settings[setting].get_value() 113 | 114 | def __setitem__(self, setting, newvalue): 115 | result = self._settings[setting].set_value(newvalue) 116 | if result != 0: 117 | # Trying to make some false change to our own settings? How dumb! 118 | assert False 119 | -------------------------------------------------------------------------------- /velib_python/velib_python/v3.34/ve_utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | import sys 4 | from traceback import print_exc 5 | from os import _exit as os_exit 6 | from os import statvfs 7 | from subprocess import check_output, CalledProcessError 8 | import logging 9 | import dbus 10 | logger = logging.getLogger(__name__) 11 | 12 | VEDBUS_INVALID = dbus.Array([], signature=dbus.Signature('i'), variant_level=1) 13 | 14 | class NoVrmPortalIdError(Exception): 15 | pass 16 | 17 | # Use this function to make sure the code quits on an unexpected exception. Make sure to use it 18 | # when using GLib.idle_add and also GLib.timeout_add. 19 | # Without this, the code will just keep running, since GLib does not stop the mainloop on an 20 | # exception. 21 | # Example: GLib.idle_add(exit_on_error, myfunc, arg1, arg2) 22 | def exit_on_error(func, *args, **kwargs): 23 | try: 24 | return func(*args, **kwargs) 25 | except: 26 | try: 27 | print ('exit_on_error: there was an exception. Printing stacktrace will be tried and then exit') 28 | print_exc() 29 | except: 30 | pass 31 | 32 | # sys.exit() is not used, since that throws an exception, which does not lead to a program 33 | # halt when used in a dbus callback, see connection.py in the Python/Dbus libraries, line 230. 34 | os_exit(1) 35 | 36 | 37 | __vrm_portal_id = None 38 | def get_vrm_portal_id(): 39 | # The original definition of the VRM Portal ID is that it is the mac 40 | # address of the onboard- ethernet port (eth0), stripped from its colons 41 | # (:) and lower case. This may however differ between platforms. On Venus 42 | # the task is therefore deferred to /sbin/get-unique-id so that a 43 | # platform specific method can be easily defined. 44 | # 45 | # If /sbin/get-unique-id does not exist, then use the ethernet address 46 | # of eth0. This also handles the case where velib_python is used as a 47 | # package install on a Raspberry Pi. 48 | # 49 | # On a Linux host where the network interface may not be eth0, you can set 50 | # the VRM_IFACE environment variable to the correct name. 51 | 52 | global __vrm_portal_id 53 | 54 | if __vrm_portal_id: 55 | return __vrm_portal_id 56 | 57 | portal_id = None 58 | 59 | # First try the method that works if we don't have a data partition. This 60 | # will fail when the current user is not root. 61 | try: 62 | portal_id = check_output("/sbin/get-unique-id").decode("utf-8", "ignore").strip() 63 | if not portal_id: 64 | raise NoVrmPortalIdError("get-unique-id returned blank") 65 | __vrm_portal_id = portal_id 66 | return portal_id 67 | except CalledProcessError: 68 | # get-unique-id returned non-zero 69 | raise NoVrmPortalIdError("get-unique-id returned non-zero") 70 | except OSError: 71 | # File doesn't exist, use fallback 72 | pass 73 | 74 | # Fall back to getting our id using a syscall. Assume we are on linux. 75 | # Allow the user to override what interface is used using an environment 76 | # variable. 77 | import fcntl, socket, struct, os 78 | 79 | iface = os.environ.get('VRM_IFACE', 'eth0').encode('ascii') 80 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 81 | try: 82 | info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', iface[:15])) 83 | except IOError: 84 | raise NoVrmPortalIdError("ioctl failed for eth0") 85 | 86 | __vrm_portal_id = info[18:24].hex() 87 | return __vrm_portal_id 88 | 89 | 90 | # See VE.Can registers - public.docx for definition of this conversion 91 | def convert_vreg_version_to_readable(version): 92 | def str_to_arr(x, length): 93 | a = [] 94 | for i in range(0, len(x), length): 95 | a.append(x[i:i+length]) 96 | return a 97 | 98 | x = "%x" % version 99 | x = x.upper() 100 | 101 | if len(x) == 5 or len(x) == 3 or len(x) == 1: 102 | x = '0' + x 103 | 104 | a = str_to_arr(x, 2); 105 | 106 | # remove the first 00 if there are three bytes and it is 00 107 | if len(a) == 3 and a[0] == '00': 108 | a.remove(0); 109 | 110 | # if we have two or three bytes now, and the first character is a 0, remove it 111 | if len(a) >= 2 and a[0][0:1] == '0': 112 | a[0] = a[0][1]; 113 | 114 | result = '' 115 | for item in a: 116 | result += ('.' if result != '' else '') + item 117 | 118 | 119 | result = 'v' + result 120 | 121 | return result 122 | 123 | 124 | def get_free_space(path): 125 | result = -1 126 | 127 | try: 128 | s = statvfs(path) 129 | result = s.f_frsize * s.f_bavail # Number of free bytes that ordinary users 130 | except Exception as ex: 131 | logger.info("Error while retrieving free space for path %s: %s" % (path, ex)) 132 | 133 | return result 134 | 135 | 136 | def _get_sysfs_machine_name(): 137 | try: 138 | with open('/sys/firmware/devicetree/base/model', 'r') as f: 139 | return f.read().rstrip('\x00') 140 | except IOError: 141 | pass 142 | 143 | return None 144 | 145 | # Returns None if it cannot find a machine name. Otherwise returns the string 146 | # containing the name 147 | def get_machine_name(): 148 | # First try calling the venus utility script 149 | try: 150 | return check_output("/usr/bin/product-name").strip().decode('UTF-8') 151 | except (CalledProcessError, OSError): 152 | pass 153 | 154 | # Fall back to sysfs 155 | name = _get_sysfs_machine_name() 156 | if name is not None: 157 | return name 158 | 159 | # Fall back to venus build machine name 160 | try: 161 | with open('/etc/venus/machine', 'r', encoding='UTF-8') as f: 162 | return f.read().strip() 163 | except IOError: 164 | pass 165 | 166 | return None 167 | 168 | 169 | def get_product_id(): 170 | """ Find the machine ID and return it. """ 171 | 172 | # First try calling the venus utility script 173 | try: 174 | return check_output("/usr/bin/product-id").strip().decode('UTF-8') 175 | except (CalledProcessError, OSError): 176 | pass 177 | 178 | # Fall back machine name mechanism 179 | name = _get_sysfs_machine_name() 180 | return { 181 | 'Color Control GX': 'C001', 182 | 'Venus GX': 'C002', 183 | 'Octo GX': 'C006', 184 | 'EasySolar-II': 'C007', 185 | 'MultiPlus-II': 'C008', 186 | 'Maxi GX': 'C009', 187 | 'Cerbo GX': 'C00A' 188 | }.get(name, 'C003') # C003 is Generic 189 | 190 | 191 | # Returns False if it cannot open the file. Otherwise returns its rstripped contents 192 | def read_file(path): 193 | content = False 194 | 195 | try: 196 | with open(path, 'r') as f: 197 | content = f.read().rstrip() 198 | except Exception as ex: 199 | logger.debug("Error while reading %s: %s" % (path, ex)) 200 | 201 | return content 202 | 203 | 204 | def wrap_dbus_value(value): 205 | if value is None: 206 | return VEDBUS_INVALID 207 | if isinstance(value, float): 208 | return dbus.Double(value, variant_level=1) 209 | if isinstance(value, bool): 210 | return dbus.Boolean(value, variant_level=1) 211 | if isinstance(value, int): 212 | try: 213 | return dbus.Int32(value, variant_level=1) 214 | except OverflowError: 215 | return dbus.Int64(value, variant_level=1) 216 | if isinstance(value, str): 217 | return dbus.String(value, variant_level=1) 218 | if isinstance(value, list): 219 | if len(value) == 0: 220 | # If the list is empty we cannot infer the type of the contents. So assume unsigned integer. 221 | # A (signed) integer is dangerous, because an empty list of signed integers is used to encode 222 | # an invalid value. 223 | return dbus.Array([], signature=dbus.Signature('u'), variant_level=1) 224 | return dbus.Array([wrap_dbus_value(x) for x in value], variant_level=1) 225 | if isinstance(value, dict): 226 | # Wrapping the keys of the dictionary causes D-Bus errors like: 227 | # 'arguments to dbus_message_iter_open_container() were incorrect, 228 | # assertion "(type == DBUS_TYPE_ARRAY && contained_signature && 229 | # *contained_signature == DBUS_DICT_ENTRY_BEGIN_CHAR) || (contained_signature == NULL || 230 | # _dbus_check_is_valid_signature (contained_signature))" failed in file ...' 231 | return dbus.Dictionary({(k, wrap_dbus_value(v)) for k, v in value.items()}, variant_level=1) 232 | return value 233 | 234 | 235 | dbus_int_types = (dbus.Int32, dbus.UInt32, dbus.Byte, dbus.Int16, dbus.UInt16, dbus.UInt32, dbus.Int64, dbus.UInt64) 236 | 237 | 238 | def unwrap_dbus_value(val): 239 | """Converts D-Bus values back to the original type. For example if val is of type DBus.Double, 240 | a float will be returned.""" 241 | if isinstance(val, dbus_int_types): 242 | return int(val) 243 | if isinstance(val, dbus.Double): 244 | return float(val) 245 | if isinstance(val, dbus.Array): 246 | v = [unwrap_dbus_value(x) for x in val] 247 | return None if len(v) == 0 else v 248 | if isinstance(val, (dbus.Signature, dbus.String)): 249 | return str(val) 250 | # Python has no byte type, so we convert to an integer. 251 | if isinstance(val, dbus.Byte): 252 | return int(val) 253 | if isinstance(val, dbus.ByteArray): 254 | return "".join([bytes(x) for x in val]) 255 | if isinstance(val, (list, tuple)): 256 | return [unwrap_dbus_value(x) for x in val] 257 | if isinstance(val, (dbus.Dictionary, dict)): 258 | # Do not unwrap the keys, see comment in wrap_dbus_value 259 | return dict([(x, unwrap_dbus_value(y)) for x, y in val.items()]) 260 | if isinstance(val, dbus.Boolean): 261 | return bool(val) 262 | return val 263 | -------------------------------------------------------------------------------- /velib_python/velib_python/v3.41/oldestVersion: -------------------------------------------------------------------------------- 1 | v3.40 2 | -------------------------------------------------------------------------------- /velib_python/velib_python/v3.41/settingsdevice.py: -------------------------------------------------------------------------------- 1 | import dbus 2 | import logging 3 | import time 4 | from functools import partial 5 | 6 | # Local imports 7 | from vedbus import VeDbusItemImport 8 | 9 | ## Indexes for the setting dictonary. 10 | PATH = 0 11 | VALUE = 1 12 | MINIMUM = 2 13 | MAXIMUM = 3 14 | SILENT = 4 15 | 16 | ## The Settings Device class. 17 | # Used by python programs, such as the vrm-logger, to read and write settings they 18 | # need to store on disk. And since these settings might be changed from a different 19 | # source, such as the GUI, the program can pass an eventCallback that will be called 20 | # as soon as some setting is changed. 21 | # 22 | # The settings are stored in flash via the com.victronenergy.settings service on dbus. 23 | # See https://github.com/victronenergy/localsettings for more info. 24 | # 25 | # If there are settings in de supportSettings list which are not yet on the dbus, 26 | # and therefore not yet in the xml file, they will be added through the dbus-addSetting 27 | # interface of com.victronenergy.settings. 28 | class SettingsDevice(object): 29 | ## The constructor processes the tree of dbus-items. 30 | # @param bus the system-dbus object 31 | # @param name the dbus-service-name of the settings dbus service, 'com.victronenergy.settings' 32 | # @param supportedSettings dictionary with all setting-names, and their defaultvalue, min, max and whether 33 | # the setting is silent. The 'silent' entry is optional. If set to true, no changes in the setting will 34 | # be logged by localsettings. 35 | # @param eventCallback function that will be called on changes on any of these settings 36 | # @param timeout Maximum interval to wait for localsettings. An exception is thrown at the end of the 37 | # interval if the localsettings D-Bus service has not appeared yet. 38 | def __init__(self, bus, supportedSettings, eventCallback, name='com.victronenergy.settings', timeout=0): 39 | logging.debug("===== Settings device init starting... =====") 40 | self._bus = bus 41 | self._dbus_name = name 42 | self._eventCallback = eventCallback 43 | self._values = {} # stored the values, used to pass the old value along on a setting change 44 | self._settings = {} 45 | 46 | count = 0 47 | while True: 48 | if 'com.victronenergy.settings' in self._bus.list_names(): 49 | break 50 | if count == timeout: 51 | raise Exception("The settings service com.victronenergy.settings does not exist!") 52 | count += 1 53 | logging.info('waiting for settings') 54 | time.sleep(1) 55 | 56 | # Add the items. 57 | self.addSettings(supportedSettings) 58 | 59 | logging.debug("===== Settings device init finished =====") 60 | 61 | def addSettings(self, settings): 62 | for setting, options in settings.items(): 63 | silent = len(options) > SILENT and options[SILENT] 64 | busitem = self.addSetting(options[PATH], options[VALUE], 65 | options[MINIMUM], options[MAXIMUM], silent, callback=partial(self.handleChangedSetting, setting)) 66 | self._settings[setting] = busitem 67 | self._values[setting] = busitem.get_value() 68 | 69 | def addSetting(self, path, value, _min, _max, silent=False, callback=None): 70 | busitem = VeDbusItemImport(self._bus, self._dbus_name, path, callback) 71 | if busitem.exists and (value, _min, _max, silent) == busitem._proxy.GetAttributes(): 72 | logging.debug("Setting %s found" % path) 73 | else: 74 | logging.info("Setting %s does not exist yet or must be adjusted" % path) 75 | 76 | # Prepare to add the setting. Most dbus types extend the python 77 | # type so it is only necessary to additionally test for Int64. 78 | if isinstance(value, (int, dbus.Int64)): 79 | itemType = 'i' 80 | elif isinstance(value, float): 81 | itemType = 'f' 82 | else: 83 | itemType = 's' 84 | 85 | # Add the setting 86 | # TODO, make an object that inherits VeDbusItemImport, and complete the D-Bus settingsitem interface 87 | settings_item = VeDbusItemImport(self._bus, self._dbus_name, '/Settings', createsignal=False) 88 | setting_path = path.replace('/Settings/', '', 1) 89 | if silent: 90 | settings_item._proxy.AddSilentSetting('', setting_path, value, itemType, _min, _max) 91 | else: 92 | settings_item._proxy.AddSetting('', setting_path, value, itemType, _min, _max) 93 | 94 | busitem = VeDbusItemImport(self._bus, self._dbus_name, path, callback) 95 | 96 | return busitem 97 | 98 | def handleChangedSetting(self, setting, servicename, path, changes): 99 | oldvalue = self._values[setting] if setting in self._values else None 100 | self._values[setting] = changes['Value'] 101 | 102 | if self._eventCallback is None: 103 | return 104 | 105 | self._eventCallback(setting, oldvalue, changes['Value']) 106 | 107 | def setDefault(self, path): 108 | item = VeDbusItemImport(self._bus, self._dbus_name, path, createsignal=False) 109 | item.set_default() 110 | 111 | def __getitem__(self, setting): 112 | return self._settings[setting].get_value() 113 | 114 | def __setitem__(self, setting, newvalue): 115 | result = self._settings[setting].set_value(newvalue) 116 | if result != 0: 117 | # Trying to make some false change to our own settings? How dumb! 118 | assert False 119 | -------------------------------------------------------------------------------- /velib_python/velib_python/v3.41/ve_utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | import sys 4 | from traceback import print_exc 5 | from os import _exit as os_exit 6 | from os import statvfs 7 | from subprocess import check_output, CalledProcessError 8 | import logging 9 | import dbus 10 | logger = logging.getLogger(__name__) 11 | 12 | VEDBUS_INVALID = dbus.Array([], signature=dbus.Signature('i'), variant_level=1) 13 | 14 | class NoVrmPortalIdError(Exception): 15 | pass 16 | 17 | # Use this function to make sure the code quits on an unexpected exception. Make sure to use it 18 | # when using GLib.idle_add and also GLib.timeout_add. 19 | # Without this, the code will just keep running, since GLib does not stop the mainloop on an 20 | # exception. 21 | # Example: GLib.idle_add(exit_on_error, myfunc, arg1, arg2) 22 | def exit_on_error(func, *args, **kwargs): 23 | try: 24 | return func(*args, **kwargs) 25 | except: 26 | try: 27 | print ('exit_on_error: there was an exception. Printing stacktrace will be tried and then exit') 28 | print_exc() 29 | except: 30 | pass 31 | 32 | # sys.exit() is not used, since that throws an exception, which does not lead to a program 33 | # halt when used in a dbus callback, see connection.py in the Python/Dbus libraries, line 230. 34 | os_exit(1) 35 | 36 | 37 | __vrm_portal_id = None 38 | def get_vrm_portal_id(): 39 | # The original definition of the VRM Portal ID is that it is the mac 40 | # address of the onboard- ethernet port (eth0), stripped from its colons 41 | # (:) and lower case. This may however differ between platforms. On Venus 42 | # the task is therefore deferred to /sbin/get-unique-id so that a 43 | # platform specific method can be easily defined. 44 | # 45 | # If /sbin/get-unique-id does not exist, then use the ethernet address 46 | # of eth0. This also handles the case where velib_python is used as a 47 | # package install on a Raspberry Pi. 48 | # 49 | # On a Linux host where the network interface may not be eth0, you can set 50 | # the VRM_IFACE environment variable to the correct name. 51 | 52 | global __vrm_portal_id 53 | 54 | if __vrm_portal_id: 55 | return __vrm_portal_id 56 | 57 | portal_id = None 58 | 59 | # First try the method that works if we don't have a data partition. This 60 | # will fail when the current user is not root. 61 | try: 62 | portal_id = check_output("/sbin/get-unique-id").decode("utf-8", "ignore").strip() 63 | if not portal_id: 64 | raise NoVrmPortalIdError("get-unique-id returned blank") 65 | __vrm_portal_id = portal_id 66 | return portal_id 67 | except CalledProcessError: 68 | # get-unique-id returned non-zero 69 | raise NoVrmPortalIdError("get-unique-id returned non-zero") 70 | except OSError: 71 | # File doesn't exist, use fallback 72 | pass 73 | 74 | # Fall back to getting our id using a syscall. Assume we are on linux. 75 | # Allow the user to override what interface is used using an environment 76 | # variable. 77 | import fcntl, socket, struct, os 78 | 79 | iface = os.environ.get('VRM_IFACE', 'eth0').encode('ascii') 80 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 81 | try: 82 | info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', iface[:15])) 83 | except IOError: 84 | raise NoVrmPortalIdError("ioctl failed for eth0") 85 | 86 | __vrm_portal_id = info[18:24].hex() 87 | return __vrm_portal_id 88 | 89 | 90 | # See VE.Can registers - public.docx for definition of this conversion 91 | def convert_vreg_version_to_readable(version): 92 | def str_to_arr(x, length): 93 | a = [] 94 | for i in range(0, len(x), length): 95 | a.append(x[i:i+length]) 96 | return a 97 | 98 | x = "%x" % version 99 | x = x.upper() 100 | 101 | if len(x) == 5 or len(x) == 3 or len(x) == 1: 102 | x = '0' + x 103 | 104 | a = str_to_arr(x, 2); 105 | 106 | # remove the first 00 if there are three bytes and it is 00 107 | if len(a) == 3 and a[0] == '00': 108 | a.remove(0); 109 | 110 | # if we have two or three bytes now, and the first character is a 0, remove it 111 | if len(a) >= 2 and a[0][0:1] == '0': 112 | a[0] = a[0][1]; 113 | 114 | result = '' 115 | for item in a: 116 | result += ('.' if result != '' else '') + item 117 | 118 | 119 | result = 'v' + result 120 | 121 | return result 122 | 123 | 124 | def get_free_space(path): 125 | result = -1 126 | 127 | try: 128 | s = statvfs(path) 129 | result = s.f_frsize * s.f_bavail # Number of free bytes that ordinary users 130 | except Exception as ex: 131 | logger.info("Error while retrieving free space for path %s: %s" % (path, ex)) 132 | 133 | return result 134 | 135 | 136 | def _get_sysfs_machine_name(): 137 | try: 138 | with open('/sys/firmware/devicetree/base/model', 'r') as f: 139 | return f.read().rstrip('\x00') 140 | except IOError: 141 | pass 142 | 143 | return None 144 | 145 | # Returns None if it cannot find a machine name. Otherwise returns the string 146 | # containing the name 147 | def get_machine_name(): 148 | # First try calling the venus utility script 149 | try: 150 | return check_output("/usr/bin/product-name").strip().decode('UTF-8') 151 | except (CalledProcessError, OSError): 152 | pass 153 | 154 | # Fall back to sysfs 155 | name = _get_sysfs_machine_name() 156 | if name is not None: 157 | return name 158 | 159 | # Fall back to venus build machine name 160 | try: 161 | with open('/etc/venus/machine', 'r', encoding='UTF-8') as f: 162 | return f.read().strip() 163 | except IOError: 164 | pass 165 | 166 | return None 167 | 168 | 169 | def get_product_id(): 170 | """ Find the machine ID and return it. """ 171 | 172 | # First try calling the venus utility script 173 | try: 174 | return check_output("/usr/bin/product-id").strip().decode('UTF-8') 175 | except (CalledProcessError, OSError): 176 | pass 177 | 178 | # Fall back machine name mechanism 179 | name = _get_sysfs_machine_name() 180 | return { 181 | 'Color Control GX': 'C001', 182 | 'Venus GX': 'C002', 183 | 'Octo GX': 'C006', 184 | 'EasySolar-II': 'C007', 185 | 'MultiPlus-II': 'C008', 186 | 'Maxi GX': 'C009', 187 | 'Cerbo GX': 'C00A' 188 | }.get(name, 'C003') # C003 is Generic 189 | 190 | 191 | # Returns False if it cannot open the file. Otherwise returns its rstripped contents 192 | def read_file(path): 193 | content = False 194 | 195 | try: 196 | with open(path, 'r') as f: 197 | content = f.read().rstrip() 198 | except Exception as ex: 199 | logger.debug("Error while reading %s: %s" % (path, ex)) 200 | 201 | return content 202 | 203 | 204 | def wrap_dbus_value(value): 205 | if value is None: 206 | return VEDBUS_INVALID 207 | if isinstance(value, float): 208 | return dbus.Double(value, variant_level=1) 209 | if isinstance(value, bool): 210 | return dbus.Boolean(value, variant_level=1) 211 | if isinstance(value, int): 212 | try: 213 | return dbus.Int32(value, variant_level=1) 214 | except OverflowError: 215 | return dbus.Int64(value, variant_level=1) 216 | if isinstance(value, str): 217 | return dbus.String(value, variant_level=1) 218 | if isinstance(value, list): 219 | if len(value) == 0: 220 | # If the list is empty we cannot infer the type of the contents. So assume unsigned integer. 221 | # A (signed) integer is dangerous, because an empty list of signed integers is used to encode 222 | # an invalid value. 223 | return dbus.Array([], signature=dbus.Signature('u'), variant_level=1) 224 | return dbus.Array([wrap_dbus_value(x) for x in value], variant_level=1) 225 | if isinstance(value, dict): 226 | # Wrapping the keys of the dictionary causes D-Bus errors like: 227 | # 'arguments to dbus_message_iter_open_container() were incorrect, 228 | # assertion "(type == DBUS_TYPE_ARRAY && contained_signature && 229 | # *contained_signature == DBUS_DICT_ENTRY_BEGIN_CHAR) || (contained_signature == NULL || 230 | # _dbus_check_is_valid_signature (contained_signature))" failed in file ...' 231 | return dbus.Dictionary({(k, wrap_dbus_value(v)) for k, v in value.items()}, variant_level=1) 232 | return value 233 | 234 | 235 | dbus_int_types = (dbus.Int32, dbus.UInt32, dbus.Byte, dbus.Int16, dbus.UInt16, dbus.UInt32, dbus.Int64, dbus.UInt64) 236 | 237 | 238 | def unwrap_dbus_value(val): 239 | """Converts D-Bus values back to the original type. For example if val is of type DBus.Double, 240 | a float will be returned.""" 241 | if isinstance(val, dbus_int_types): 242 | return int(val) 243 | if isinstance(val, dbus.Double): 244 | return float(val) 245 | if isinstance(val, dbus.Array): 246 | v = [unwrap_dbus_value(x) for x in val] 247 | return None if len(v) == 0 else v 248 | if isinstance(val, (dbus.Signature, dbus.String)): 249 | return str(val) 250 | # Python has no byte type, so we convert to an integer. 251 | if isinstance(val, dbus.Byte): 252 | return int(val) 253 | if isinstance(val, dbus.ByteArray): 254 | return "".join([bytes(x) for x in val]) 255 | if isinstance(val, (list, tuple)): 256 | return [unwrap_dbus_value(x) for x in val] 257 | if isinstance(val, (dbus.Dictionary, dict)): 258 | # Do not unwrap the keys, see comment in wrap_dbus_value 259 | return dict([(x, unwrap_dbus_value(y)) for x, y in val.items()]) 260 | if isinstance(val, dbus.Boolean): 261 | return bool(val) 262 | return val 263 | 264 | # When supported, only name owner changes for the the given namespace are reported. This 265 | # prevents spending cpu time at irrelevant changes, like scripts accessing the bus temporarily. 266 | def add_name_owner_changed_receiver(dbus, name_owner_changed, namespace="com.victronenergy"): 267 | # support for arg0namespace is submitted upstream, but not included at the time of 268 | # writing, Venus OS does support it, so try if it works. 269 | if namespace is None: 270 | dbus.add_signal_receiver(name_owner_changed, signal_name='NameOwnerChanged') 271 | else: 272 | try: 273 | dbus.add_signal_receiver(name_owner_changed, 274 | signal_name='NameOwnerChanged', arg0namespace=namespace) 275 | except TypeError: 276 | dbus.add_signal_receiver(name_owner_changed, signal_name='NameOwnerChanged') 277 | -------------------------------------------------------------------------------- /venus-data-UninstallPackages.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kwindrem/SetupHelper/3d0779cf9bd24a860003b0ccd3c55d359ac69b7c/venus-data-UninstallPackages.tgz -------------------------------------------------------------------------------- /venus-data.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kwindrem/SetupHelper/3d0779cf9bd24a860003b0ccd3c55d359ac69b7c/venus-data.tgz -------------------------------------------------------------------------------- /version: -------------------------------------------------------------------------------- 1 | v8.33 2 | --------------------------------------------------------------------------------