diff --git a/include/BatteryStats.h b/include/BatteryStats.h index 7051d2b15..0f86c7c5d 100644 --- a/include/BatteryStats.h +++ b/include/BatteryStats.h @@ -58,6 +58,8 @@ class BatteryStats { } String _manufacturer = "unknown"; + String _hwversion = ""; + String _fwversion = ""; uint32_t _lastUpdate = 0; private: @@ -157,7 +159,6 @@ class VictronSmartShuntStats : public BatteryStats { uint32_t _timeToGo; float _chargedEnergy; float _dischargedEnergy; - String _modelName; int32_t _instantaneousPower; float _midpointVoltage; float _midpointDeviation; diff --git a/lib/VeDirectFrameHandler/VeDirectData.cpp b/lib/VeDirectFrameHandler/VeDirectData.cpp index c0deb5faa..a67175fde 100644 --- a/lib/VeDirectFrameHandler/VeDirectData.cpp +++ b/lib/VeDirectFrameHandler/VeDirectData.cpp @@ -158,18 +158,25 @@ uint32_t veStruct::getFwVersionAsInteger() const String veStruct::getFwVersionFormatted() const { char const* strVersion = firmwareVer_FW; + char rc = 0; // VE.Direct protocol manual states that the first char can be a non-digit, // in which case that char represents a release candidate version - if (strVersion[0] < '0' || strVersion[0] > '9') { ++strVersion; } + if (strVersion[0] < '0' || strVersion[0] > '9') { + rc = strVersion[0]; + ++strVersion; + } + + // SmartShunt firmware version is transmitted with leading zero(es) + while (strVersion[0] == '0') { ++strVersion; } String res(strVersion[0]); res += "."; res += strVersion + 1; - if (strVersion > firmwareVer_FW) { + if (rc != 0) { res += "-rc-"; - res += firmwareVer_FW[0]; + res += rc; } return res; diff --git a/src/BatteryStats.cpp b/src/BatteryStats.cpp index c1102c59f..61f23823f 100644 --- a/src/BatteryStats.cpp +++ b/src/BatteryStats.cpp @@ -62,6 +62,12 @@ bool BatteryStats::updateAvailable(uint32_t since) const void BatteryStats::getLiveViewData(JsonVariant& root) const { root["manufacturer"] = _manufacturer; + if (!_fwversion.isEmpty()) { + root["fwversion"] = _fwversion; + } + if (!_hwversion.isEmpty()) { + root["hwversion"] = _hwversion; + } root["data_age"] = getAgeSeconds(); addLiveViewValue(root, "SoC", _soc, "%", _socPrecision); @@ -375,20 +381,38 @@ void JkBmsBatteryStats::updateFrom(JkBms::DataPointContainer const& dp) _cellVoltageTimestamp = millis(); } + auto oVersion = _dataPoints.get(); + if (oVersion.has_value()) { + // raw: "11.XW_S11.262H_" + // => Hardware "V11.XW" (displayed in Android app) + // => Software "V11.262H" (displayed in Android app) + auto first = oVersion->find('_'); + if (first != std::string::npos) { + _hwversion = oVersion->substr(0, first).c_str(); + + auto second = oVersion->find('_', first + 1); + + // the 'S' seems to be merely an indicator for "software"? + if (oVersion->at(first + 1) == 'S') { first++; } + + _fwversion = oVersion->substr(first + 1, second - first - 1).c_str(); + } + } + _lastUpdate = millis(); } void VictronSmartShuntStats::updateFrom(VeDirectShuntController::data_t const& shuntData) { BatteryStats::setVoltage(shuntData.batteryVoltage_V_mV / 1000.0, millis()); BatteryStats::setSoC(static_cast(shuntData.SOC) / 10, 1/*precision*/, millis()); + _fwversion = shuntData.getFwVersionFormatted(); _current = static_cast(shuntData.batteryCurrent_I_mA) / 1000; - _modelName = shuntData.getPidAsString().data(); _chargeCycles = shuntData.H4; _timeToGo = shuntData.TTG / 60; _chargedEnergy = static_cast(shuntData.H18) / 100; _dischargedEnergy = static_cast(shuntData.H17) / 100; - _manufacturer = "Victron " + _modelName; + _manufacturer = String("Victron ") + shuntData.getPidAsString().data(); _temperature = shuntData.T; _tempPresent = shuntData.tempPresent; _midpointVoltage = static_cast(shuntData.VM) / 1000; diff --git a/webapp/src/components/BatteryView.vue b/webapp/src/components/BatteryView.vue index fe539d9bf..929669197 100644 --- a/webapp/src/components/BatteryView.vue +++ b/webapp/src/components/BatteryView.vue @@ -18,6 +18,12 @@
{{ $t('battery.battery') }}: {{ batteryData.manufacturer }}
+
+ {{ $t('battery.FwVersion') }}: {{ batteryData.fwversion }} +
+
+ {{ $t('battery.HwVersion') }}: {{ batteryData.hwversion }} +
{{ $t('battery.DataAge') }} {{ $t('battery.Seconds', { 'val': batteryData.data_age }) }}
diff --git a/webapp/src/locales/de.json b/webapp/src/locales/de.json index 8435e815a..ff73380c8 100644 --- a/webapp/src/locales/de.json +++ b/webapp/src/locales/de.json @@ -874,6 +874,8 @@ }, "battery": { "battery": "Batterie", + "FwVersion": "Firmware-Version", + "HwVersion": "Hardware-Version", "DataAge": "letzte Aktualisierung: ", "Seconds": "vor {val} Sekunden", "status": "Status", diff --git a/webapp/src/locales/en.json b/webapp/src/locales/en.json index 34428706e..de7240d76 100644 --- a/webapp/src/locales/en.json +++ b/webapp/src/locales/en.json @@ -881,6 +881,8 @@ }, "battery": { "battery": "Battery", + "FwVersion": "Firmware Version", + "HwVersion": "Hardware Version", "DataAge": "Data Age: ", "Seconds": " {val} seconds", "status": "Status", diff --git a/webapp/src/locales/fr.json b/webapp/src/locales/fr.json index ee6ab0f88..2db449fcb 100644 --- a/webapp/src/locales/fr.json +++ b/webapp/src/locales/fr.json @@ -866,6 +866,8 @@ }, "battery": { "battery": "Battery", + "FwVersion": "Firmware Version", + "HwVersion": "Hardware Version", "DataAge": "Data Age: ", "Seconds": " {val} seconds", "status": "Status", diff --git a/webapp/src/types/BatteryDataStatus.ts b/webapp/src/types/BatteryDataStatus.ts index c9167d3c3..0143725d8 100644 --- a/webapp/src/types/BatteryDataStatus.ts +++ b/webapp/src/types/BatteryDataStatus.ts @@ -4,6 +4,8 @@ type BatteryData = (ValueObject | string)[]; export interface Battery { manufacturer: string; + fwversion: string; + hwversion: string; data_age: number; values: BatteryData[]; issues: number[];