Feature: JK BMS: export (more) data to live view and MQTT (#549)

* add more values to web app live view. this should add all interesting
  values for the web app live view. those include important values and
  values that change frequently.

* add more interesting JK BMS dummy messages: one has 0% SoC and an
  alarm (discharge undervoltage) set. the other has the undertemperature
  alarm set.

* add alarms and warnings to live view

* publish alarm and status bits through MQTT individually

* publish cell voltages to MQTT broker

* remove trailing spaces in BatteryStats class
This commit is contained in:
Bernhard Kirchen 2023-12-15 10:59:07 +01:00 committed by GitHub
parent dd8446df0a
commit 6e78c5bd1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 333 additions and 14 deletions

View File

@ -89,15 +89,29 @@ class PylontechBatteryStats : public BatteryStats {
class JkBmsBatteryStats : public BatteryStats {
public:
void getLiveViewData(JsonVariant& root) const final;
void getLiveViewData(JsonVariant& root) const final {
getJsonData(root, false);
}
void getInfoViewData(JsonVariant& root) const {
getJsonData(root, true);
}
void mqttPublish() const final;
void updateFrom(JkBms::DataPointContainer const& dp);
private:
void getJsonData(JsonVariant& root, bool verbose) const;
JkBms::DataPointContainer _dataPoints;
mutable uint32_t _lastMqttPublish = 0;
mutable uint32_t _lastFullMqttPublish = 0;
uint16_t _cellMinMilliVolt = 0;
uint16_t _cellAvgMilliVolt = 0;
uint16_t _cellMaxMilliVolt = 0;
uint32_t _cellVoltageTimestamp = 0;
};
class VictronSmartShuntStats : public BatteryStats {
@ -107,8 +121,8 @@ class VictronSmartShuntStats : public BatteryStats {
void updateFrom(VeDirectShuntController::veShuntStruct const& shuntData);
private:
float _voltage;
private:
float _voltage;
float _current;
float _temperature;
bool _tempPresent;

View File

@ -9,6 +9,54 @@
namespace JkBms {
#define ALARM_BITS(fnc) \
fnc(LowCapacity, (1<<0)) \
fnc(BmsOvertemperature, (1<<1)) \
fnc(ChargingOvervoltage, (1<<2)) \
fnc(DischargeUndervoltage, (1<<3)) \
fnc(BatteryOvertemperature, (1<<4)) \
fnc(ChargingOvercurrent, (1<<5)) \
fnc(DischargeOvercurrent, (1<<6)) \
fnc(CellVoltageDifference, (1<<7)) \
fnc(BatteryBoxOvertemperature, (1<<8)) \
fnc(BatteryUndertemperature, (1<<9)) \
fnc(CellOvervoltage, (1<<10)) \
fnc(CellUndervoltage, (1<<11)) \
fnc(AProtect, (1<<12)) \
fnc(BProtect, (1<<13)) \
fnc(Reserved1, (1<<14)) \
fnc(Reserved2, (1<<15))
enum class AlarmBits : uint16_t {
#define ALARM_ENUM(name, value) name = value,
ALARM_BITS(ALARM_ENUM)
#undef ALARM_ENUM
};
static const std::map<AlarmBits, std::string> AlarmBitTexts = {
#define ALARM_TEXT(name, value) { AlarmBits::name, #name },
ALARM_BITS(ALARM_TEXT)
#undef ALARM_TEXT
};
#define STATUS_BITS(fnc) \
fnc(ChargingActive, (1<<0)) \
fnc(DischargingActive, (1<<1)) \
fnc(BalancingActive, (1<<2)) \
fnc(BatteryOnline, (1<<3))
enum class StatusBits : uint16_t {
#define STATUS_ENUM(name, value) name = value,
STATUS_BITS(STATUS_ENUM)
#undef STATUS_ENUM
};
static const std::map<StatusBits, std::string> StatusBitTexts = {
#define STATUS_TEXT(name, value) { StatusBits::name, #name },
STATUS_BITS(STATUS_TEXT)
#undef STATUS_TEXT
};
enum class DataPointLabel : uint8_t {
CellsMilliVolt = 0x79,
BmsTempCelsius = 0x80,

View File

@ -84,7 +84,7 @@ void PylontechBatteryStats::getLiveViewData(JsonVariant& root) const
addLiveViewAlarm(root, "bmsInternal", _alarmBmsInternal);
}
void JkBmsBatteryStats::getLiveViewData(JsonVariant& root) const
void JkBmsBatteryStats::getJsonData(JsonVariant& root, bool verbose) const
{
BatteryStats::getLiveViewData(root);
@ -102,9 +102,80 @@ void JkBmsBatteryStats::getLiveViewData(JsonVariant& root) const
static_cast<float>(*oCurrent) / 1000, "A", 2);
}
auto oTemperature = _dataPoints.get<Label::BatteryTempOneCelsius>();
if (oTemperature.has_value()) {
addLiveViewValue(root, "temperature", *oTemperature, "°C", 0);
if (oVoltage.has_value() && oCurrent.has_value()) {
auto current = static_cast<float>(*oCurrent) / 1000;
auto voltage = static_cast<float>(*oVoltage) / 1000;
addLiveViewValue(root, "power", current * voltage , "W", 2);
}
if (verbose) {
auto oTemperatureOne = _dataPoints.get<Label::BatteryTempOneCelsius>();
if (oTemperatureOne.has_value()) {
addLiveViewValue(root, "batOneTemp", *oTemperatureOne, "°C", 0);
}
}
if (verbose) {
auto oTemperatureTwo = _dataPoints.get<Label::BatteryTempTwoCelsius>();
if (oTemperatureTwo.has_value()) {
addLiveViewValue(root, "batTwoTemp", *oTemperatureTwo, "°C", 0);
}
}
auto oTemperatureBms = _dataPoints.get<Label::BmsTempCelsius>();
if (oTemperatureBms.has_value()) {
addLiveViewValue(root, "bmsTemp", *oTemperatureBms, "°C", 0);
}
if (_cellVoltageTimestamp > 0) {
if (verbose) {
addLiveViewValue(root, "cellMinVoltage", static_cast<float>(_cellMinMilliVolt)/1000, "V", 3);
}
addLiveViewValue(root, "cellAvgVoltage", static_cast<float>(_cellAvgMilliVolt)/1000, "V", 3);
if (verbose) {
addLiveViewValue(root, "cellMaxVoltage", static_cast<float>(_cellMaxMilliVolt)/1000, "V", 3);
addLiveViewValue(root, "cellDiffVoltage", (_cellMaxMilliVolt - _cellMinMilliVolt), "mV", 0);
}
}
// labels BatteryChargeEnabled, BatteryDischargeEnabled, and
// BalancingEnabled refer to the user setting. we want to show the
// actual MOSFETs' state which control whether charging and discharging
// is possible and whether the BMS is currently balancing cells.
auto oStatus = _dataPoints.get<Label::StatusBitmask>();
if (oStatus.has_value()) {
using Bits = JkBms::StatusBits;
auto chargeEnabled = *oStatus & static_cast<uint16_t>(Bits::ChargingActive);
addLiveViewText(root, "chargeEnabled", (chargeEnabled?"yes":"no"));
auto dischargeEnabled = *oStatus & static_cast<uint16_t>(Bits::DischargingActive);
addLiveViewText(root, "dischargeEnabled", (dischargeEnabled?"yes":"no"));
auto balancingActive = *oStatus & static_cast<uint16_t>(Bits::BalancingActive);
addLiveViewText(root, "balancingActive", (balancingActive?"yes":"no"));
}
auto oAlarms = _dataPoints.get<Label::AlarmsBitmask>();
if (oAlarms.has_value()) {
#define ISSUE(t, x) \
auto x = *oAlarms & static_cast<uint16_t>(JkBms::AlarmBits::x); \
addLiveView##t(root, "JkBmsIssue"#x, x > 0);
ISSUE(Warning, LowCapacity);
ISSUE(Alarm, BmsOvertemperature);
ISSUE(Alarm, ChargingOvervoltage);
ISSUE(Alarm, DischargeUndervoltage);
ISSUE(Alarm, BatteryOvertemperature);
ISSUE(Alarm, ChargingOvercurrent);
ISSUE(Alarm, DischargeOvercurrent);
ISSUE(Alarm, CellVoltageDifference);
ISSUE(Alarm, BatteryBoxOvertemperature);
ISSUE(Alarm, BatteryUndertemperature);
ISSUE(Alarm, CellOvervoltage);
ISSUE(Alarm, CellUndervoltage);
ISSUE(Alarm, AProtect);
ISSUE(Alarm, BProtect);
#undef ISSUE
}
}
@ -174,6 +245,43 @@ void JkBmsBatteryStats::mqttPublish() const
MqttSettings.publish(topic, iter->second.getValueText().c_str());
}
auto oCellVoltages = _dataPoints.get<Label::CellsMilliVolt>();
if (oCellVoltages.has_value() && (fullPublish || _cellVoltageTimestamp > _lastMqttPublish)) {
unsigned idx = 1;
for (auto iter = oCellVoltages->cbegin(); iter != oCellVoltages->cend(); ++iter) {
String topic("battery/Cell");
topic += String(idx);
topic += "MilliVolt";
MqttSettings.publish(topic, String(iter->second));
++idx;
}
MqttSettings.publish("battery/CellMinMilliVolt", String(_cellMinMilliVolt));
MqttSettings.publish("battery/CellAvgMilliVolt", String(_cellAvgMilliVolt));
MqttSettings.publish("battery/CellMaxMilliVolt", String(_cellMaxMilliVolt));
MqttSettings.publish("battery/CellDiffMilliVolt", String(_cellMaxMilliVolt - _cellMinMilliVolt));
}
auto oAlarms = _dataPoints.get<Label::AlarmsBitmask>();
if (oAlarms.has_value()) {
for (auto iter = JkBms::AlarmBitTexts.begin(); iter != JkBms::AlarmBitTexts.end(); ++iter) {
auto bit = iter->first;
String value = (*oAlarms & static_cast<uint16_t>(bit))?"1":"0";
MqttSettings.publish(String("battery/alarms/") + iter->second.c_str(), value);
}
}
auto oStatus = _dataPoints.get<Label::StatusBitmask>();
if (oStatus.has_value()) {
for (auto iter = JkBms::StatusBitTexts.begin(); iter != JkBms::StatusBitTexts.end(); ++iter) {
auto bit = iter->first;
String value = (*oStatus & static_cast<uint16_t>(bit))?"1":"0";
MqttSettings.publish(String("battery/status/") + iter->second.c_str(), value);
}
}
_lastMqttPublish = millis();
if (fullPublish) { _lastFullMqttPublish = _lastMqttPublish; }
}
@ -201,6 +309,20 @@ void JkBmsBatteryStats::updateFrom(JkBms::DataPointContainer const& dp)
_dataPoints.updateFrom(dp);
auto oCellVoltages = _dataPoints.get<Label::CellsMilliVolt>();
if (oCellVoltages.has_value()) {
for (auto iter = oCellVoltages->cbegin(); iter != oCellVoltages->cend(); ++iter) {
if (iter == oCellVoltages->cbegin()) {
_cellMinMilliVolt = _cellAvgMilliVolt = _cellMaxMilliVolt = iter->second;
continue;
}
_cellMinMilliVolt = std::min(_cellMinMilliVolt, iter->second);
_cellAvgMilliVolt = (_cellAvgMilliVolt + iter->second) / 2;
_cellMaxMilliVolt = std::max(_cellMaxMilliVolt, iter->second);
}
_cellVoltageTimestamp = millis();
}
_lastUpdate = millis();
}
@ -216,7 +338,7 @@ void VictronSmartShuntStats::updateFrom(VeDirectShuntController::veShuntStruct c
_manufacturer = "Victron " + _modelName;
_temperature = shuntData.T;
_tempPresent = shuntData.tempPresent;
// shuntData.AR is a bitfield, so we need to check each bit individually
_alarmLowVoltage = shuntData.AR & 1;
_alarmHighVoltage = shuntData.AR & 2;
@ -233,19 +355,19 @@ void VictronSmartShuntStats::getLiveViewData(JsonVariant& root) const {
// values go into the "Status" card of the web application
addLiveViewValue(root, "voltage", _voltage, "V", 2);
addLiveViewValue(root, "current", _current, "A", 1);
addLiveViewValue(root, "current", _current, "A", 1);
addLiveViewValue(root, "chargeCycles", _chargeCycles, "", 0);
addLiveViewValue(root, "chargedEnergy", _chargedEnergy, "KWh", 1);
addLiveViewValue(root, "dischargedEnergy", _dischargedEnergy, "KWh", 1);
if (_tempPresent) {
addLiveViewValue(root, "temperature", _temperature, "°C", 0);
}
addLiveViewAlarm(root, "lowVoltage", _alarmLowVoltage);
addLiveViewAlarm(root, "highVoltage", _alarmHighVoltage);
addLiveViewAlarm(root, "lowSOC", _alarmLowSOC);
addLiveViewAlarm(root, "lowTemperature", _alarmLowTemperature);
addLiveViewAlarm(root, "highTemperature", _alarmHighTemperature);
addLiveViewAlarm(root, "highTemperature", _alarmHighTemperature);
}
void VictronSmartShuntStats::mqttPublish() const {

View File

@ -113,6 +113,84 @@ class DummySerial {
0x31, 0x41, 0x32, 0x34, 0x53, 0x31, 0x35, 0x50,
0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
0x00, 0x4f, 0xc1
},
{
0x4e, 0x57, 0x01, 0x21, 0x00, 0x00, 0x00, 0x00,
0x06, 0x00, 0x01, 0x79, 0x30, 0x01, 0x0c, 0x13,
0x02, 0x0c, 0x12, 0x03, 0x0c, 0x0f, 0x04, 0x0c,
0x15, 0x05, 0x0c, 0x0d, 0x06, 0x0c, 0x13, 0x07,
0x0c, 0x16, 0x08, 0x0c, 0x13, 0x09, 0x0b, 0xdb,
0x0a, 0x0b, 0xf6, 0x0b, 0x0c, 0x17, 0x0c, 0x0b,
0xf5, 0x0d, 0x0c, 0x16, 0x0e, 0x0c, 0x1a, 0x0f,
0x0c, 0x1b, 0x10, 0x0c, 0x1c, 0x80, 0x00, 0x18,
0x81, 0x00, 0x18, 0x82, 0x00, 0x18, 0x83, 0x13,
0x49, 0x84, 0x00, 0x00, 0x85, 0x00, 0x86, 0x02,
0x87, 0x00, 0x23, 0x89, 0x00, 0x00, 0x20, 0x14,
0x8a, 0x00, 0x10, 0x8b, 0x00, 0x08, 0x8c, 0x00,
0x05, 0x8e, 0x16, 0x80, 0x8f, 0x12, 0xc0, 0x90,
0x0e, 0x10, 0x91, 0x0c, 0xda, 0x92, 0x00, 0x05,
0x93, 0x0b, 0xb8, 0x94, 0x0c, 0x80, 0x95, 0x00,
0x05, 0x96, 0x01, 0x2c, 0x97, 0x00, 0x28, 0x98,
0x01, 0x2c, 0x99, 0x00, 0x28, 0x9a, 0x00, 0x1e,
0x9b, 0x0b, 0xb8, 0x9c, 0x00, 0x0a, 0x9d, 0x01,
0x9e, 0x00, 0x64, 0x9f, 0x00, 0x50, 0xa0, 0x00,
0x64, 0xa1, 0x00, 0x64, 0xa2, 0x00, 0x14, 0xa3,
0x00, 0x46, 0xa4, 0x00, 0x46, 0xa5, 0x00, 0x00,
0xa6, 0x00, 0x02, 0xa7, 0xff, 0xec, 0xa8, 0xff,
0xf6, 0xa9, 0x10, 0xaa, 0x00, 0x00, 0x00, 0xe6,
0xab, 0x01, 0xac, 0x01, 0xad, 0x04, 0x4d, 0xae,
0x01, 0xaf, 0x00, 0xb0, 0x00, 0x0a, 0xb1, 0x14,
0xb2, 0x32, 0x32, 0x31, 0x31, 0x38, 0x37, 0x00,
0x00, 0x00, 0x00, 0xb3, 0x00, 0xb4, 0x62, 0x65,
0x6b, 0x69, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x32,
0x33, 0x30, 0x36, 0xb6, 0x00, 0x02, 0x17, 0x10,
0xb7, 0x31, 0x31, 0x2e, 0x58, 0x57, 0x5f, 0x53,
0x31, 0x31, 0x2e, 0x32, 0x36, 0x32, 0x48, 0x5f,
0xb8, 0x00, 0xb9, 0x00, 0x00, 0x00, 0xe6, 0xba,
0x62, 0x65, 0x6b, 0x69, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x4a, 0x4b, 0x5f, 0x42,
0x31, 0x41, 0x32, 0x34, 0x53, 0x31, 0x35, 0x50,
0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
0x00, 0x45, 0xce
},
{
0x4e, 0x57, 0x01, 0x21, 0x00, 0x00, 0x00, 0x00,
0x06, 0x00, 0x01, 0x79, 0x30, 0x01, 0x0c, 0x07,
0x02, 0x0c, 0x0a, 0x03, 0x0c, 0x0b, 0x04, 0x0c,
0x08, 0x05, 0x0c, 0x05, 0x06, 0x0c, 0x0b, 0x07,
0x0c, 0x07, 0x08, 0x0c, 0x0a, 0x09, 0x0c, 0x08,
0x0a, 0x0c, 0x06, 0x0b, 0x0c, 0x0a, 0x0c, 0x0c,
0x05, 0x0d, 0x0c, 0x0a, 0x0e, 0x0c, 0x0a, 0x0f,
0x0c, 0x0a, 0x10, 0x0c, 0x0a, 0x80, 0x00, 0x06,
0x81, 0x00, 0x03, 0x82, 0x00, 0x03, 0x83, 0x13,
0x40, 0x84, 0x00, 0x00, 0x85, 0x29, 0x86, 0x02,
0x87, 0x00, 0x01, 0x89, 0x00, 0x00, 0x01, 0x0a,
0x8a, 0x00, 0x10, 0x8b, 0x02, 0x00, 0x8c, 0x00,
0x02, 0x8e, 0x16, 0x80, 0x8f, 0x10, 0x40, 0x90,
0x0e, 0x10, 0x91, 0x0d, 0xde, 0x92, 0x00, 0x05,
0x93, 0x0a, 0x28, 0x94, 0x0a, 0x5a, 0x95, 0x00,
0x05, 0x96, 0x01, 0x2c, 0x97, 0x00, 0x28, 0x98,
0x01, 0x2c, 0x99, 0x00, 0x28, 0x9a, 0x00, 0x1e,
0x9b, 0x0b, 0xb8, 0x9c, 0x00, 0x0a, 0x9d, 0x01,
0x9e, 0x00, 0x5a, 0x9f, 0x00, 0x50, 0xa0, 0x00,
0x64, 0xa1, 0x00, 0x64, 0xa2, 0x00, 0x14, 0xa3,
0x00, 0x37, 0xa4, 0x00, 0x37, 0xa5, 0x00, 0x03,
0xa6, 0x00, 0x05, 0xa7, 0xff, 0xec, 0xa8, 0xff,
0xf6, 0xa9, 0x10, 0xaa, 0x00, 0x00, 0x00, 0xe6,
0xab, 0x01, 0xac, 0x01, 0xad, 0x04, 0x4d, 0xae,
0x01, 0xaf, 0x00, 0xb0, 0x00, 0x0a, 0xb1, 0x14,
0xb2, 0x32, 0x32, 0x31, 0x31, 0x38, 0x37, 0x00,
0x00, 0x00, 0x00, 0xb3, 0x00, 0xb4, 0x62, 0x65,
0x6b, 0x69, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x32,
0x33, 0x30, 0x36, 0xb6, 0x00, 0x03, 0xb7, 0x2d,
0xb7, 0x31, 0x31, 0x2e, 0x58, 0x57, 0x5f, 0x53,
0x31, 0x31, 0x2e, 0x32, 0x36, 0x32, 0x48, 0x5f,
0xb8, 0x00, 0xb9, 0x00, 0x00, 0x00, 0xe6, 0xba,
0x62, 0x65, 0x6b, 0x69, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x4a, 0x4b, 0x5f, 0x42,
0x31, 0x41, 0x32, 0x34, 0x53, 0x31, 0x35, 0x50,
0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
0x00, 0x41, 0x7b
}
};
size_t _msg_idx = 0;

View File

@ -804,19 +804,37 @@
"stateOfHealth": "Batteriezustand (SoH)",
"voltage": "Spannung",
"current": "Strom",
"power": "Leistung",
"temperature": "Temperatur",
"bmsTemp": "BMS-Temperatur",
"chargeVoltage": "Gewünschte Ladespannung (BMS)",
"chargeCurrentLimitation": "Ladestromlimit",
"dischargeCurrentLimitation": "Entladestromlimit",
"chargeEnabled": "Laden ermöglicht",
"dischargeEnabled": "Entladen ermöglicht",
"balancingActive": "Ausgleichen aktiv",
"chargeImmediately": "Sofortiges Laden angefordert",
"cellAvgVoltage": "Durchschnittliche Zellspannung",
"issues": "Meldungen",
"noIssues": "Keine Meldungen",
"issueName": "Bezeichnung",
"issueType": "Art",
"alarm": "Alarm",
"warning": "Warnung",
"JkBmsIssueLowCapacity": "Niedrige Kapazität",
"JkBmsIssueBmsOvertemperature": "BMS Übertemperatur",
"JkBmsIssueChargingOvervoltage": "Überspannung (Akkupack)",
"JkBmsIssueDischargeUndervoltage": "Unterspannung (Akkupack)",
"JkBmsIssueBatteryOvertemperature": "Batterie Übertemperatur",
"JkBmsIssueChargingOvercurrent": "Überstrom (Laden)",
"JkBmsIssueDischargeOvercurrent": "Überstrom (Entladen)",
"JkBmsIssueCellVoltageDifference": "Zellspannungsdifferenz zu hoch",
"JkBmsIssueBatteryBoxOvertemperature": "Batterie (Box?) Übertemperatur",
"JkBmsIssueBatteryUndertemperature": "Batterie Untertemperatur",
"JkBmsIssueCellOvervoltage": "Überspannung (einzelne Zelle)",
"JkBmsIssueCellUndervoltage": "Unterspannung (einzelne Zelle)",
"JkBmsIssueAProtect": "AProtect (Bedeutung?)",
"JkBmsIssueBProtect": "BProtect (Bedeutung?)",
"highCurrentDischarge": "Hoher Entladestrom",
"overCurrentDischarge": "Überstrom (Entladen)",
"highCurrentCharge": "Hoher Ladestrom",

View File

@ -814,19 +814,37 @@
"stateOfHealth": "State of Health",
"voltage": "Voltage",
"current": "Current",
"power": "Power",
"temperature": "Temperature",
"bmsTemp": "BMS temperature",
"chargeVoltage": "Requested charge voltage",
"chargeCurrentLimitation": "Charge current limit",
"dischargeCurrentLimitation": "Discharge current limit",
"chargeEnabled": "Charging possible",
"dischargeEnabled": "Discharging possible",
"balancingActive": "Balancing active",
"chargeImmediately": "Immediate charging requested",
"cellAvgVoltage": "Average cell voltage",
"issues": "Issues",
"noIssues": "No Issues",
"issueName": "Name",
"issueType": "Type",
"alarm": "Alarm",
"warning": "Warning",
"JkBmsIssueLowCapacity": "Low Capacity",
"JkBmsIssueBmsOvertemperature": "BMS overtemperature",
"JkBmsIssueChargingOvervoltage": "Overvoltage (sum of all cells)",
"JkBmsIssueDischargeUndervoltage": "Undervoltage (sum of all cells)",
"JkBmsIssueBatteryOvertemperature": "Battery overtemperature",
"JkBmsIssueChargingOvercurrent": "Overcurrent (Charging)",
"JkBmsIssueDischargeOvercurrent": "Overcurrent (Discharging)",
"JkBmsIssueCellVoltageDifference": "Cell voltage difference too high",
"JkBmsIssueBatteryBoxOvertemperature": "Battery (box?) overtemperature",
"JkBmsIssueBatteryUndertemperature": "Battery undertemperature",
"JkBmsIssueCellOvervoltage": "Overvoltage (single cell)",
"JkBmsIssueCellUndervoltage": "Undervoltage (single cell)",
"JkBmsIssueAProtect": "AProtect (meaning?)",
"JkBmsIssueBProtect": "BProtect (meaning?)",
"highCurrentDischarge": "High current (discharge)",
"overCurrentDischarge": "Overcurrent (discharge)",
"highCurrentCharge": "High current (charge)",

View File

@ -757,7 +757,7 @@
"Save": "@:dtuadmin.Save"
},
"battery": {
"battery": "battery",
"battery": "Battery",
"DataAge": "Data Age: ",
"Seconds": " {val} seconds",
"Status": "Status",
@ -766,20 +766,41 @@
"no": "@:base.No",
"Value": "Value",
"Unit": "Unit",
"SoC": "State of charge",
"stateOfHealth": "State of health",
"SoC": "State of Charge",
"stateOfHealth": "State of Health",
"voltage": "Voltage",
"current": "Current",
"power": "Power",
"temperature": "Temperature",
"bmsTemp": "BMS temperature",
"chargeVoltage": "Requested charge voltage",
"chargeCurrentLimitation": "Charge current limit",
"dischargeCurrentLimitation": "Discharge current limit",
"chargeEnabled": "Charging possible",
"dischargeEnabled": "Discharging possible",
"balancingActive": "Balancing active",
"chargeImmediately": "Immediate charging requested",
"cellAvgVoltage": "Average cell voltage",
"issues": "Issues",
"noIssues": "No Issues",
"issueName": "Name",
"issueType": "Type",
"alarm": "Alarm",
"warning": "Warning",
"JkBmsIssueLowCapacity": "Low Capacity",
"JkBmsIssueBmsOvertemperature": "BMS overtemperature",
"JkBmsIssueChargingOvervoltage": "Overvoltage (sum of all cells)",
"JkBmsIssueDischargeUndervoltage": "Undervoltage (sum of all cells)",
"JkBmsIssueBatteryOvertemperature": "Battery overtemperature",
"JkBmsIssueChargingOvercurrent": "Overcurrent (Charging)",
"JkBmsIssueDischargeOvercurrent": "Overcurrent (Discharging)",
"JkBmsIssueCellVoltageDifference": "Cell voltage difference too high",
"JkBmsIssueBatteryBoxOvertemperature": "Battery (box?) overtemperature",
"JkBmsIssueBatteryUndertemperature": "Battery undertemperature",
"JkBmsIssueCellOvervoltage": "Overvoltage (single cell)",
"JkBmsIssueCellUndervoltage": "Undervoltage (single cell)",
"JkBmsIssueAProtect": "AProtect (meaning?)",
"JkBmsIssueBProtect": "BProtect (meaning?)",
"highCurrentDischarge": "High current (discharge)",
"overCurrentDischarge": "Overcurrent (discharge)",
"highCurrentCharge": "High current (charge)",