feature: show battery voltage, current, and power in live view (#1131)
* show battery voltage, current, and power in live view header (the "totals") * show battery current and power in extra card * use soc and current precision in live view * BatteryStats: do not knowingly publish invalid data: not all battery providers know all values the base class manages. make sure to prevent publishing invalid values. Co-authored-by: Bernhard Kirchen <schlimmchen@posteo.net>
This commit is contained in:
parent
e95b70efeb
commit
accc70dea0
@ -14,33 +14,37 @@ class BatteryStats {
|
|||||||
public:
|
public:
|
||||||
String const& getManufacturer() const { return _manufacturer; }
|
String const& getManufacturer() const { return _manufacturer; }
|
||||||
|
|
||||||
// the last time *any* datum was updated
|
// the last time *any* data was updated
|
||||||
uint32_t getAgeSeconds() const { return (millis() - _lastUpdate) / 1000; }
|
uint32_t getAgeSeconds() const { return (millis() - _lastUpdate) / 1000; }
|
||||||
bool updateAvailable(uint32_t since) const;
|
bool updateAvailable(uint32_t since) const;
|
||||||
|
|
||||||
uint8_t getSoC() const { return _soc; }
|
uint8_t getSoC() const { return _soc; }
|
||||||
uint32_t getSoCAgeSeconds() const { return (millis() - _lastUpdateSoC) / 1000; }
|
uint32_t getSoCAgeSeconds() const { return (millis() - _lastUpdateSoC) / 1000; }
|
||||||
|
uint8_t getSoCPrecision() const { return _socPrecision; }
|
||||||
|
|
||||||
float getVoltage() const { return _voltage; }
|
float getVoltage() const { return _voltage; }
|
||||||
uint32_t getVoltageAgeSeconds() const { return (millis() - _lastUpdateVoltage) / 1000; }
|
uint32_t getVoltageAgeSeconds() const { return (millis() - _lastUpdateVoltage) / 1000; }
|
||||||
|
|
||||||
|
float getChargeCurrent() const { return _current; };
|
||||||
|
uint8_t getChargeCurrentPrecision() const { return _currentPrecision; }
|
||||||
|
|
||||||
// convert stats to JSON for web application live view
|
// convert stats to JSON for web application live view
|
||||||
virtual void getLiveViewData(JsonVariant& root) const;
|
virtual void getLiveViewData(JsonVariant& root) const;
|
||||||
|
|
||||||
void mqttLoop();
|
void mqttLoop();
|
||||||
|
|
||||||
// the interval at which all battery datums will be re-published, even
|
// the interval at which all battery data will be re-published, even
|
||||||
// if they did not change. used to calculate Home Assistent expiration.
|
// if they did not change. used to calculate Home Assistent expiration.
|
||||||
virtual uint32_t getMqttFullPublishIntervalMs() const;
|
virtual uint32_t getMqttFullPublishIntervalMs() const;
|
||||||
|
|
||||||
bool isSoCValid() const { return _lastUpdateSoC > 0; }
|
bool isSoCValid() const { return _lastUpdateSoC > 0; }
|
||||||
bool isVoltageValid() const { return _lastUpdateVoltage > 0; }
|
bool isVoltageValid() const { return _lastUpdateVoltage > 0; }
|
||||||
|
bool isCurrentValid() const { return _lastUpdateCurrent > 0; }
|
||||||
|
|
||||||
// returns true if the battery reached a critically low voltage/SoC,
|
// returns true if the battery reached a critically low voltage/SoC,
|
||||||
// such that it is in need of charging to prevent degredation.
|
// such that it is in need of charging to prevent degredation.
|
||||||
virtual bool getImmediateChargingRequest() const { return false; };
|
virtual bool getImmediateChargingRequest() const { return false; };
|
||||||
|
|
||||||
virtual float getChargeCurrent() const { return 0; };
|
|
||||||
virtual float getChargeCurrentLimitation() const { return FLT_MAX; };
|
virtual float getChargeCurrentLimitation() const { return FLT_MAX; };
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -57,6 +61,12 @@ class BatteryStats {
|
|||||||
_lastUpdateVoltage = _lastUpdate = timestamp;
|
_lastUpdateVoltage = _lastUpdate = timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setCurrent(float current, uint8_t precision, uint32_t timestamp) {
|
||||||
|
_current = current;
|
||||||
|
_currentPrecision = precision;
|
||||||
|
_lastUpdateCurrent = _lastUpdate = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
String _manufacturer = "unknown";
|
String _manufacturer = "unknown";
|
||||||
String _hwversion = "";
|
String _hwversion = "";
|
||||||
String _fwversion = "";
|
String _fwversion = "";
|
||||||
@ -70,6 +80,12 @@ class BatteryStats {
|
|||||||
uint32_t _lastUpdateSoC = 0;
|
uint32_t _lastUpdateSoC = 0;
|
||||||
float _voltage = 0; // total battery pack voltage
|
float _voltage = 0; // total battery pack voltage
|
||||||
uint32_t _lastUpdateVoltage = 0;
|
uint32_t _lastUpdateVoltage = 0;
|
||||||
|
|
||||||
|
// total current into (positive) or from (negative)
|
||||||
|
// the battery, i.e., the charging current
|
||||||
|
float _current = 0;
|
||||||
|
uint8_t _currentPrecision = 0; // decimal places
|
||||||
|
uint32_t _lastUpdateCurrent = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PylontechBatteryStats : public BatteryStats {
|
class PylontechBatteryStats : public BatteryStats {
|
||||||
@ -79,7 +95,6 @@ class PylontechBatteryStats : public BatteryStats {
|
|||||||
void getLiveViewData(JsonVariant& root) const final;
|
void getLiveViewData(JsonVariant& root) const final;
|
||||||
void mqttPublish() const final;
|
void mqttPublish() const final;
|
||||||
bool getImmediateChargingRequest() const { return _chargeImmediately; } ;
|
bool getImmediateChargingRequest() const { return _chargeImmediately; } ;
|
||||||
float getChargeCurrent() const { return _current; } ;
|
|
||||||
float getChargeCurrentLimitation() const { return _chargeCurrentLimitation; } ;
|
float getChargeCurrentLimitation() const { return _chargeCurrentLimitation; } ;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -90,9 +105,6 @@ class PylontechBatteryStats : public BatteryStats {
|
|||||||
float _chargeCurrentLimitation;
|
float _chargeCurrentLimitation;
|
||||||
float _dischargeCurrentLimitation;
|
float _dischargeCurrentLimitation;
|
||||||
uint16_t _stateOfHealth;
|
uint16_t _stateOfHealth;
|
||||||
// total current into (positive) or from (negative)
|
|
||||||
// the battery, i.e., the charging current
|
|
||||||
float _current;
|
|
||||||
float _temperature;
|
float _temperature;
|
||||||
|
|
||||||
bool _alarmOverCurrentDischarge;
|
bool _alarmOverCurrentDischarge;
|
||||||
@ -122,7 +134,6 @@ class PytesBatteryStats : public BatteryStats {
|
|||||||
public:
|
public:
|
||||||
void getLiveViewData(JsonVariant& root) const final;
|
void getLiveViewData(JsonVariant& root) const final;
|
||||||
void mqttPublish() const final;
|
void mqttPublish() const final;
|
||||||
float getChargeCurrent() const { return _current; } ;
|
|
||||||
float getChargeCurrentLimitation() const { return _chargeCurrentLimit; } ;
|
float getChargeCurrentLimitation() const { return _chargeCurrentLimit; } ;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -144,9 +155,6 @@ class PytesBatteryStats : public BatteryStats {
|
|||||||
|
|
||||||
uint16_t _stateOfHealth;
|
uint16_t _stateOfHealth;
|
||||||
|
|
||||||
// total current into (positive) or from (negative)
|
|
||||||
// the battery, i.e., the charging current
|
|
||||||
float _current;
|
|
||||||
float _temperature;
|
float _temperature;
|
||||||
|
|
||||||
uint16_t _cellMinMilliVolt;
|
uint16_t _cellMinMilliVolt;
|
||||||
@ -231,7 +239,6 @@ class VictronSmartShuntStats : public BatteryStats {
|
|||||||
void updateFrom(VeDirectShuntController::data_t const& shuntData);
|
void updateFrom(VeDirectShuntController::data_t const& shuntData);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float _current;
|
|
||||||
float _temperature;
|
float _temperature;
|
||||||
bool _tempPresent;
|
bool _tempPresent;
|
||||||
uint8_t _chargeCycles;
|
uint8_t _chargeCycles;
|
||||||
@ -259,7 +266,7 @@ class MqttBatteryStats : public BatteryStats {
|
|||||||
// we do NOT publish the same data under a different topic.
|
// we do NOT publish the same data under a different topic.
|
||||||
void mqttPublish() const final { }
|
void mqttPublish() const final { }
|
||||||
|
|
||||||
// if the voltage is subscribed to at all, it alone does not warrant a
|
// we don't need a card in the liveview, since the SoC and
|
||||||
// card in the live view, since the SoC is already displayed at the top
|
// voltage (if available) is already displayed at the top.
|
||||||
void getLiveViewData(JsonVariant& root) const final { }
|
void getLiveViewData(JsonVariant& root) const final { }
|
||||||
};
|
};
|
||||||
|
|||||||
@ -78,6 +78,7 @@ void BatteryStats::getLiveViewData(JsonVariant& root) const
|
|||||||
|
|
||||||
addLiveViewValue(root, "SoC", _soc, "%", _socPrecision);
|
addLiveViewValue(root, "SoC", _soc, "%", _socPrecision);
|
||||||
addLiveViewValue(root, "voltage", _voltage, "V", 2);
|
addLiveViewValue(root, "voltage", _voltage, "V", 2);
|
||||||
|
addLiveViewValue(root, "current", _current, "A", _currentPrecision);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PylontechBatteryStats::getLiveViewData(JsonVariant& root) const
|
void PylontechBatteryStats::getLiveViewData(JsonVariant& root) const
|
||||||
@ -89,7 +90,6 @@ void PylontechBatteryStats::getLiveViewData(JsonVariant& root) const
|
|||||||
addLiveViewValue(root, "chargeCurrentLimitation", _chargeCurrentLimitation, "A", 1);
|
addLiveViewValue(root, "chargeCurrentLimitation", _chargeCurrentLimitation, "A", 1);
|
||||||
addLiveViewValue(root, "dischargeCurrentLimitation", _dischargeCurrentLimitation, "A", 1);
|
addLiveViewValue(root, "dischargeCurrentLimitation", _dischargeCurrentLimitation, "A", 1);
|
||||||
addLiveViewValue(root, "stateOfHealth", _stateOfHealth, "%", 0);
|
addLiveViewValue(root, "stateOfHealth", _stateOfHealth, "%", 0);
|
||||||
addLiveViewValue(root, "current", _current, "A", 1);
|
|
||||||
addLiveViewValue(root, "temperature", _temperature, "°C", 1);
|
addLiveViewValue(root, "temperature", _temperature, "°C", 1);
|
||||||
|
|
||||||
addLiveViewTextValue(root, "chargeEnabled", (_chargeEnabled?"yes":"no"));
|
addLiveViewTextValue(root, "chargeEnabled", (_chargeEnabled?"yes":"no"));
|
||||||
@ -124,7 +124,6 @@ void PytesBatteryStats::getLiveViewData(JsonVariant& root) const
|
|||||||
BatteryStats::getLiveViewData(root);
|
BatteryStats::getLiveViewData(root);
|
||||||
|
|
||||||
// values go into the "Status" card of the web application
|
// values go into the "Status" card of the web application
|
||||||
addLiveViewValue(root, "current", _current, "A", 1);
|
|
||||||
addLiveViewValue(root, "chargeVoltage", _chargeVoltageLimit, "V", 1);
|
addLiveViewValue(root, "chargeVoltage", _chargeVoltageLimit, "V", 1);
|
||||||
addLiveViewValue(root, "chargeCurrentLimitation", _chargeCurrentLimit, "A", 1);
|
addLiveViewValue(root, "chargeCurrentLimitation", _chargeCurrentLimit, "A", 1);
|
||||||
addLiveViewValue(root, "dischargeVoltageLimitation", _dischargeVoltageLimit, "V", 1);
|
addLiveViewValue(root, "dischargeVoltageLimitation", _dischargeVoltageLimit, "V", 1);
|
||||||
@ -198,11 +197,6 @@ void JkBmsBatteryStats::getJsonData(JsonVariant& root, bool verbose) const
|
|||||||
using Label = JkBms::DataPointLabel;
|
using Label = JkBms::DataPointLabel;
|
||||||
|
|
||||||
auto oCurrent = _dataPoints.get<Label::BatteryCurrentMilliAmps>();
|
auto oCurrent = _dataPoints.get<Label::BatteryCurrentMilliAmps>();
|
||||||
if (oCurrent.has_value()) {
|
|
||||||
addLiveViewValue(root, "current",
|
|
||||||
static_cast<float>(*oCurrent) / 1000, "A", 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto oVoltage = _dataPoints.get<Label::BatteryVoltageMilliVolt>();
|
auto oVoltage = _dataPoints.get<Label::BatteryVoltageMilliVolt>();
|
||||||
if (oVoltage.has_value() && oCurrent.has_value()) {
|
if (oVoltage.has_value() && oCurrent.has_value()) {
|
||||||
auto current = static_cast<float>(*oCurrent) / 1000;
|
auto current = static_cast<float>(*oCurrent) / 1000;
|
||||||
@ -304,9 +298,16 @@ void BatteryStats::mqttPublish() const
|
|||||||
{
|
{
|
||||||
MqttSettings.publish("battery/manufacturer", _manufacturer);
|
MqttSettings.publish("battery/manufacturer", _manufacturer);
|
||||||
MqttSettings.publish("battery/dataAge", String(getAgeSeconds()));
|
MqttSettings.publish("battery/dataAge", String(getAgeSeconds()));
|
||||||
|
if (isSoCValid()) {
|
||||||
MqttSettings.publish("battery/stateOfCharge", String(_soc));
|
MqttSettings.publish("battery/stateOfCharge", String(_soc));
|
||||||
|
}
|
||||||
|
if (isVoltageValid()) {
|
||||||
MqttSettings.publish("battery/voltage", String(_voltage));
|
MqttSettings.publish("battery/voltage", String(_voltage));
|
||||||
}
|
}
|
||||||
|
if (isCurrentValid()) {
|
||||||
|
MqttSettings.publish("battery/current", String(_current));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PylontechBatteryStats::mqttPublish() const
|
void PylontechBatteryStats::mqttPublish() const
|
||||||
{
|
{
|
||||||
@ -316,7 +317,6 @@ void PylontechBatteryStats::mqttPublish() const
|
|||||||
MqttSettings.publish("battery/settings/chargeCurrentLimitation", String(_chargeCurrentLimitation));
|
MqttSettings.publish("battery/settings/chargeCurrentLimitation", String(_chargeCurrentLimitation));
|
||||||
MqttSettings.publish("battery/settings/dischargeCurrentLimitation", String(_dischargeCurrentLimitation));
|
MqttSettings.publish("battery/settings/dischargeCurrentLimitation", String(_dischargeCurrentLimitation));
|
||||||
MqttSettings.publish("battery/stateOfHealth", String(_stateOfHealth));
|
MqttSettings.publish("battery/stateOfHealth", String(_stateOfHealth));
|
||||||
MqttSettings.publish("battery/current", String(_current));
|
|
||||||
MqttSettings.publish("battery/temperature", String(_temperature));
|
MqttSettings.publish("battery/temperature", String(_temperature));
|
||||||
MqttSettings.publish("battery/alarm/overCurrentDischarge", String(_alarmOverCurrentDischarge));
|
MqttSettings.publish("battery/alarm/overCurrentDischarge", String(_alarmOverCurrentDischarge));
|
||||||
MqttSettings.publish("battery/alarm/overCurrentCharge", String(_alarmOverCurrentCharge));
|
MqttSettings.publish("battery/alarm/overCurrentCharge", String(_alarmOverCurrentCharge));
|
||||||
@ -347,7 +347,6 @@ void PytesBatteryStats::mqttPublish() const
|
|||||||
MqttSettings.publish("battery/settings/dischargeVoltageLimitation", String(_dischargeVoltageLimit));
|
MqttSettings.publish("battery/settings/dischargeVoltageLimitation", String(_dischargeVoltageLimit));
|
||||||
|
|
||||||
MqttSettings.publish("battery/stateOfHealth", String(_stateOfHealth));
|
MqttSettings.publish("battery/stateOfHealth", String(_stateOfHealth));
|
||||||
MqttSettings.publish("battery/current", String(_current));
|
|
||||||
MqttSettings.publish("battery/temperature", String(_temperature));
|
MqttSettings.publish("battery/temperature", String(_temperature));
|
||||||
|
|
||||||
if (_chargedEnergy != -1) {
|
if (_chargedEnergy != -1) {
|
||||||
@ -505,6 +504,13 @@ void JkBmsBatteryStats::updateFrom(JkBms::DataPointContainer const& dp)
|
|||||||
oVoltageDataPoint->getTimestamp());
|
oVoltageDataPoint->getTimestamp());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto oCurrent = dp.get<Label::BatteryCurrentMilliAmps>();
|
||||||
|
if (oCurrent.has_value()) {
|
||||||
|
auto oCurrentDataPoint = dp.getDataPointFor<Label::BatteryCurrentMilliAmps>();
|
||||||
|
BatteryStats::setCurrent(static_cast<float>(*oCurrent) / 1000, 2/*precision*/,
|
||||||
|
oCurrentDataPoint->getTimestamp());
|
||||||
|
}
|
||||||
|
|
||||||
_dataPoints.updateFrom(dp);
|
_dataPoints.updateFrom(dp);
|
||||||
|
|
||||||
auto oCellVoltages = _dataPoints.get<Label::CellsMilliVolt>();
|
auto oCellVoltages = _dataPoints.get<Label::CellsMilliVolt>();
|
||||||
@ -545,9 +551,9 @@ void JkBmsBatteryStats::updateFrom(JkBms::DataPointContainer const& dp)
|
|||||||
void VictronSmartShuntStats::updateFrom(VeDirectShuntController::data_t const& shuntData) {
|
void VictronSmartShuntStats::updateFrom(VeDirectShuntController::data_t const& shuntData) {
|
||||||
BatteryStats::setVoltage(shuntData.batteryVoltage_V_mV / 1000.0, millis());
|
BatteryStats::setVoltage(shuntData.batteryVoltage_V_mV / 1000.0, millis());
|
||||||
BatteryStats::setSoC(static_cast<float>(shuntData.SOC) / 10, 1/*precision*/, millis());
|
BatteryStats::setSoC(static_cast<float>(shuntData.SOC) / 10, 1/*precision*/, millis());
|
||||||
|
BatteryStats::setCurrent(static_cast<float>(shuntData.batteryCurrent_I_mA) / 1000, 2/*precision*/, millis());
|
||||||
_fwversion = shuntData.getFwVersionFormatted();
|
_fwversion = shuntData.getFwVersionFormatted();
|
||||||
|
|
||||||
_current = static_cast<float>(shuntData.batteryCurrent_I_mA) / 1000;
|
|
||||||
_chargeCycles = shuntData.H4;
|
_chargeCycles = shuntData.H4;
|
||||||
_timeToGo = shuntData.TTG / 60;
|
_timeToGo = shuntData.TTG / 60;
|
||||||
_chargedEnergy = static_cast<float>(shuntData.H18) / 100;
|
_chargedEnergy = static_cast<float>(shuntData.H18) / 100;
|
||||||
@ -574,7 +580,6 @@ void VictronSmartShuntStats::getLiveViewData(JsonVariant& root) const {
|
|||||||
BatteryStats::getLiveViewData(root);
|
BatteryStats::getLiveViewData(root);
|
||||||
|
|
||||||
// values go into the "Status" card of the web application
|
// values go into the "Status" card of the web application
|
||||||
addLiveViewValue(root, "current", _current, "A", 1);
|
|
||||||
addLiveViewValue(root, "chargeCycles", _chargeCycles, "", 0);
|
addLiveViewValue(root, "chargeCycles", _chargeCycles, "", 0);
|
||||||
addLiveViewValue(root, "chargedEnergy", _chargedEnergy, "kWh", 2);
|
addLiveViewValue(root, "chargedEnergy", _chargedEnergy, "kWh", 2);
|
||||||
addLiveViewValue(root, "dischargedEnergy", _dischargedEnergy, "kWh", 2);
|
addLiveViewValue(root, "dischargedEnergy", _dischargedEnergy, "kWh", 2);
|
||||||
@ -597,7 +602,6 @@ void VictronSmartShuntStats::getLiveViewData(JsonVariant& root) const {
|
|||||||
void VictronSmartShuntStats::mqttPublish() const {
|
void VictronSmartShuntStats::mqttPublish() const {
|
||||||
BatteryStats::mqttPublish();
|
BatteryStats::mqttPublish();
|
||||||
|
|
||||||
MqttSettings.publish("battery/current", String(_current));
|
|
||||||
MqttSettings.publish("battery/chargeCycles", String(_chargeCycles));
|
MqttSettings.publish("battery/chargeCycles", String(_chargeCycles));
|
||||||
MqttSettings.publish("battery/chargedEnergy", String(_chargedEnergy));
|
MqttSettings.publish("battery/chargedEnergy", String(_chargedEnergy));
|
||||||
MqttSettings.publish("battery/dischargedEnergy", String(_dischargedEnergy));
|
MqttSettings.publish("battery/dischargedEnergy", String(_dischargedEnergy));
|
||||||
|
|||||||
@ -39,12 +39,12 @@ void PylontechCanReceiver::onMessage(twai_message_t rx_message)
|
|||||||
|
|
||||||
case 0x356: {
|
case 0x356: {
|
||||||
_stats->setVoltage(this->scaleValue(this->readSignedInt16(rx_message.data), 0.01), millis());
|
_stats->setVoltage(this->scaleValue(this->readSignedInt16(rx_message.data), 0.01), millis());
|
||||||
_stats->_current = this->scaleValue(this->readSignedInt16(rx_message.data + 2), 0.1);
|
_stats->setCurrent(this->scaleValue(this->readSignedInt16(rx_message.data + 2), 0.1), 1/*precision*/, millis());
|
||||||
_stats->_temperature = this->scaleValue(this->readSignedInt16(rx_message.data + 4), 0.1);
|
_stats->_temperature = this->scaleValue(this->readSignedInt16(rx_message.data + 4), 0.1);
|
||||||
|
|
||||||
if (_verboseLogging) {
|
if (_verboseLogging) {
|
||||||
MessageOutput.printf("[Pylontech] voltage: %f current: %f temperature: %f\r\n",
|
MessageOutput.printf("[Pylontech] voltage: %f current: %f temperature: %f\r\n",
|
||||||
_stats->getVoltage(), _stats->_current, _stats->_temperature);
|
_stats->getVoltage(), _stats->getChargeCurrent(), _stats->_temperature);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -157,7 +157,7 @@ void PylontechCanReceiver::dummyData()
|
|||||||
_stats->_dischargeCurrentLimitation = dummyFloat(12);
|
_stats->_dischargeCurrentLimitation = dummyFloat(12);
|
||||||
_stats->_stateOfHealth = 99;
|
_stats->_stateOfHealth = 99;
|
||||||
_stats->setVoltage(48.67, millis());
|
_stats->setVoltage(48.67, millis());
|
||||||
_stats->_current = dummyFloat(-1);
|
_stats->setCurrent(dummyFloat(-1), 1/*precision*/, millis());
|
||||||
_stats->_temperature = dummyFloat(20);
|
_stats->_temperature = dummyFloat(20);
|
||||||
|
|
||||||
_stats->_chargeEnabled = true;
|
_stats->_chargeEnabled = true;
|
||||||
|
|||||||
@ -40,12 +40,12 @@ void PytesCanReceiver::onMessage(twai_message_t rx_message)
|
|||||||
|
|
||||||
case 0x356: {
|
case 0x356: {
|
||||||
_stats->setVoltage(this->scaleValue(this->readSignedInt16(rx_message.data), 0.01), millis());
|
_stats->setVoltage(this->scaleValue(this->readSignedInt16(rx_message.data), 0.01), millis());
|
||||||
_stats->_current = this->scaleValue(this->readSignedInt16(rx_message.data + 2), 0.1);
|
_stats->setCurrent(this->scaleValue(this->readSignedInt16(rx_message.data + 2), 0.1), 1/*precision*/, millis());
|
||||||
_stats->_temperature = this->scaleValue(this->readSignedInt16(rx_message.data + 4), 0.1);
|
_stats->_temperature = this->scaleValue(this->readSignedInt16(rx_message.data + 4), 0.1);
|
||||||
|
|
||||||
if (_verboseLogging) {
|
if (_verboseLogging) {
|
||||||
MessageOutput.printf("[Pytes] voltage: %f current: %f temperature: %f\r\n",
|
MessageOutput.printf("[Pytes] voltage: %f current: %f temperature: %f\r\n",
|
||||||
_stats->getVoltage(), _stats->_current, _stats->_temperature);
|
_stats->getVoltage(), _stats->getChargeCurrent(), _stats->_temperature);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -92,7 +92,21 @@ void WebApiWsLiveClass::generateOnBatteryJsonResponse(JsonVariant& root, bool al
|
|||||||
batteryObj["enabled"] = config.Battery.Enabled;
|
batteryObj["enabled"] = config.Battery.Enabled;
|
||||||
|
|
||||||
if (config.Battery.Enabled) {
|
if (config.Battery.Enabled) {
|
||||||
addTotalField(batteryObj, "soc", spStats->getSoC(), "%", 0);
|
if (spStats->isSoCValid()) {
|
||||||
|
addTotalField(batteryObj, "soc", spStats->getSoC(), "%", spStats->getSoCPrecision());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spStats->isVoltageValid()) {
|
||||||
|
addTotalField(batteryObj, "voltage", spStats->getVoltage(), "V", 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spStats->isCurrentValid()) {
|
||||||
|
addTotalField(batteryObj, "current", spStats->getChargeCurrent(), "A", spStats->getChargeCurrentPrecision());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spStats->isVoltageValid() && spStats->isCurrentValid()) {
|
||||||
|
addTotalField(batteryObj, "power", spStats->getVoltage() * spStats->getChargeCurrent(), "W", 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!all) { _lastPublishBattery = millis(); }
|
if (!all) { _lastPublishBattery = millis(); }
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="['card', addSpace ? 'mt-5' : '' ]">
|
<div :class="['card', addSpace ? 'mt-5' : '' ]">
|
||||||
<div :class="['card-header', textVariant]">{{ text }}</div>
|
<div :class="['card-header', textVariant]">{{ text }}</div>
|
||||||
<div :class="['card-body', 'card-text', centerContent ? 'text-center' : '']">
|
<div :class="['card-body', 'card-text', centerContent ? 'text-center' : '', flexChildren ? 'd-flex' : '']">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -16,6 +16,7 @@ export default defineComponent({
|
|||||||
'textVariant': String,
|
'textVariant': String,
|
||||||
'addSpace': Boolean,
|
'addSpace': Boolean,
|
||||||
'centerContent': Boolean,
|
'centerContent': Boolean,
|
||||||
|
'flexChildren': Boolean,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -66,8 +66,10 @@
|
|||||||
</h2>
|
</h2>
|
||||||
</CardElement>
|
</CardElement>
|
||||||
</div>
|
</div>
|
||||||
<div class="col" v-if="totalBattData.enabled">
|
<template v-if="totalBattData.enabled">
|
||||||
<CardElement centerContent textVariant="text-bg-success" :text="$t('invertertotalinfo.BatterySoc')">
|
<div class="col">
|
||||||
|
<CardElement centerContent flexChildren textVariant="text-bg-success" :text="$t('invertertotalinfo.BatteryCharge')">
|
||||||
|
<div class="flex-fill" v-if="totalBattData.soc">
|
||||||
<h2>
|
<h2>
|
||||||
{{ $n(totalBattData.soc.v, 'decimal', {
|
{{ $n(totalBattData.soc.v, 'decimal', {
|
||||||
minimumFractionDigits: totalBattData.soc.d,
|
minimumFractionDigits: totalBattData.soc.d,
|
||||||
@ -75,8 +77,43 @@
|
|||||||
}) }}
|
}) }}
|
||||||
<small class="text-muted">{{ totalBattData.soc.u }}</small>
|
<small class="text-muted">{{ totalBattData.soc.u }}</small>
|
||||||
</h2>
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-fill" v-if="totalBattData.voltage">
|
||||||
|
<h2>
|
||||||
|
{{ $n(totalBattData.voltage.v, 'decimal', {
|
||||||
|
minimumFractionDigits: totalBattData.voltage.d,
|
||||||
|
maximumFractionDigits: totalBattData.voltage.d
|
||||||
|
}) }}
|
||||||
|
<small class="text-muted">{{ totalBattData.voltage.u }}</small>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
</CardElement>
|
</CardElement>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col" v-if="totalBattData.power || totalBattData.current">
|
||||||
|
<CardElement centerContent flexChildren textVariant="text-bg-success" :text="$t('invertertotalinfo.BatteryPower')">
|
||||||
|
<div class="flex-fill" v-if="totalBattData.power">
|
||||||
|
<h2>
|
||||||
|
{{ $n(totalBattData.power.v, 'decimal', {
|
||||||
|
minimumFractionDigits: totalBattData.power.d,
|
||||||
|
maximumFractionDigits: totalBattData.power.d
|
||||||
|
}) }}
|
||||||
|
<small class="text-muted">{{ totalBattData.power.u }}</small>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-fill" v-if="totalBattData.current">
|
||||||
|
<h2>
|
||||||
|
{{ $n(totalBattData.current.v, 'decimal', {
|
||||||
|
minimumFractionDigits: totalBattData.current.d,
|
||||||
|
maximumFractionDigits: totalBattData.current.d
|
||||||
|
}) }}
|
||||||
|
<small class="text-muted">{{ totalBattData.current.u }}</small>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</CardElement>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
<div class="col" v-if="powerMeterData.enabled">
|
<div class="col" v-if="powerMeterData.enabled">
|
||||||
<CardElement centerContent textVariant="text-bg-success" :text="$t('invertertotalinfo.HomePower')">
|
<CardElement centerContent textVariant="text-bg-success" :text="$t('invertertotalinfo.HomePower')">
|
||||||
<h2>
|
<h2>
|
||||||
|
|||||||
@ -395,7 +395,8 @@
|
|||||||
"MpptTotalYieldTotal": "MPPT Gesamtertrag Insgesamt",
|
"MpptTotalYieldTotal": "MPPT Gesamtertrag Insgesamt",
|
||||||
"MpptTotalYieldDay": "MPPT Gesamtertrag Heute",
|
"MpptTotalYieldDay": "MPPT Gesamtertrag Heute",
|
||||||
"MpptTotalPower": "MPPT Gesamtleistung",
|
"MpptTotalPower": "MPPT Gesamtleistung",
|
||||||
"BatterySoc": "Ladezustand",
|
"BatteryCharge": "Batterie Ladezustand",
|
||||||
|
"BatteryPower": "Batterie Leistung",
|
||||||
"HomePower": "Leistung / Netz",
|
"HomePower": "Leistung / Netz",
|
||||||
"HuaweiPower": "Huawei AC Leistung"
|
"HuaweiPower": "Huawei AC Leistung"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -397,7 +397,8 @@
|
|||||||
"MpptTotalYieldTotal": "MPPT Total Yield Total",
|
"MpptTotalYieldTotal": "MPPT Total Yield Total",
|
||||||
"MpptTotalYieldDay": "MPPT Total Yield Day",
|
"MpptTotalYieldDay": "MPPT Total Yield Day",
|
||||||
"MpptTotalPower": "MPPT Total Power",
|
"MpptTotalPower": "MPPT Total Power",
|
||||||
"BatterySoc": "State of charge",
|
"BatteryCharge": "Battery Charge",
|
||||||
|
"BatteryPower": "Battery Power",
|
||||||
"HomePower": "Grid Power",
|
"HomePower": "Grid Power",
|
||||||
"HuaweiPower": "Huawei AC Power"
|
"HuaweiPower": "Huawei AC Power"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -430,7 +430,8 @@
|
|||||||
"MpptTotalYieldTotal": "MPPT rendement total",
|
"MpptTotalYieldTotal": "MPPT rendement total",
|
||||||
"MpptTotalYieldDay": "MPPT rendement du jour",
|
"MpptTotalYieldDay": "MPPT rendement du jour",
|
||||||
"MpptTotalPower": "MPPT puissance de l'installation",
|
"MpptTotalPower": "MPPT puissance de l'installation",
|
||||||
"BatterySoc": "State of charge",
|
"BatteryCharge": "Battery Charge",
|
||||||
|
"BatteryPower": "Battery Power",
|
||||||
"HomePower": "Grid Power",
|
"HomePower": "Grid Power",
|
||||||
"HuaweiPower": "Huawei AC Power"
|
"HuaweiPower": "Huawei AC Power"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -61,7 +61,10 @@ export interface Huawei {
|
|||||||
|
|
||||||
export interface Battery {
|
export interface Battery {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
soc: ValueObject;
|
soc?: ValueObject;
|
||||||
|
voltage?: ValueObject;
|
||||||
|
power?: ValueObject;
|
||||||
|
current?: ValueObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PowerMeter {
|
export interface PowerMeter {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user