Feature: JK BMS: add more values to live view (#552)

there are more interesting values available to display in the live view.
however, adding them made the list of values very long. this can be
mitigated by using a new column/card, which uses the available screen
space nicely on bigger screens.
This commit is contained in:
Bernhard Kirchen 2023-12-30 16:46:44 +01:00 committed by GitHub
parent bb34fa74fd
commit 7928f2f8cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 81 additions and 63 deletions

View File

@ -28,16 +28,6 @@ class BatteryStats {
bool isValid() const { return _lastUpdateSoC > 0 && _lastUpdate > 0; }
protected:
template<typename T>
void addLiveViewValue(JsonVariant& root, std::string const& name,
T&& value, std::string const& unit, uint8_t precision) const;
void addLiveViewText(JsonVariant& root, std::string const& name,
std::string const& text) const;
void addLiveViewWarning(JsonVariant& root, std::string const& name,
bool warning) const;
void addLiveViewAlarm(JsonVariant& root, std::string const& name,
bool alarm) const;
String _manufacturer = "unknown";
uint8_t _SoC = 0;
uint32_t _lastUpdateSoC = 0;

View File

@ -7,30 +7,44 @@
#include "JkBmsDataPoints.h"
template<typename T>
void BatteryStats::addLiveViewValue(JsonVariant& root, std::string const& name,
T&& value, std::string const& unit, uint8_t precision) const
static void addLiveViewInSection(JsonVariant& root,
std::string const& section, std::string const& name,
T&& value, std::string const& unit, uint8_t precision)
{
auto jsonValue = root["values"][name];
auto jsonValue = root["values"][section][name];
jsonValue["v"] = value;
jsonValue["u"] = unit;
jsonValue["d"] = precision;
}
void BatteryStats::addLiveViewText(JsonVariant& root, std::string const& name,
std::string const& text) const
template<typename T>
static void addLiveViewValue(JsonVariant& root, std::string const& name,
T&& value, std::string const& unit, uint8_t precision)
{
root["values"][name] = text;
addLiveViewInSection(root, "status", name, value, unit, precision);
}
void BatteryStats::addLiveViewWarning(JsonVariant& root, std::string const& name,
bool warning) const
static void addLiveViewTextInSection(JsonVariant& root,
std::string const& section, std::string const& name, std::string const& text)
{
root["values"][section][name] = text;
}
static void addLiveViewTextValue(JsonVariant& root, std::string const& name,
std::string const& text)
{
addLiveViewTextInSection(root, "status", name, text);
}
static void addLiveViewWarning(JsonVariant& root, std::string const& name,
bool warning)
{
if (!warning) { return; }
root["issues"][name] = 1;
}
void BatteryStats::addLiveViewAlarm(JsonVariant& root, std::string const& name,
bool alarm) const
static void addLiveViewAlarm(JsonVariant& root, std::string const& name,
bool alarm)
{
if (!alarm) { return; }
root["issues"][name] = 2;
@ -57,9 +71,9 @@ void PylontechBatteryStats::getLiveViewData(JsonVariant& root) const
addLiveViewValue(root, "current", _current, "A", 1);
addLiveViewValue(root, "temperature", _temperature, "°C", 1);
addLiveViewText(root, "chargeEnabled", (_chargeEnabled?"yes":"no"));
addLiveViewText(root, "dischargeEnabled", (_dischargeEnabled?"yes":"no"));
addLiveViewText(root, "chargeImmediately", (_chargeImmediately?"yes":"no"));
addLiveViewTextValue(root, "chargeEnabled", (_chargeEnabled?"yes":"no"));
addLiveViewTextValue(root, "dischargeEnabled", (_dischargeEnabled?"yes":"no"));
addLiveViewTextValue(root, "chargeImmediately", (_chargeImmediately?"yes":"no"));
// alarms and warnings go into the "Issues" card of the web application
addLiveViewWarning(root, "highCurrentDischarge", _warningHighCurrentDischarge);
@ -108,38 +122,11 @@ void JkBmsBatteryStats::getJsonData(JsonVariant& root, bool verbose) const
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
@ -148,11 +135,32 @@ void JkBmsBatteryStats::getJsonData(JsonVariant& root, bool verbose) const
if (oStatus.has_value()) {
using Bits = JkBms::StatusBits;
auto chargeEnabled = *oStatus & static_cast<uint16_t>(Bits::ChargingActive);
addLiveViewText(root, "chargeEnabled", (chargeEnabled?"yes":"no"));
addLiveViewTextValue(root, "chargeEnabled", (chargeEnabled?"yes":"no"));
auto dischargeEnabled = *oStatus & static_cast<uint16_t>(Bits::DischargingActive);
addLiveViewText(root, "dischargeEnabled", (dischargeEnabled?"yes":"no"));
addLiveViewTextValue(root, "dischargeEnabled", (dischargeEnabled?"yes":"no"));
}
auto oTemperatureOne = _dataPoints.get<Label::BatteryTempOneCelsius>();
if (oTemperatureOne.has_value()) {
addLiveViewInSection(root, "cells", "batOneTemp", *oTemperatureOne, "°C", 0);
}
auto oTemperatureTwo = _dataPoints.get<Label::BatteryTempTwoCelsius>();
if (oTemperatureTwo.has_value()) {
addLiveViewInSection(root, "cells", "batTwoTemp", *oTemperatureTwo, "°C", 0);
}
if (_cellVoltageTimestamp > 0) {
addLiveViewInSection(root, "cells", "cellMinVoltage", static_cast<float>(_cellMinMilliVolt)/1000, "V", 3);
addLiveViewInSection(root, "cells", "cellAvgVoltage", static_cast<float>(_cellAvgMilliVolt)/1000, "V", 3);
addLiveViewInSection(root, "cells", "cellMaxVoltage", static_cast<float>(_cellMaxMilliVolt)/1000, "V", 3);
addLiveViewInSection(root, "cells", "cellDiffVoltage", (_cellMaxMilliVolt - _cellMinMilliVolt), "mV", 0);
}
if (oStatus.has_value()) {
using Bits = JkBms::StatusBits;
auto balancingActive = *oStatus & static_cast<uint16_t>(Bits::BalancingActive);
addLiveViewText(root, "balancingActive", (balancingActive?"yes":"no"));
addLiveViewTextInSection(root, "cells", "balancingActive", (balancingActive?"yes":"no"));
}
auto oAlarms = _dataPoints.get<Label::AlarmsBitmask>();

View File

@ -27,9 +27,9 @@
<div class="card-body">
<div class="row flex-row flex-wrap align-items-start g-3">
<div class="col order-0">
<div v-for="(values, section) in batteryData.values" v-bind:key="section" class="col order-0">
<div class="card" :class="{ 'border-info': true }">
<div class="card-header text-bg-info">{{ $t('battery.Status') }}</div>
<div class="card-header text-bg-info">{{ $t('battery.' + section) }}</div>
<div class="card-body">
<table class="table table-striped table-hover">
<thead>
@ -40,7 +40,7 @@
</tr>
</thead>
<tbody>
<tr v-for="(prop, key) in batteryData.values" v-bind:key="key">
<tr v-for="(prop, key) in values" v-bind:key="key">
<th scope="row">{{ $t('battery.' + key) }}</th>
<td style="text-align: right">
<template v-if="typeof prop === 'string'">

View File

@ -803,7 +803,7 @@
"battery": "Batterie",
"DataAge": "letzte Aktualisierung: ",
"Seconds": "vor {val} Sekunden",
"Status": "Status",
"status": "Status",
"Property": "Eigenschaft",
"yes": "@:base.Yes",
"no": "@:base.No",
@ -821,9 +821,15 @@
"dischargeCurrentLimitation": "Entladestromlimit",
"chargeEnabled": "Laden ermöglicht",
"dischargeEnabled": "Entladen ermöglicht",
"balancingActive": "Ausgleichen aktiv",
"chargeImmediately": "Sofortiges Laden angefordert",
"cells": "Zellen",
"batOneTemp": "Batterietemperatur 1",
"batTwoTemp": "Batterietemperatur 2",
"cellMinVoltage": "Kleinste Zellspannung",
"cellAvgVoltage": "Durchschnittliche Zellspannung",
"cellMaxVoltage": "Höchste Zellspannung",
"cellDiffVoltage": "Zellspannungsdifferenz",
"balancingActive": "Ausgleichen aktiv",
"issues": "Meldungen",
"noIssues": "Keine Meldungen",
"issueName": "Bezeichnung",

View File

@ -813,7 +813,7 @@
"battery": "Battery",
"DataAge": "Data Age: ",
"Seconds": " {val} seconds",
"Status": "Status",
"status": "Status",
"Property": "Property",
"yes": "@:base.Yes",
"no": "@:base.No",
@ -831,9 +831,15 @@
"dischargeCurrentLimitation": "Discharge current limit",
"chargeEnabled": "Charging possible",
"dischargeEnabled": "Discharging possible",
"balancingActive": "Balancing active",
"chargeImmediately": "Immediate charging requested",
"cells": "Cells",
"batOneTemp": "Battery temperature 1",
"batTwoTemp": "Battery temperature 2",
"cellMinVoltage": "Minimum cell voltage",
"cellAvgVoltage": "Average cell voltage",
"cellMaxVoltage": "Maximum cell voltage",
"cellDiffVoltage": "Cell voltage difference",
"balancingActive": "Balancing active",
"issues": "Issues",
"noIssues": "No Issues",
"issueName": "Name",

View File

@ -772,7 +772,7 @@
"battery": "Battery",
"DataAge": "Data Age: ",
"Seconds": " {val} seconds",
"Status": "Status",
"status": "Status",
"Property": "Property",
"yes": "@:base.Yes",
"no": "@:base.No",
@ -790,9 +790,15 @@
"dischargeCurrentLimitation": "Discharge current limit",
"chargeEnabled": "Charging possible",
"dischargeEnabled": "Discharging possible",
"balancingActive": "Balancing active",
"chargeImmediately": "Immediate charging requested",
"cells": "Cells",
"batOneTemp": "Battery temperature 1",
"batTwoTemp": "Battery temperature 2",
"cellMinVoltage": "Minimum cell voltage",
"cellAvgVoltage": "Average cell voltage",
"cellMaxVoltage": "Maximum cell voltage",
"cellDiffVoltage": "Cell voltage difference",
"balancingActive": "Balancing active",
"issues": "Issues",
"noIssues": "No Issues",
"issueName": "Name",

View File

@ -1,8 +1,10 @@
import type { ValueObject } from '@/types/LiveDataStatus';
type BatteryData = (ValueObject | string)[];
export interface Battery {
manufacturer: string;
data_age: number;
values: (ValueObject | string)[];
values: BatteryData[];
issues: number[];
}